Skip to main content

tao/platform/
unix.rs

1// Copyright 2014-2021 The winit contributors
2// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
3// SPDX-License-Identifier: Apache-2.0
4
5#![cfg(any(
6  target_os = "linux",
7  target_os = "dragonfly",
8  target_os = "freebsd",
9  target_os = "netbsd",
10  target_os = "openbsd"
11))]
12
13#[cfg(feature = "x11")]
14use std::{os::raw::c_int, sync::Arc};
15
16// XConnection utilities
17#[doc(hidden)]
18#[cfg(feature = "x11")]
19pub use crate::platform_impl::x11;
20
21#[cfg(feature = "x11")]
22use crate::platform_impl::x11::xdisplay::XError;
23pub use crate::platform_impl::EventLoop as UnixEventLoop;
24use crate::{
25  error::{ExternalError, OsError},
26  event_loop::{EventLoopBuilder, EventLoopWindowTarget},
27  monitor::MonitorHandle,
28  platform_impl::{Parent, Window as UnixWindow},
29  window::{Window, WindowBuilder},
30};
31
32#[cfg(feature = "x11")]
33use self::x11::xdisplay::XConnection;
34
35/// Additional methods on `EventLoop` that are specific to Unix.
36pub trait EventLoopBuilderExtUnix {
37  /// Whether to allow the event loop to be created off of the main thread.
38  ///
39  /// By default, the window is only allowed to be created on the main
40  /// thread, to make platform compatibility easier.
41  ///
42  /// # `Window` caveats
43  ///
44  /// Note that any `Window` created on the new thread will be destroyed when the thread
45  /// terminates. Attempting to use a `Window` after its parent thread terminates has
46  /// unspecified, although explicitly not undefined, behavior.
47  fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
48
49  /// Set the gtk application id.
50  ///
51  /// If no application ID is given then some features (most notably application uniqueness) will be disabled.
52  fn with_app_id<S: Into<String>>(&mut self, id: S) -> &mut Self;
53}
54
55impl<T> EventLoopBuilderExtUnix for EventLoopBuilder<T> {
56  #[inline]
57  fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
58    self.platform_specific.any_thread = any_thread;
59    self
60  }
61
62  fn with_app_id<S: Into<String>>(&mut self, id: S) -> &mut Self {
63    self.platform_specific.app_id = Some(id.into());
64    self
65  }
66}
67
68/// Additional methods on `Window` that are specific to Unix.
69pub trait WindowExtUnix {
70  /// Create a new Tao window from an existing GTK window. Generally you should use
71  /// the non-Linux `WindowBuilder`, this is for those who need lower level window access
72  /// and know what they're doing.
73  fn new_from_gtk_window<T: 'static>(
74    event_loop_window_target: &EventLoopWindowTarget<T>,
75    window: gtk::ApplicationWindow,
76  ) -> Result<Window, OsError>;
77
78  /// Returns the `gtk::ApplicatonWindow` from gtk crate that is used by this window.
79  fn gtk_window(&self) -> &gtk::ApplicationWindow;
80
81  /// Returns the vertical `gtk::Box` that is added by default as the sole child of this window.
82  /// Returns `None` if the default vertical `gtk::Box` creation was disabled by [`WindowBuilderExtUnix::with_default_vbox`].
83  fn default_vbox(&self) -> Option<&gtk::Box>;
84
85  /// Whether to show the window icon in the taskbar or not.
86  fn set_skip_taskbar(&self, skip: bool) -> Result<(), ExternalError>;
87
88  fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>);
89}
90
91impl WindowExtUnix for Window {
92  fn gtk_window(&self) -> &gtk::ApplicationWindow {
93    &self.window.window
94  }
95
96  fn default_vbox(&self) -> Option<&gtk::Box> {
97    self.window.default_vbox.as_ref()
98  }
99
100  fn set_skip_taskbar(&self, skip: bool) -> Result<(), ExternalError> {
101    self.window.set_skip_taskbar(skip)
102  }
103
104  fn new_from_gtk_window<T: 'static>(
105    event_loop_window_target: &EventLoopWindowTarget<T>,
106    window: gtk::ApplicationWindow,
107  ) -> Result<Window, OsError> {
108    let window = UnixWindow::new_from_gtk_window(&event_loop_window_target.p, window)?;
109    Ok(Window { window })
110  }
111
112  fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) {
113    self.window.set_badge_count(count, desktop_filename);
114  }
115}
116
117pub trait WindowBuilderExtUnix {
118  /// Whether to create the window icon with the taskbar icon or not.
119  fn with_skip_taskbar(self, skip: bool) -> WindowBuilder;
120  /// Set this window as a transient dialog for `parent`
121  /// <https://gtk-rs.org/gtk3-rs/stable/latest/docs/gdk/struct.Window.html#method.set_transient_for>
122  fn with_transient_for(self, parent: &impl gtk::glib::IsA<gtk::Window>) -> WindowBuilder;
123
124  /// Whether to enable or disable the internal draw for transparent window.
125  ///
126  /// When tranparent attribute is enabled, we will call `connect_draw` and draw a transparent background.
127  /// For anyone who wants to draw the background themselves, set this to `false`.
128  /// Default is `true`.
129  fn with_transparent_draw(self, draw: bool) -> WindowBuilder;
130
131  /// Whether to enable or disable the double buffered rendering of the window.
132  ///
133  /// Default is `true`.
134  fn with_double_buffered(self, double_buffered: bool) -> WindowBuilder;
135
136  /// Whether to enable the rgba visual for the window.
137  ///
138  /// Default is `false` but is always `true` if [`WindowAttributes::transparent`](crate::window::WindowAttributes::transparent) is `true`
139  fn with_rgba_visual(self, rgba_visual: bool) -> WindowBuilder;
140
141  /// Wether to set this window as app paintable
142  ///
143  /// <https://docs.gtk.org/gtk3/method.Widget.set_app_paintable.html>
144  ///
145  /// Default is `false` but is always `true` if [`WindowAttributes::transparent`](crate::window::WindowAttributes::transparent) is `true`
146  fn with_app_paintable(self, app_paintable: bool) -> WindowBuilder;
147
148  /// Whether to set cursor moved event. Cursor event is suited for native GUI frameworks and
149  /// games. But it can block gtk's own pipeline occasionally. Turn this off can help Gtk looks
150  /// smoother.
151  ///
152  /// Default is `true`.
153  fn with_cursor_moved_event(self, cursor_moved: bool) -> WindowBuilder;
154
155  /// Whether to create a vertical `gtk::Box` and add it as the sole child of this window.
156  /// Created by default.
157  fn with_default_vbox(self, add: bool) -> WindowBuilder;
158}
159
160impl WindowBuilderExtUnix for WindowBuilder {
161  fn with_skip_taskbar(mut self, skip: bool) -> WindowBuilder {
162    self.platform_specific.skip_taskbar = skip;
163    self
164  }
165
166  fn with_transient_for(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> WindowBuilder {
167    use gtk::glib::Cast;
168    self.platform_specific.parent = Parent::ChildOf(parent.clone().upcast());
169    self
170  }
171
172  fn with_transparent_draw(mut self, draw: bool) -> WindowBuilder {
173    self.platform_specific.auto_transparent = draw;
174    self
175  }
176
177  fn with_double_buffered(mut self, double_buffered: bool) -> WindowBuilder {
178    self.platform_specific.double_buffered = double_buffered;
179    self
180  }
181
182  fn with_rgba_visual(mut self, rgba_visual: bool) -> WindowBuilder {
183    self.platform_specific.rgba_visual = rgba_visual;
184    self
185  }
186
187  fn with_app_paintable(mut self, app_paintable: bool) -> WindowBuilder {
188    self.platform_specific.app_paintable = app_paintable;
189    self
190  }
191
192  fn with_cursor_moved_event(mut self, cursor_moved: bool) -> WindowBuilder {
193    self.platform_specific.cursor_moved = cursor_moved;
194    self
195  }
196
197  fn with_default_vbox(mut self, add: bool) -> WindowBuilder {
198    self.platform_specific.default_vbox = add;
199    self
200  }
201}
202
203/// Additional methods on `EventLoopWindowTarget` that are specific to Unix.
204pub trait EventLoopWindowTargetExtUnix {
205  /// True if the `EventLoopWindowTarget` uses Wayland.
206  fn is_wayland(&self) -> bool;
207
208  /// True if the `EventLoopWindowTarget` uses X11.
209  #[cfg(feature = "x11")]
210  fn is_x11(&self) -> bool;
211
212  #[cfg(feature = "x11")]
213  fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;
214
215  // /// Returns a pointer to the `wl_display` object of wayland that is used by this
216  // /// `EventLoopWindowTarget`.
217  // ///
218  // /// Returns `None` if the `EventLoop` doesn't use wayland (if it uses xlib for example).
219  // ///
220  // /// The pointer will become invalid when the winit `EventLoop` is destroyed.
221  // fn wayland_display(&self) -> Option<*mut raw::c_void>;
222
223  /// Returns the gtk application for this event loop.
224  fn gtk_app(&self) -> &gtk::Application;
225
226  /// Sets the badge count on the taskbar
227  fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>);
228}
229
230impl<T> EventLoopWindowTargetExtUnix for EventLoopWindowTarget<T> {
231  #[inline]
232  fn is_wayland(&self) -> bool {
233    self.p.is_wayland()
234  }
235
236  #[cfg(feature = "x11")]
237  #[inline]
238  fn is_x11(&self) -> bool {
239    !self.p.is_wayland()
240  }
241
242  #[cfg(feature = "x11")]
243  #[inline]
244  fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
245    if self.is_x11() {
246      if let Ok(xconn) = XConnection::new(Some(x_error_callback)) {
247        Some(Arc::new(xconn))
248      } else {
249        None
250      }
251    } else {
252      None
253    }
254  }
255
256  // #[inline]
257  // fn wayland_display(&self) -> Option<*mut raw::c_void> {
258  //     match self.p {
259  //         LinuxEventLoopWindowTarget::Wayland(ref p) => {
260  //             Some(p.display().get_display_ptr() as *mut _)
261  //         }
262  //         #[cfg(feature = "x11")]
263  //         _ => None,
264  //     }
265  // }
266
267  #[inline]
268  fn gtk_app(&self) -> &gtk::Application {
269    &self.p.app
270  }
271
272  #[inline]
273  fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) {
274    self.p.set_badge_count(count, desktop_filename);
275  }
276}
277
278#[cfg(feature = "x11")]
279unsafe extern "C" fn x_error_callback(
280  _display: *mut x11::ffi::Display,
281  event: *mut x11::ffi::XErrorEvent,
282) -> c_int {
283  let error = XError {
284    // TODO get the error text as description
285    description: String::new(),
286    error_code: (*event).error_code,
287    request_code: (*event).request_code,
288    minor_code: (*event).minor_code,
289  };
290
291  error!("X11 error: {:#?}", error);
292
293  // Fun fact: this return value is completely ignored.
294  0
295}
296
297/// Additional methods on `MonitorHandle` that are specific to Unix.
298pub trait MonitorHandleExtUnix {
299  /// Returns the gdk handle of the monitor.
300  fn gdk_monitor(&self) -> &gtk::gdk::Monitor;
301}
302
303impl MonitorHandleExtUnix for MonitorHandle {
304  #[inline]
305  fn gdk_monitor(&self) -> &gtk::gdk::Monitor {
306    &self.inner.monitor
307  }
308}