Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for wlr_layer_shell windows #2832

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 103 additions & 1 deletion src/platform/wayland.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::os::raw;

use sctk::reexports::client::Proxy;
use sctk::{
reexports::client::Proxy,
shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer},
};

use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
Expand Down Expand Up @@ -90,6 +93,16 @@ pub trait WindowExtWayland {
///
/// The pointer will become invalid when the [`Window`] is destroyed.
fn wayland_display(&self) -> Option<*mut raw::c_void>;

fn set_anchor(&self, anchor: Anchor);

fn set_exclusive_zone(&self, exclusive_zone: i32);

fn set_margin(&self, top: i32, right: i32, bottom: i32, left: i32);

fn set_keyboard_interactivity(&self, keyboard_interactivity: KeyboardInteractivity);

fn set_layer(&self, anchor: Layer);
}

impl WindowExtWayland for Window {
Expand All @@ -110,6 +123,46 @@ impl WindowExtWayland for Window {
_ => None,
}
}

fn set_anchor(&self, anchor: Anchor) {
match self.window {
LinuxWindow::Wayland(ref w) => w.set_anchor(anchor),
#[cfg(x11_platform)]
_ => {}
}
}

fn set_exclusive_zone(&self, exclusive_zone: i32) {
match self.window {
LinuxWindow::Wayland(ref w) => w.set_exclusive_zone(exclusive_zone),
#[cfg(x11_platform)]
_ => {}
}
}

fn set_margin(&self, top: i32, right: i32, bottom: i32, left: i32) {
match self.window {
LinuxWindow::Wayland(ref w) => w.set_margin(top, right, bottom, left),
#[cfg(x11_platform)]
_ => {}
}
}

fn set_keyboard_interactivity(&self, keyboard_interactivity: KeyboardInteractivity) {
match self.window {
LinuxWindow::Wayland(ref w) => w.set_keyboard_interactivity(keyboard_interactivity),
#[cfg(x11_platform)]
_ => {}
}
}

fn set_layer(&self, layer: Layer) {
match self.window {
LinuxWindow::Wayland(ref w) => w.set_layer(layer),
#[cfg(x11_platform)]
_ => {}
}
}
}

/// Additional methods on [`WindowBuilder`] that are specific to Wayland.
Expand All @@ -122,6 +175,22 @@ pub trait WindowBuilderExtWayland {
/// For details about application ID conventions, see the
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;

// TODO(theonlymrcat): Users shouldn't need to pull sctk in. Reexport? or Redefine?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the types from sctk are used in the API (without or without a re-export) that makes updating sctk a breaking change to winit. But that might not be a problem?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. Currently, the only public dependency in winit (except for cursor_icon, which is under the rust-windowing org) is on the android-activity crate, and it's a simple re-export of the entire crate for the android platform.

In this case, a re-export seems to have negligible benefit, and is very easily avoidable; so I'll redefine the types.


/// Create this window using the WLR Layer Shell protocol.
///
/// Building this window will fail if the compositor does not support the `zwlr_layer_shell_v1`
/// protocol.
fn with_layer_shell(self, layer: Layer) -> Self;

fn with_anchor(self, anchor: Anchor) -> Self;

fn with_exclusive_zone(self, exclusive_zone: i32) -> Self;

fn with_margin(self, top: i32, right: i32, bottom: i32, left: i32) -> Self;

fn with_keyboard_interactivity(self, keyboard_interactivity: KeyboardInteractivity) -> Self;
}

impl WindowBuilderExtWayland for WindowBuilder {
Expand All @@ -130,6 +199,39 @@ impl WindowBuilderExtWayland for WindowBuilder {
self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into()));
self
}

#[inline]
fn with_layer_shell(mut self, layer: Layer) -> Self {
self.platform_specific.layer_shell = Some(layer);
self
}

#[inline]
fn with_anchor(mut self, anchor: Anchor) -> Self {
self.platform_specific.anchor = Some(anchor);
self
}

#[inline]
fn with_exclusive_zone(mut self, exclusive_zone: i32) -> Self {
self.platform_specific.exclusive_zone = Some(exclusive_zone);
self
}

#[inline]
fn with_margin(mut self, top: i32, right: i32, bottom: i32, left: i32) -> Self {
self.platform_specific.margin = Some((top, right, bottom, left));
self
}

#[inline]
fn with_keyboard_interactivity(
mut self,
keyboard_interactivity: KeyboardInteractivity,
) -> Self {
self.platform_specific.keyboard_interactivity = Some(keyboard_interactivity);
self
}
}

/// Additional methods on `MonitorHandle` that are specific to Wayland.
Expand Down
23 changes: 23 additions & 0 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ use crate::{
},
};

#[cfg(wayland_platform)]
use sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};

pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
pub(self) use crate::platform_impl::Fullscreen;

Expand Down Expand Up @@ -97,6 +100,16 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub override_redirect: bool,
#[cfg(x11_platform)]
pub x11_window_types: Vec<XWindowType>,
#[cfg(wayland_platform)]
pub layer_shell: Option<Layer>,
#[cfg(wayland_platform)]
pub anchor: Option<Anchor>,
#[cfg(wayland_platform)]
pub exclusive_zone: Option<i32>,
#[cfg(wayland_platform)]
pub margin: Option<(i32, i32, i32, i32)>,
#[cfg(wayland_platform)]
pub keyboard_interactivity: Option<KeyboardInteractivity>,
}

impl Default for PlatformSpecificWindowBuilderAttributes {
Expand All @@ -113,6 +126,16 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
override_redirect: false,
#[cfg(x11_platform)]
x11_window_types: vec![XWindowType::Normal],
#[cfg(wayland_platform)]
layer_shell: None,
#[cfg(wayland_platform)]
anchor: None,
#[cfg(wayland_platform)]
exclusive_zone: None,
#[cfg(wayland_platform)]
margin: None,
#[cfg(wayland_platform)]
keyboard_interactivity: None,
}
}
}
Expand Down
53 changes: 52 additions & 1 deletion src/platform_impl/linux/wayland/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use sctk::output::{OutputHandler, OutputState};
use sctk::registry::{ProvidesRegistryState, RegistryState};
use sctk::seat::pointer::ThemedPointer;
use sctk::seat::SeatState;
use sctk::shell::wlr_layer::{LayerShell, LayerShellHandler, LayerSurface, LayerSurfaceConfigure};
use sctk::shell::xdg::window::{Window, WindowConfigure, WindowHandler};
use sctk::shell::xdg::XdgShell;
use sctk::shell::WaylandSurface;
Expand Down Expand Up @@ -59,6 +60,9 @@ pub struct WinitState {
/// The XDG shell that is used for widnows.
pub xdg_shell: XdgShell,

/// The layer shell for layer surfaces
pub layer_shell: LayerShell,

/// The currently present windows.
pub windows: RefCell<FnvHashMap<WindowId, Arc<Mutex<WindowState>>>>,

Expand Down Expand Up @@ -148,6 +152,8 @@ impl WinitState {
xdg_shell: XdgShell::bind(globals, queue_handle)?,
xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(),

layer_shell: LayerShell::bind(globals, queue_handle)?,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this make winit fail on compositors without layer-shell support? Like Gnome.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way it's written -> yes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So definitely something that would need to be fixed here.

An application using this will want some way to recognize when X is being used instead of Wayland, and when Wayland is in use but the layer-shell protocol isn't available.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but I mean, the entire approach present here I don't like, because it complicates internal code without a reason for that. I'd also like to have a way to bind stuff lazily.


windows: Default::default(),
window_requests: Default::default(),
window_compositor_updates: Vec::new(),
Expand Down Expand Up @@ -273,7 +279,51 @@ impl WindowHandler for WinitState {
.expect("got configure for dead window.")
.lock()
.unwrap()
.configure(configure, &self.shm, &self.subcompositor_state);
.configure_xdg(configure, &self.shm, &self.subcompositor_state);

self.window_compositor_updates[pos].size = Some(new_size);
}
}

impl LayerShellHandler for WinitState {
fn closed(&mut self, _: &Connection, _: &QueueHandle<Self>, layer: &LayerSurface) {
let window_id = super::make_wid(layer.wl_surface());
Self::queue_close(&mut self.window_compositor_updates, window_id);
}

fn configure(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
layer: &LayerSurface,
configure: LayerSurfaceConfigure,
_serial: u32,
) {
let window_id = super::make_wid(layer.wl_surface());

let pos = if let Some(pos) = self
.window_compositor_updates
.iter()
.position(|update| update.window_id == window_id)
{
pos
} else {
self.window_compositor_updates
.push(WindowCompositorUpdate::new(window_id));
self.window_compositor_updates.len() - 1
};

// Populate the configure to the window.
//
// XXX the size on the window will be updated right before dispatching the size to the user.
let new_size = self
.windows
.get_mut()
.get_mut(&window_id)
.expect("got configure for dead window.")
.lock()
.unwrap()
.configure_layer(configure);

self.window_compositor_updates[pos].size = Some(new_size);
}
Expand Down Expand Up @@ -366,3 +416,4 @@ sctk::delegate_registry!(WinitState);
sctk::delegate_shm!(WinitState);
sctk::delegate_xdg_shell!(WinitState);
sctk::delegate_xdg_window!(WinitState);
sctk::delegate_layer!(WinitState);
Loading