winit/platform/
unix.rs

1#![cfg(any(
2    target_os = "linux",
3    target_os = "dragonfly",
4    target_os = "freebsd",
5    target_os = "netbsd",
6    target_os = "openbsd"
7))]
8
9use std::os::raw;
10#[cfg(feature = "x11")]
11use std::{ptr, sync::Arc};
12
13use crate::{
14    event_loop::{EventLoop, EventLoopWindowTarget},
15    monitor::MonitorHandle,
16    window::{Window, WindowBuilder},
17};
18
19#[cfg(feature = "x11")]
20use crate::dpi::Size;
21#[cfg(feature = "x11")]
22use crate::platform_impl::x11::{ffi::XVisualInfo, XConnection};
23use crate::platform_impl::{
24    EventLoop as LinuxEventLoop, EventLoopWindowTarget as LinuxEventLoopWindowTarget,
25    Window as LinuxWindow,
26};
27
28// TODO: stupid hack so that glutin can do its work
29#[doc(hidden)]
30#[cfg(feature = "x11")]
31pub use crate::platform_impl::x11;
32#[cfg(feature = "x11")]
33pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported};
34
35/// Additional methods on `EventLoopWindowTarget` that are specific to Unix.
36pub trait EventLoopWindowTargetExtUnix {
37    /// True if the `EventLoopWindowTarget` uses Wayland.
38    #[cfg(feature = "wayland")]
39    fn is_wayland(&self) -> bool;
40
41    /// True if the `EventLoopWindowTarget` uses X11.
42    #[cfg(feature = "x11")]
43    fn is_x11(&self) -> bool;
44
45    #[doc(hidden)]
46    #[cfg(feature = "x11")]
47    fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;
48
49    /// Returns a pointer to the `wl_display` object of wayland that is used by this
50    /// `EventLoopWindowTarget`.
51    ///
52    /// Returns `None` if the `EventLoop` doesn't use wayland (if it uses xlib for example).
53    ///
54    /// The pointer will become invalid when the winit `EventLoop` is destroyed.
55    #[cfg(feature = "wayland")]
56    fn wayland_display(&self) -> Option<*mut raw::c_void>;
57}
58
59impl<T> EventLoopWindowTargetExtUnix for EventLoopWindowTarget<T> {
60    #[inline]
61    #[cfg(feature = "wayland")]
62    fn is_wayland(&self) -> bool {
63        self.p.is_wayland()
64    }
65
66    #[inline]
67    #[cfg(feature = "x11")]
68    fn is_x11(&self) -> bool {
69        !self.p.is_wayland()
70    }
71
72    #[inline]
73    #[doc(hidden)]
74    #[cfg(feature = "x11")]
75    fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
76        match self.p {
77            LinuxEventLoopWindowTarget::X(ref e) => Some(e.x_connection().clone()),
78            #[cfg(feature = "wayland")]
79            _ => None,
80        }
81    }
82
83    #[inline]
84    #[cfg(feature = "wayland")]
85    fn wayland_display(&self) -> Option<*mut raw::c_void> {
86        match self.p {
87            LinuxEventLoopWindowTarget::Wayland(ref p) => {
88                Some(p.display().get_display_ptr() as *mut _)
89            }
90            #[cfg(feature = "x11")]
91            _ => None,
92        }
93    }
94}
95
96/// Additional methods on `EventLoop` that are specific to Unix.
97pub trait EventLoopExtUnix {
98    /// Builds a new `EventLoop` that is forced to use X11.
99    ///
100    /// # Panics
101    ///
102    /// If called outside the main thread. To initialize an X11 event loop outside
103    /// the main thread, use [`new_x11_any_thread`](#tymethod.new_x11_any_thread).
104    #[cfg(feature = "x11")]
105    fn new_x11() -> Result<Self, XNotSupported>
106    where
107        Self: Sized;
108
109    /// Builds a new `EventLoop` that is forced to use Wayland.
110    ///
111    /// # Panics
112    ///
113    /// If called outside the main thread. To initialize a Wayland event loop outside
114    /// the main thread, use [`new_wayland_any_thread`](#tymethod.new_wayland_any_thread).
115    #[cfg(feature = "wayland")]
116    fn new_wayland() -> Self
117    where
118        Self: Sized;
119
120    /// Builds a new `EventLoop` on any thread.
121    ///
122    /// This method bypasses the cross-platform compatibility requirement
123    /// that `EventLoop` be created on the main thread.
124    fn new_any_thread() -> Self
125    where
126        Self: Sized;
127
128    /// Builds a new X11 `EventLoop` on any thread.
129    ///
130    /// This method bypasses the cross-platform compatibility requirement
131    /// that `EventLoop` be created on the main thread.
132    #[cfg(feature = "x11")]
133    fn new_x11_any_thread() -> Result<Self, XNotSupported>
134    where
135        Self: Sized;
136
137    /// Builds a new Wayland `EventLoop` on any thread.
138    ///
139    /// This method bypasses the cross-platform compatibility requirement
140    /// that `EventLoop` be created on the main thread.
141    #[cfg(feature = "wayland")]
142    fn new_wayland_any_thread() -> Self
143    where
144        Self: Sized;
145}
146
147fn wrap_ev<T>(event_loop: LinuxEventLoop<T>) -> EventLoop<T> {
148    EventLoop {
149        event_loop,
150        _marker: std::marker::PhantomData,
151    }
152}
153
154impl<T> EventLoopExtUnix for EventLoop<T> {
155    #[inline]
156    fn new_any_thread() -> Self {
157        wrap_ev(LinuxEventLoop::new_any_thread())
158    }
159
160    #[inline]
161    #[cfg(feature = "x11")]
162    fn new_x11_any_thread() -> Result<Self, XNotSupported> {
163        LinuxEventLoop::new_x11_any_thread().map(wrap_ev)
164    }
165
166    #[inline]
167    #[cfg(feature = "wayland")]
168    fn new_wayland_any_thread() -> Self {
169        wrap_ev(
170            LinuxEventLoop::new_wayland_any_thread()
171                // TODO: propagate
172                .expect("failed to open Wayland connection"),
173        )
174    }
175
176    #[inline]
177    #[cfg(feature = "x11")]
178    fn new_x11() -> Result<Self, XNotSupported> {
179        LinuxEventLoop::new_x11().map(wrap_ev)
180    }
181
182    #[inline]
183    #[cfg(feature = "wayland")]
184    fn new_wayland() -> Self {
185        wrap_ev(
186            LinuxEventLoop::new_wayland()
187                // TODO: propagate
188                .expect("failed to open Wayland connection"),
189        )
190    }
191}
192
193/// Additional methods on `Window` that are specific to Unix.
194pub trait WindowExtUnix {
195    /// Returns the ID of the `Window` xlib object that is used by this window.
196    ///
197    /// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
198    #[cfg(feature = "x11")]
199    fn xlib_window(&self) -> Option<raw::c_ulong>;
200
201    /// Returns a pointer to the `Display` object of xlib that is used by this window.
202    ///
203    /// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
204    ///
205    /// The pointer will become invalid when the glutin `Window` is destroyed.
206    #[cfg(feature = "x11")]
207    fn xlib_display(&self) -> Option<*mut raw::c_void>;
208
209    #[cfg(feature = "x11")]
210    fn xlib_screen_id(&self) -> Option<raw::c_int>;
211
212    #[doc(hidden)]
213    #[cfg(feature = "x11")]
214    fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;
215
216    /// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
217    ///
218    /// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
219    ///
220    /// The pointer will become invalid when the glutin `Window` is destroyed.
221    #[cfg(feature = "x11")]
222    fn xcb_connection(&self) -> Option<*mut raw::c_void>;
223
224    /// Returns a pointer to the `wl_surface` object of wayland that is used by this window.
225    ///
226    /// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
227    ///
228    /// The pointer will become invalid when the glutin `Window` is destroyed.
229    #[cfg(feature = "wayland")]
230    fn wayland_surface(&self) -> Option<*mut raw::c_void>;
231
232    /// Returns a pointer to the `wl_display` object of wayland that is used by this window.
233    ///
234    /// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
235    ///
236    /// The pointer will become invalid when the glutin `Window` is destroyed.
237    #[cfg(feature = "wayland")]
238    fn wayland_display(&self) -> Option<*mut raw::c_void>;
239
240    /// Sets the color theme of the client side window decorations on wayland
241    #[cfg(feature = "wayland")]
242    fn set_wayland_theme<T: Theme>(&self, theme: T);
243
244    /// Check if the window is ready for drawing
245    ///
246    /// It is a remnant of a previous implementation detail for the
247    /// wayland backend, and is no longer relevant.
248    ///
249    /// Always return true.
250    #[deprecated]
251    fn is_ready(&self) -> bool;
252}
253
254impl WindowExtUnix for Window {
255    #[inline]
256    #[cfg(feature = "x11")]
257    fn xlib_window(&self) -> Option<raw::c_ulong> {
258        match self.window {
259            LinuxWindow::X(ref w) => Some(w.xlib_window()),
260            #[cfg(feature = "wayland")]
261            _ => None,
262        }
263    }
264
265    #[inline]
266    #[cfg(feature = "x11")]
267    fn xlib_display(&self) -> Option<*mut raw::c_void> {
268        match self.window {
269            LinuxWindow::X(ref w) => Some(w.xlib_display()),
270            #[cfg(feature = "wayland")]
271            _ => None,
272        }
273    }
274
275    #[inline]
276    #[cfg(feature = "x11")]
277    fn xlib_screen_id(&self) -> Option<raw::c_int> {
278        match self.window {
279            LinuxWindow::X(ref w) => Some(w.xlib_screen_id()),
280            #[cfg(feature = "wayland")]
281            _ => None,
282        }
283    }
284
285    #[inline]
286    #[doc(hidden)]
287    #[cfg(feature = "x11")]
288    fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
289        match self.window {
290            LinuxWindow::X(ref w) => Some(w.xlib_xconnection()),
291            #[cfg(feature = "wayland")]
292            _ => None,
293        }
294    }
295
296    #[inline]
297    #[cfg(feature = "x11")]
298    fn xcb_connection(&self) -> Option<*mut raw::c_void> {
299        match self.window {
300            LinuxWindow::X(ref w) => Some(w.xcb_connection()),
301            #[cfg(feature = "wayland")]
302            _ => None,
303        }
304    }
305
306    #[inline]
307    #[cfg(feature = "wayland")]
308    fn wayland_surface(&self) -> Option<*mut raw::c_void> {
309        match self.window {
310            LinuxWindow::Wayland(ref w) => Some(w.surface().as_ref().c_ptr() as *mut _),
311            #[cfg(feature = "x11")]
312            _ => None,
313        }
314    }
315
316    #[inline]
317    #[cfg(feature = "wayland")]
318    fn wayland_display(&self) -> Option<*mut raw::c_void> {
319        match self.window {
320            LinuxWindow::Wayland(ref w) => Some(w.display().get_display_ptr() as *mut _),
321            #[cfg(feature = "x11")]
322            _ => None,
323        }
324    }
325
326    #[inline]
327    #[cfg(feature = "wayland")]
328    fn set_wayland_theme<T: Theme>(&self, theme: T) {
329        match self.window {
330            LinuxWindow::Wayland(ref w) => w.set_theme(theme),
331            #[cfg(feature = "x11")]
332            _ => {}
333        }
334    }
335
336    #[inline]
337    fn is_ready(&self) -> bool {
338        true
339    }
340}
341
342/// Additional methods on `WindowBuilder` that are specific to Unix.
343pub trait WindowBuilderExtUnix {
344    #[cfg(feature = "x11")]
345    fn with_x11_visual<T>(self, visual_infos: *const T) -> Self;
346    #[cfg(feature = "x11")]
347    fn with_x11_screen(self, screen_id: i32) -> Self;
348
349    /// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11.
350    #[cfg(feature = "x11")]
351    fn with_class(self, class: String, instance: String) -> Self;
352    /// Build window with override-redirect flag; defaults to false. Only relevant on X11.
353    #[cfg(feature = "x11")]
354    fn with_override_redirect(self, override_redirect: bool) -> Self;
355    /// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. Only relevant on X11.
356    #[cfg(feature = "x11")]
357    fn with_x11_window_type(self, x11_window_type: Vec<XWindowType>) -> Self;
358    /// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11.
359    #[cfg(feature = "x11")]
360    fn with_gtk_theme_variant(self, variant: String) -> Self;
361    /// Build window with resize increment hint. Only implemented on X11.
362    #[cfg(feature = "x11")]
363    fn with_resize_increments<S: Into<Size>>(self, increments: S) -> Self;
364    /// Build window with base size hint. Only implemented on X11.
365    #[cfg(feature = "x11")]
366    fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
367
368    /// Build window with a given application ID. It should match the `.desktop` file distributed with
369    /// your program. Only relevant on Wayland.
370    ///
371    /// For details about application ID conventions, see the
372    /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
373    #[cfg(feature = "wayland")]
374    fn with_app_id(self, app_id: String) -> Self;
375}
376
377impl WindowBuilderExtUnix for WindowBuilder {
378    #[inline]
379    #[cfg(feature = "x11")]
380    fn with_x11_visual<T>(mut self, visual_infos: *const T) -> Self {
381        {
382            self.platform_specific.visual_infos =
383                Some(unsafe { ptr::read(visual_infos as *const XVisualInfo) });
384        }
385        self
386    }
387
388    #[inline]
389    #[cfg(feature = "x11")]
390    fn with_x11_screen(mut self, screen_id: i32) -> Self {
391        self.platform_specific.screen_id = Some(screen_id);
392        self
393    }
394
395    #[inline]
396    #[cfg(feature = "x11")]
397    fn with_class(mut self, instance: String, class: String) -> Self {
398        self.platform_specific.class = Some((instance, class));
399        self
400    }
401
402    #[inline]
403    #[cfg(feature = "x11")]
404    fn with_override_redirect(mut self, override_redirect: bool) -> Self {
405        self.platform_specific.override_redirect = override_redirect;
406        self
407    }
408
409    #[inline]
410    #[cfg(feature = "x11")]
411    fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self {
412        self.platform_specific.x11_window_types = x11_window_types;
413        self
414    }
415
416    #[inline]
417    #[cfg(feature = "x11")]
418    fn with_gtk_theme_variant(mut self, variant: String) -> Self {
419        self.platform_specific.gtk_theme_variant = Some(variant);
420        self
421    }
422
423    #[inline]
424    #[cfg(feature = "x11")]
425    fn with_resize_increments<S: Into<Size>>(mut self, increments: S) -> Self {
426        self.platform_specific.resize_increments = Some(increments.into());
427        self
428    }
429
430    #[inline]
431    #[cfg(feature = "x11")]
432    fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
433        self.platform_specific.base_size = Some(base_size.into());
434        self
435    }
436
437    #[inline]
438    #[cfg(feature = "wayland")]
439    fn with_app_id(mut self, app_id: String) -> Self {
440        self.platform_specific.app_id = Some(app_id);
441        self
442    }
443}
444
445/// Additional methods on `MonitorHandle` that are specific to Linux.
446pub trait MonitorHandleExtUnix {
447    /// Returns the inner identifier of the monitor.
448    fn native_id(&self) -> u32;
449}
450
451impl MonitorHandleExtUnix for MonitorHandle {
452    #[inline]
453    fn native_id(&self) -> u32 {
454        self.inner.native_identifier()
455    }
456}
457
458/// A theme for a Wayland's client side decorations.
459#[cfg(feature = "wayland")]
460pub trait Theme: Send + 'static {
461    /// Title bar color.
462    fn element_color(&self, element: Element, window_active: bool) -> ARGBColor;
463
464    /// Color for a given button part.
465    fn button_color(
466        &self,
467        button: Button,
468        state: ButtonState,
469        foreground: bool,
470        window_active: bool,
471    ) -> ARGBColor;
472
473    /// Font name and the size for the title bar.
474    ///
475    /// By default the font is `sans-serif` at the size of 17.
476    ///
477    /// Returning `None` means that title won't be drawn.
478    fn font(&self) -> Option<(String, f32)> {
479        // Not having any title isn't something desirable for the users, so setting it to
480        // something generic.
481        Some((String::from("sans-serif"), 17.))
482    }
483}
484
485/// A button on Wayland's client side decorations.
486#[cfg(feature = "wayland")]
487#[derive(Clone, Copy, Debug, PartialEq, Eq)]
488pub enum Button {
489    /// Button that maximizes the window.
490    Maximize,
491
492    /// Button that minimizes the window.
493    Minimize,
494
495    /// Button that closes the window.
496    Close,
497}
498
499/// A button state of the button on Wayland's client side decorations.
500#[cfg(feature = "wayland")]
501#[derive(Clone, Copy, Debug, PartialEq, Eq)]
502pub enum ButtonState {
503    /// Button is being hovered over by pointer.
504    Hovered,
505    /// Button is not being hovered over by pointer.
506    Idle,
507    /// Button is disabled.
508    Disabled,
509}
510
511#[cfg(feature = "wayland")]
512#[derive(Clone, Copy, Debug, PartialEq, Eq)]
513pub enum Element {
514    /// Bar itself.
515    Bar,
516
517    /// Separator between window and title bar.
518    Separator,
519
520    /// Title bar text.
521    Text,
522}
523
524#[cfg(feature = "wayland")]
525#[derive(Clone, Copy, Debug, PartialEq, Eq)]
526pub struct ARGBColor {
527    pub a: u8,
528    pub r: u8,
529    pub g: u8,
530    pub b: u8,
531}