Skip to content

Commit

Permalink
feat(linux): Add necessary features for creating GL windows (#495)
Browse files Browse the repository at this point in the history
* Add draw_event platform attribute

* Add EventLoopWindowTargetExtUnix

* Add x11 module

* Replace draw_event to auto_transparent attribute

* Cargo fmt and add change file

* Address fixes according to review.

- Rename trait method name to `with_transparent_draw`.
- Use enum struct variant on WireUpEvents.
- Hide doc on x11 module.

* Update change file
  • Loading branch information
Ngo Iok Ui (Wu Yu Wei) committed Jul 25, 2022
1 parent b905852 commit db7e5cb
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 20 deletions.
7 changes: 7 additions & 0 deletions .changes/gl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"tao": patch
---

- On Linux, add `EventLoopWindowTargetExtUnix` for methods to determine if the backend is x11 or wayland.
- On Linux, add `x11` module for glutin internal use. This is basically just x11-dl, but winit secretly exports it.
- On Linux, add `WindowBuilder::with_transparent_draw` to disable the internal draw for transparent window and allows users to draw it manually.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,4 @@ dirs-next = { version = "2.0.0", optional = true }
x11-dl = "2.19"
uuid = { version = "1.1", features = [ "v4" ] }
png = "0.17"
parking_lot = "0.12"
97 changes: 95 additions & 2 deletions src/platform/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@
target_os = "openbsd"
))]

use std::{os::raw::c_int, sync::Arc};

// XConnection utilities
#[doc(hidden)]
pub use crate::platform_impl::x11;

pub use crate::platform_impl::{hit_test, EventLoop as UnixEventLoop};
use crate::{
event_loop::EventLoop,
platform_impl::Parent,
event_loop::{EventLoop, EventLoopWindowTarget},
platform_impl::{x11::xdisplay::XError, Parent},
window::{Window, WindowBuilder},
};

use self::x11::xdisplay::XConnection;

/// Additional methods on `Window` that are specific to Unix.
pub trait WindowExtUnix {
/// Returns the `ApplicatonWindow` from gtk crate that is used by this window.
Expand All @@ -41,6 +49,13 @@ pub trait WindowBuilderExtUnix {
/// Set this window as a transient dialog for `parent`
/// <https://gtk-rs.org/gtk3-rs/stable/latest/docs/gdk/struct.Window.html#method.set_transient_for>
fn with_transient_for(self, parent: gtk::ApplicationWindow) -> WindowBuilder;

/// Whether to enable or disable the internal draw for transparent window.
///
/// When tranparent attribute is enabled, we will call `connect_draw` and draw a transparent background.
/// For anyone who wants to draw the background themselves, set this to `false`.
/// Default is `true`.
fn with_transparent_draw(self, draw: bool) -> WindowBuilder;
}

impl WindowBuilderExtUnix for WindowBuilder {
Expand All @@ -53,6 +68,11 @@ impl WindowBuilderExtUnix for WindowBuilder {
self.platform_specific.parent = Parent::ChildOf(parent);
self
}

fn with_transparent_draw(mut self, draw: bool) -> WindowBuilder {
self.platform_specific.auto_transparent = draw;
self
}
}

/// Additional methods on `EventLoop` that are specific to Unix.
Expand All @@ -79,3 +99,76 @@ impl<T> EventLoopExtUnix for EventLoop<T> {
wrap_ev(UnixEventLoop::new_any_thread())
}
}

/// Additional methods on `EventLoopWindowTarget` that are specific to Unix.
pub trait EventLoopWindowTargetExtUnix {
/// True if the `EventLoopWindowTarget` uses Wayland.
fn is_wayland(&self) -> bool;

/// True if the `EventLoopWindowTarget` uses X11.
fn is_x11(&self) -> bool;

fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;

// /// Returns a pointer to the `wl_display` object of wayland that is used by this
// /// `EventLoopWindowTarget`.
// ///
// /// Returns `None` if the `EventLoop` doesn't use wayland (if it uses xlib for example).
// ///
// /// The pointer will become invalid when the winit `EventLoop` is destroyed.
// fn wayland_display(&self) -> Option<*mut raw::c_void>;
}

impl<T> EventLoopWindowTargetExtUnix for EventLoopWindowTarget<T> {
#[inline]
fn is_wayland(&self) -> bool {
self.p.is_wayland()
}

#[inline]
fn is_x11(&self) -> bool {
!self.p.is_wayland()
}

#[inline]
fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
if self.is_x11() {
if let Ok(xconn) = XConnection::new(Some(x_error_callback)) {
Some(Arc::new(xconn))
} else {
None
}
} else {
None
}
}

// #[inline]
// fn wayland_display(&self) -> Option<*mut raw::c_void> {
// match self.p {
// LinuxEventLoopWindowTarget::Wayland(ref p) => {
// Some(p.display().get_display_ptr() as *mut _)
// }
// #[cfg(feature = "x11")]
// _ => None,
// }
// }
}

unsafe extern "C" fn x_error_callback(
_display: *mut x11::ffi::Display,
event: *mut x11::ffi::XErrorEvent,
) -> c_int {
let error = XError {
// TODO get the error text as description
description: String::new(),
error_code: (*event).error_code,
request_code: (*event).request_code,
minor_code: (*event).minor_code,
};

error!("X11 error: {:#?}", error);

// Fun fact: this return value is completely ignored.
0
}
23 changes: 22 additions & 1 deletion src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ impl<T> EventLoopWindowTarget<T> {
}
RawDisplayHandle::Xlib(display_handle)
}

pub fn is_wayland(&self) -> bool {
self.display.backend().is_wayland()
}
}

pub struct EventLoop<T: 'static> {
Expand Down Expand Up @@ -346,7 +350,7 @@ impl<T: 'static> EventLoop<T> {
window.input_shape_combine_region(None)
};
}
WindowRequest::WireUpEvents => {
WindowRequest::WireUpEvents { transparent } => {
window.add_events(
EventMask::POINTER_MOTION_MASK
| EventMask::BUTTON1_MOTION_MASK
Expand Down Expand Up @@ -737,6 +741,23 @@ impl<T: 'static> EventLoop<T> {
}
Inhibit(false)
});

// Receive draw events of the window.
let draw_clone = draw_tx.clone();
window.connect_draw(move |_, cr| {
if let Err(e) = draw_clone.send(id) {
log::warn!("Failed to send redraw event to event channel: {}", e);
}

if transparent {
cr.set_source_rgba(0., 0., 0., 0.);
cr.set_operator(cairo::Operator::Source);
let _ = cr.paint();
cr.set_operator(cairo::Operator::Over);
}

Inhibit(false)
});
}
WindowRequest::Redraw => {
if let Err(e) = draw_tx.send(id) {
Expand Down
14 changes: 13 additions & 1 deletion src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod monitor;
#[cfg(feature = "tray")]
mod system_tray;
mod window;
pub mod x11;

#[cfg(feature = "tray")]
pub use self::system_tray::{SystemTray, SystemTrayBuilder};
Expand Down Expand Up @@ -55,10 +56,21 @@ impl Default for Parent {
}
}

#[derive(Clone, Default)]
#[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub parent: Parent,
pub skip_taskbar: bool,
pub auto_transparent: bool,
}

impl Default for PlatformSpecificWindowBuilderAttributes {
fn default() -> Self {
Self {
parent: Default::default(),
skip_taskbar: Default::default(),
auto_transparent: true,
}
}
}

unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
Expand Down
38 changes: 22 additions & 16 deletions src/platform_impl/linux/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::{
};

use gdk::{WindowEdge, WindowState};
use glib::translate::ToGlibPtr;
use gtk::{prelude::*, traits::SettingsExt, AccelGroup, Orientation, Settings};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XlibDisplayHandle, XlibWindowHandle};

Expand Down Expand Up @@ -139,22 +140,20 @@ impl Window {
window.move_(x, y);
}

// Set Transparent
if attributes.transparent {
if let Some(screen) = window.screen() {
if let Some(visual) = screen.rgba_visual() {
window.set_visual(Some(&visual));
}
// Set GDK Visual
if let Some(screen) = window.screen() {
if let Some(visual) = screen.rgba_visual() {
window.set_visual(Some(&visual));
}
}

window.connect_draw(|_, cr| {
cr.set_source_rgba(0., 0., 0., 0.);
cr.set_operator(cairo::Operator::Source);
let _ = cr.paint();
cr.set_operator(cairo::Operator::Over);
Inhibit(false)
});
window.set_app_paintable(true);
// Set a few attributes to make the window can be painted.
// See Gtk drawing model for more info:
// https://docs.gtk.org/gtk3/drawing-model.html
window.set_app_paintable(true);
let widget = window.upcast_ref::<gtk::Widget>();
unsafe {
gtk::ffi::gtk_widget_set_double_buffered(widget.to_glib_none().0, 0);
}

// We always create a box and allocate menubar, so if they set_menu after creation
Expand Down Expand Up @@ -284,7 +283,14 @@ impl Window {
scale_factor_clone.store(window.scale_factor(), Ordering::Release);
});

if let Err(e) = window_requests_tx.send((window_id, WindowRequest::WireUpEvents)) {
// Check if we should paint the transparent background ourselves.
let mut transparent = false;
if attributes.transparent && pl_attribs.auto_transparent {
transparent = true;
}
if let Err(e) =
window_requests_tx.send((window_id, WindowRequest::WireUpEvents { transparent }))
{
log::warn!("Fail to send wire up events request: {}", e);
}

Expand Down Expand Up @@ -743,7 +749,7 @@ pub enum WindowRequest {
CursorIcon(Option<CursorIcon>),
CursorPosition((i32, i32)),
CursorIgnoreEvents(bool),
WireUpEvents,
WireUpEvents { transparent: bool },
Redraw,
Menu((Option<MenuItem>, Option<MenuId>)),
SetMenu((Option<menu::Menu>, AccelGroup, gtk::MenuBar)),
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/linux/x11/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub use x11_dl::{
error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*, xrandr::*,
xrender::*,
};
4 changes: 4 additions & 0 deletions src/platform_impl/linux/x11/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod ffi;
pub mod xdisplay;

pub use xdisplay::XConnection;

0 comments on commit db7e5cb

Please sign in to comment.