1 #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
2 
3 use std::os::raw;
4 use std::ptr;
5 use std::sync::Arc;
6 
7 use sctk::window::{ButtonState, Theme};
8 
9 use {
10     EventsLoop,
11     LogicalSize,
12     MonitorId,
13     Window,
14     WindowBuilder,
15 };
16 use platform::{
17     EventsLoop as LinuxEventsLoop,
18     Window as LinuxWindow,
19 };
20 use platform::x11::XConnection;
21 use platform::x11::ffi::XVisualInfo;
22 
23 // TODO: stupid hack so that glutin can do its work
24 #[doc(hidden)]
25 pub use platform::x11;
26 
27 pub use platform::XNotSupported;
28 pub use platform::x11::util::WindowType as XWindowType;
29 
30 /// Theme for wayland client side decorations
31 ///
32 /// Colors must be in ARGB8888 format
33 pub struct WaylandTheme {
34     /// Primary color when the window is focused
35     pub primary_active: [u8; 4],
36     /// Primary color when the window is unfocused
37     pub primary_inactive: [u8; 4],
38     /// Secondary color when the window is focused
39     pub secondary_active: [u8; 4],
40     /// Secondary color when the window is unfocused
41     pub secondary_inactive: [u8; 4],
42     /// Close button color when hovered over
43     pub close_button_hovered: [u8; 4],
44     /// Close button color
45     pub close_button: [u8; 4],
46     /// Close button color when hovered over
47     pub maximize_button_hovered: [u8; 4],
48     /// Maximize button color
49     pub maximize_button: [u8; 4],
50     /// Minimize button color when hovered over
51     pub minimize_button_hovered: [u8; 4],
52     /// Minimize button color
53     pub minimize_button: [u8; 4],
54 }
55 
56 struct WaylandThemeObject(WaylandTheme);
57 
58 impl Theme for WaylandThemeObject {
get_primary_color(&self, active: bool) -> [u8; 4]59     fn get_primary_color(&self, active: bool) -> [u8; 4] {
60         if active {
61             self.0.primary_active
62         } else {
63             self.0.primary_inactive
64         }
65     }
66 
67     // Used for division line
get_secondary_color(&self, active: bool) -> [u8; 4]68     fn get_secondary_color(&self, active: bool) -> [u8; 4] {
69         if active {
70             self.0.secondary_active
71         } else {
72             self.0.secondary_inactive
73         }
74     }
75 
get_close_button_color(&self, state: ButtonState) -> [u8; 4]76     fn get_close_button_color(&self, state: ButtonState) -> [u8; 4] {
77         match state {
78             ButtonState::Hovered => self.0.close_button_hovered,
79             _ => self.0.close_button,
80         }
81     }
82 
get_maximize_button_color(&self, state: ButtonState) -> [u8; 4]83     fn get_maximize_button_color(&self, state: ButtonState) -> [u8; 4] {
84         match state {
85             ButtonState::Hovered => self.0.maximize_button_hovered,
86             _ => self.0.maximize_button,
87         }
88     }
89 
get_minimize_button_color(&self, state: ButtonState) -> [u8; 4]90     fn get_minimize_button_color(&self, state: ButtonState) -> [u8; 4] {
91         match state {
92             ButtonState::Hovered => self.0.minimize_button_hovered,
93             _ => self.0.minimize_button,
94         }
95     }
96 }
97 
98 /// Additional methods on `EventsLoop` that are specific to Linux.
99 pub trait EventsLoopExt {
100     /// Builds a new `EventsLoop` that is forced to use X11.
new_x11() -> Result<Self, XNotSupported> where Self: Sized101     fn new_x11() -> Result<Self, XNotSupported>
102         where Self: Sized;
103 
104     /// Builds a new `EventsLoop` that is forced to use Wayland.
new_wayland() -> Self where Self: Sized105     fn new_wayland() -> Self
106         where Self: Sized;
107 
108     /// True if the `EventsLoop` uses Wayland.
is_wayland(&self) -> bool109     fn is_wayland(&self) -> bool;
110 
111     /// True if the `EventsLoop` uses X11.
is_x11(&self) -> bool112     fn is_x11(&self) -> bool;
113 
114     #[doc(hidden)]
get_xlib_xconnection(&self) -> Option<Arc<XConnection>>115     fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
116 
117     /// Returns a pointer to the `wl_display` object of wayland that is used by this `EventsLoop`.
118     ///
119     /// Returns `None` if the `EventsLoop` doesn't use wayland (if it uses xlib for example).
120     ///
121     /// The pointer will become invalid when the glutin `EventsLoop` is destroyed.
get_wayland_display(&self) -> Option<*mut raw::c_void>122     fn get_wayland_display(&self) -> Option<*mut raw::c_void>;
123 }
124 
125 impl EventsLoopExt for EventsLoop {
126     #[inline]
new_x11() -> Result<Self, XNotSupported>127     fn new_x11() -> Result<Self, XNotSupported> {
128         LinuxEventsLoop::new_x11().map(|ev|
129             EventsLoop {
130                 events_loop: ev,
131                 _marker: ::std::marker::PhantomData,
132             }
133         )
134     }
135 
136     #[inline]
new_wayland() -> Self137     fn new_wayland() -> Self {
138         EventsLoop {
139             events_loop: match LinuxEventsLoop::new_wayland() {
140                 Ok(e) => e,
141                 Err(_) => panic!()      // TODO: propagate
142             },
143             _marker: ::std::marker::PhantomData,
144         }
145     }
146 
147     #[inline]
is_wayland(&self) -> bool148     fn is_wayland(&self) -> bool {
149         self.events_loop.is_wayland()
150     }
151 
152     #[inline]
is_x11(&self) -> bool153     fn is_x11(&self) -> bool {
154         !self.events_loop.is_wayland()
155     }
156 
157     #[inline]
158     #[doc(hidden)]
get_xlib_xconnection(&self) -> Option<Arc<XConnection>>159     fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
160         self.events_loop.x_connection().cloned()
161     }
162 
163     #[inline]
get_wayland_display(&self) -> Option<*mut raw::c_void>164     fn get_wayland_display(&self) -> Option<*mut raw::c_void> {
165         match self.events_loop {
166             LinuxEventsLoop::Wayland(ref e) => Some(e.get_display().c_ptr() as *mut _),
167             _ => None
168         }
169     }
170 }
171 
172 /// Additional methods on `Window` that are specific to Unix.
173 pub trait WindowExt {
174     /// Returns the ID of the `Window` xlib object that is used by this window.
175     ///
176     /// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
get_xlib_window(&self) -> Option<raw::c_ulong>177     fn get_xlib_window(&self) -> Option<raw::c_ulong>;
178 
179     /// Returns a pointer to the `Display` object of xlib that is used by this window.
180     ///
181     /// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
182     ///
183     /// The pointer will become invalid when the glutin `Window` is destroyed.
get_xlib_display(&self) -> Option<*mut raw::c_void>184     fn get_xlib_display(&self) -> Option<*mut raw::c_void>;
185 
get_xlib_screen_id(&self) -> Option<raw::c_int>186     fn get_xlib_screen_id(&self) -> Option<raw::c_int>;
187 
188     #[doc(hidden)]
get_xlib_xconnection(&self) -> Option<Arc<XConnection>>189     fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
190 
191     /// Set window urgency hint (`XUrgencyHint`). Only relevant on X.
set_urgent(&self, is_urgent: bool)192     fn set_urgent(&self, is_urgent: bool);
193 
194     /// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
195     ///
196     /// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
197     ///
198     /// The pointer will become invalid when the glutin `Window` is destroyed.
get_xcb_connection(&self) -> Option<*mut raw::c_void>199     fn get_xcb_connection(&self) -> Option<*mut raw::c_void>;
200 
201     /// Returns a pointer to the `wl_surface` object of wayland that is used by this window.
202     ///
203     /// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
204     ///
205     /// The pointer will become invalid when the glutin `Window` is destroyed.
get_wayland_surface(&self) -> Option<*mut raw::c_void>206     fn get_wayland_surface(&self) -> Option<*mut raw::c_void>;
207 
208     /// Returns a pointer to the `wl_display` object of wayland that is used by this window.
209     ///
210     /// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
211     ///
212     /// The pointer will become invalid when the glutin `Window` is destroyed.
get_wayland_display(&self) -> Option<*mut raw::c_void>213     fn get_wayland_display(&self) -> Option<*mut raw::c_void>;
214 
215     /// Sets the color theme of the client side window decorations on wayland
set_wayland_theme(&self, theme: WaylandTheme)216     fn set_wayland_theme(&self, theme: WaylandTheme);
217 
218     /// Check if the window is ready for drawing
219     ///
220     /// It is a remnant of a previous implementation detail for the
221     /// wayland backend, and is no longer relevant.
222     ///
223     /// Always return true.
224     #[deprecated]
is_ready(&self) -> bool225     fn is_ready(&self) -> bool;
226 }
227 
228 impl WindowExt for Window {
229     #[inline]
get_xlib_window(&self) -> Option<raw::c_ulong>230     fn get_xlib_window(&self) -> Option<raw::c_ulong> {
231         match self.window {
232             LinuxWindow::X(ref w) => Some(w.get_xlib_window()),
233             _ => None
234         }
235     }
236 
237     #[inline]
get_xlib_display(&self) -> Option<*mut raw::c_void>238     fn get_xlib_display(&self) -> Option<*mut raw::c_void> {
239         match self.window {
240             LinuxWindow::X(ref w) => Some(w.get_xlib_display()),
241             _ => None
242         }
243     }
244 
245     #[inline]
get_xlib_screen_id(&self) -> Option<raw::c_int>246     fn get_xlib_screen_id(&self) -> Option<raw::c_int> {
247         match self.window {
248             LinuxWindow::X(ref w) => Some(w.get_xlib_screen_id()),
249             _ => None
250         }
251     }
252 
253     #[inline]
254     #[doc(hidden)]
get_xlib_xconnection(&self) -> Option<Arc<XConnection>>255     fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
256         match self.window {
257             LinuxWindow::X(ref w) => Some(w.get_xlib_xconnection()),
258             _ => None
259         }
260     }
261 
262     #[inline]
get_xcb_connection(&self) -> Option<*mut raw::c_void>263     fn get_xcb_connection(&self) -> Option<*mut raw::c_void> {
264         match self.window {
265             LinuxWindow::X(ref w) => Some(w.get_xcb_connection()),
266             _ => None
267         }
268     }
269 
270     #[inline]
set_urgent(&self, is_urgent: bool)271     fn set_urgent(&self, is_urgent: bool) {
272         if let LinuxWindow::X(ref w) = self.window {
273             w.set_urgent(is_urgent);
274         }
275     }
276 
277     #[inline]
get_wayland_surface(&self) -> Option<*mut raw::c_void>278     fn get_wayland_surface(&self) -> Option<*mut raw::c_void> {
279         match self.window {
280             LinuxWindow::Wayland(ref w) => Some(w.get_surface().c_ptr() as *mut _),
281             _ => None
282         }
283     }
284 
285     #[inline]
get_wayland_display(&self) -> Option<*mut raw::c_void>286     fn get_wayland_display(&self) -> Option<*mut raw::c_void> {
287         match self.window {
288             LinuxWindow::Wayland(ref w) => Some(w.get_display().c_ptr() as *mut _),
289             _ => None
290         }
291     }
292 
293     #[inline]
set_wayland_theme(&self, theme: WaylandTheme)294     fn set_wayland_theme(&self, theme: WaylandTheme) {
295         match self.window {
296             LinuxWindow::Wayland(ref w) => w.set_theme(WaylandThemeObject(theme)),
297             _ => {}
298         }
299     }
300 
301     #[inline]
is_ready(&self) -> bool302     fn is_ready(&self) -> bool {
303         true
304     }
305 }
306 
307 /// Additional methods on `WindowBuilder` that are specific to Unix.
308 pub trait WindowBuilderExt {
with_x11_visual<T>(self, visual_infos: *const T) -> WindowBuilder309     fn with_x11_visual<T>(self, visual_infos: *const T) -> WindowBuilder;
with_x11_screen(self, screen_id: i32) -> WindowBuilder310     fn with_x11_screen(self, screen_id: i32) -> WindowBuilder;
311 
312     /// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11.
with_class(self, class: String, instance: String) -> WindowBuilder313     fn with_class(self, class: String, instance: String) -> WindowBuilder;
314     /// Build window with override-redirect flag; defaults to false. Only relevant on X11.
with_override_redirect(self, override_redirect: bool) -> WindowBuilder315     fn with_override_redirect(self, override_redirect: bool) -> WindowBuilder;
316     /// Build window with `_NET_WM_WINDOW_TYPE` hint; defaults to `Normal`. Only relevant on X11.
with_x11_window_type(self, x11_window_type: XWindowType) -> WindowBuilder317     fn with_x11_window_type(self, x11_window_type: XWindowType) -> WindowBuilder;
318     /// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11.
with_gtk_theme_variant(self, variant: String) -> WindowBuilder319     fn with_gtk_theme_variant(self, variant: String) -> WindowBuilder;
320     /// Build window with resize increment hint. Only implemented on X11.
with_resize_increments(self, increments: LogicalSize) -> WindowBuilder321     fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
322     /// Build window with base size hint. Only implemented on X11.
with_base_size(self, base_size: LogicalSize) -> WindowBuilder323     fn with_base_size(self, base_size: LogicalSize) -> WindowBuilder;
324 
325     /// Build window with a given application ID. It should match the `.desktop` file distributed with
326     /// your program. Only relevant on Wayland.
327     ///
328     /// For details about application ID conventions, see the
329     /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
with_app_id(self, app_id: String) -> WindowBuilder330     fn with_app_id(self, app_id: String) -> WindowBuilder;
331 }
332 
333 impl WindowBuilderExt for WindowBuilder {
334     #[inline]
with_x11_visual<T>(mut self, visual_infos: *const T) -> WindowBuilder335     fn with_x11_visual<T>(mut self, visual_infos: *const T) -> WindowBuilder {
336         self.platform_specific.visual_infos = Some(
337             unsafe { ptr::read(visual_infos as *const XVisualInfo) }
338         );
339         self
340     }
341 
342     #[inline]
with_x11_screen(mut self, screen_id: i32) -> WindowBuilder343     fn with_x11_screen(mut self, screen_id: i32) -> WindowBuilder {
344         self.platform_specific.screen_id = Some(screen_id);
345         self
346     }
347 
348     #[inline]
with_class(mut self, instance: String, class: String) -> WindowBuilder349     fn with_class(mut self, instance: String, class: String) -> WindowBuilder {
350         self.platform_specific.class = Some((instance, class));
351         self
352     }
353 
354     #[inline]
with_override_redirect(mut self, override_redirect: bool) -> WindowBuilder355     fn with_override_redirect(mut self, override_redirect: bool) -> WindowBuilder {
356         self.platform_specific.override_redirect = override_redirect;
357         self
358     }
359 
360     #[inline]
with_x11_window_type(mut self, x11_window_type: XWindowType) -> WindowBuilder361     fn with_x11_window_type(mut self, x11_window_type: XWindowType) -> WindowBuilder {
362         self.platform_specific.x11_window_type = x11_window_type;
363         self
364     }
365 
366     #[inline]
with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder367     fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder {
368         self.platform_specific.resize_increments = Some(increments.into());
369         self
370     }
371 
372     #[inline]
with_base_size(mut self, base_size: LogicalSize) -> WindowBuilder373     fn with_base_size(mut self, base_size: LogicalSize) -> WindowBuilder {
374         self.platform_specific.base_size = Some(base_size.into());
375         self
376     }
377 
378     #[inline]
with_gtk_theme_variant(mut self, variant: String) -> WindowBuilder379     fn with_gtk_theme_variant(mut self, variant: String) -> WindowBuilder {
380         self.platform_specific.gtk_theme_variant = Some(variant);
381         self
382     }
383 
384     #[inline]
with_app_id(mut self, app_id: String) -> WindowBuilder385     fn with_app_id(mut self, app_id: String) -> WindowBuilder {
386         self.platform_specific.app_id = Some(app_id);
387         self
388     }
389 }
390 
391 /// Additional methods on `MonitorId` that are specific to Linux.
392 pub trait MonitorIdExt {
393     /// Returns the inner identifier of the monitor.
native_id(&self) -> u32394     fn native_id(&self) -> u32;
395 }
396 
397 impl MonitorIdExt for MonitorId {
398     #[inline]
native_id(&self) -> u32399     fn native_id(&self) -> u32 {
400         self.inner.get_native_identifier()
401     }
402 }
403