Support xdg decorations
This commit is contained in:
parent
b2613aec05
commit
0559ace758
5 changed files with 328 additions and 4 deletions
|
|
@ -19,6 +19,8 @@ use wayland_protocols::wp::relative_pointer::zv1::client::{
|
|||
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
||||
zwp_relative_pointer_v1::ZwpRelativePointerV1,
|
||||
};
|
||||
use wayland_protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
|
||||
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1;
|
||||
use wayland_protocols::{
|
||||
wp::{
|
||||
linux_dmabuf::zv1::client::{
|
||||
|
|
@ -140,6 +142,8 @@ delegate_noop!(Globals: ZxdgOutputManagerV1);
|
|||
delegate_noop!(Globals: ZwpPointerConstraintsV1);
|
||||
delegate_noop!(Globals: ZwpTabletManagerV2);
|
||||
delegate_noop!(Globals: XdgActivationV1);
|
||||
delegate_noop!(Globals: ZxdgDecorationManagerV1);
|
||||
delegate_noop!(Globals: ignore ZxdgToplevelDecorationV1);
|
||||
|
||||
impl Dispatch<WlRegistry, GlobalListContents> for Globals {
|
||||
fn event(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ mod tests;
|
|||
|
||||
use self::event::*;
|
||||
use crate::clientside::*;
|
||||
use crate::xstate::{WindowDims, WmHints, WmName, WmNormalHints};
|
||||
use crate::xstate::{Decorations, WindowDims, WmHints, WmName, WmNormalHints};
|
||||
use crate::{X11Selection, XConnection};
|
||||
use log::{debug, warn};
|
||||
use rustix::event::{poll, PollFd, PollFlags};
|
||||
|
|
@ -22,6 +22,10 @@ use std::os::fd::{AsFd, BorrowedFd};
|
|||
use std::os::unix::net::UnixStream;
|
||||
use std::rc::{Rc, Weak};
|
||||
use wayland_client::{globals::Global, protocol as client, Proxy};
|
||||
use wayland_protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
|
||||
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::{
|
||||
self, ZxdgToplevelDecorationV1,
|
||||
};
|
||||
use wayland_protocols::{
|
||||
wp::{
|
||||
linux_dmabuf::zv1::{client as c_dmabuf, server as s_dmabuf},
|
||||
|
|
@ -87,6 +91,7 @@ pub struct WindowAttributes {
|
|||
pub title: Option<WmName>,
|
||||
pub class: Option<String>,
|
||||
pub group: Option<x::Window>,
|
||||
pub decorations: Option<Decorations>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
|
||||
|
|
@ -244,6 +249,7 @@ struct ToplevelData {
|
|||
toplevel: XdgToplevel,
|
||||
xdg: XdgSurfaceData,
|
||||
fullscreen: bool,
|
||||
decoration: Option<ZxdgToplevelDecorationV1>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -509,6 +515,7 @@ pub struct ServerState<C: XConnection> {
|
|||
activation_state: Option<ActivationState>,
|
||||
global_output_offset: GlobalOutputOffset,
|
||||
global_offset_updated: bool,
|
||||
decoration_manager: Option<ZxdgDecorationManagerV1>,
|
||||
}
|
||||
|
||||
impl<C: XConnection> ServerState<C> {
|
||||
|
|
@ -542,6 +549,14 @@ impl<C: XConnection> ServerState<C> {
|
|||
})
|
||||
.ok();
|
||||
|
||||
let decoration_manager = clientside
|
||||
.global_list
|
||||
.bind::<ZxdgDecorationManagerV1, _, _>(&qh, 1..=1, ())
|
||||
.inspect_err(|e| {
|
||||
warn!("Could not bind xdg decoration ({e:?}). Windows might not have decorations.")
|
||||
})
|
||||
.ok();
|
||||
|
||||
dh.create_global::<Self, XwaylandShellV1, _>(1, ());
|
||||
clientside
|
||||
.global_list
|
||||
|
|
@ -578,6 +593,7 @@ impl<C: XConnection> ServerState<C> {
|
|||
},
|
||||
},
|
||||
global_offset_updated: false,
|
||||
decoration_manager,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -717,6 +733,35 @@ impl<C: XConnection> ServerState<C> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_win_decorations(&mut self, window: x::Window, decorations: Decorations) {
|
||||
if self.decoration_manager.is_none() {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(win) = self.windows.get_mut(&window) else {
|
||||
debug!("not setting decorations for unknown window {window:?}");
|
||||
return;
|
||||
};
|
||||
|
||||
if win.attrs.decorations != Some(decorations) {
|
||||
debug!("setting {window:?} decorations {decorations:?}");
|
||||
if let Some(key) = win.surface_key {
|
||||
if let Some(object) = self.objects.get(key) {
|
||||
let surface: &SurfaceData = object.as_ref();
|
||||
if let Some(SurfaceRole::Toplevel(Some(data))) = &surface.role {
|
||||
data.decoration
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_mode(decorations.into());
|
||||
}
|
||||
} else {
|
||||
warn!("could not set decorations on {window:?}: stale surface")
|
||||
}
|
||||
}
|
||||
win.attrs.decorations = Some(decorations);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_window_serial(&mut self, window: x::Window, serial: [u32; 2]) {
|
||||
let Some(win) = self.windows.get_mut(&window) else {
|
||||
warn!("Tried to set serial for unknown window {window:?}");
|
||||
|
|
@ -1224,6 +1269,17 @@ impl<C: XConnection> ServerState<C> {
|
|||
toplevel.set_fullscreen(None);
|
||||
}
|
||||
|
||||
let decoration = self.decoration_manager.as_ref().map(|decoration_manager| {
|
||||
let decoration = decoration_manager.get_toplevel_decoration(&toplevel, &self.qh, ());
|
||||
decoration.set_mode(
|
||||
window
|
||||
.attrs
|
||||
.decorations
|
||||
.map_or(zxdg_toplevel_decoration_v1::Mode::ServerSide, From::from),
|
||||
);
|
||||
decoration
|
||||
});
|
||||
|
||||
let surface: &SurfaceData = self.objects[surface_key].as_ref();
|
||||
if let (Some(activation_state), Some(token)) = (
|
||||
self.activation_state.as_ref(),
|
||||
|
|
@ -1240,6 +1296,7 @@ impl<C: XConnection> ServerState<C> {
|
|||
},
|
||||
toplevel,
|
||||
fullscreen: false,
|
||||
decoration,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
mod selection;
|
||||
use selection::{Selection, SelectionData};
|
||||
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
|
||||
|
||||
use crate::{server::WindowAttributes, XConnection};
|
||||
use bitflags::bitflags;
|
||||
|
|
@ -246,7 +247,11 @@ impl XState {
|
|||
|
||||
self.set_root_property(self.atoms.wm_check, x::ATOM_WINDOW, &[self.wm_window]);
|
||||
self.set_root_property(self.atoms.active_win, x::ATOM_WINDOW, &[x::Window::none()]);
|
||||
self.set_root_property(self.atoms.supported, x::ATOM_ATOM, &[self.atoms.active_win]);
|
||||
self.set_root_property(
|
||||
self.atoms.supported,
|
||||
x::ATOM_ATOM,
|
||||
&[self.atoms.active_win, self.atoms.motif_wm_hints],
|
||||
);
|
||||
|
||||
self.connection
|
||||
.send_and_check_request(&x::ChangeProperty {
|
||||
|
|
@ -491,6 +496,7 @@ impl XState {
|
|||
let class = self.get_wm_class(window);
|
||||
let wm_hints = self.get_wm_hints(window);
|
||||
let size_hints = self.get_wm_size_hints(window);
|
||||
let motif_wm_hints = self.get_motif_wm_hints(window);
|
||||
|
||||
let geometry = self.connection.wait_for_reply(geometry)?;
|
||||
debug!("{window:?} geometry: {geometry:?}");
|
||||
|
|
@ -503,6 +509,7 @@ impl XState {
|
|||
let class = class.resolve()?;
|
||||
let wm_hints = wm_hints.resolve()?;
|
||||
let size_hints = size_hints.resolve()?;
|
||||
let motif_wm_hints = motif_wm_hints.resolve()?;
|
||||
|
||||
Ok(WindowAttributes {
|
||||
override_redirect: attrs.override_redirect(),
|
||||
|
|
@ -517,6 +524,7 @@ impl XState {
|
|||
class,
|
||||
group: wm_hints.and_then(|h| h.window_group),
|
||||
size_hints,
|
||||
decorations: motif_wm_hints.and_then(|h| h.decorations),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -535,6 +543,9 @@ impl XState {
|
|||
if let Some(hints) = attrs.size_hints {
|
||||
server_state.set_size_hints(window, hints);
|
||||
}
|
||||
if let Some(decorations) = attrs.decorations {
|
||||
server_state.set_win_decorations(window, decorations);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_property_cookie(
|
||||
|
|
@ -660,6 +671,28 @@ impl XState {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_motif_wm_hints(
|
||||
&self,
|
||||
window: x::Window,
|
||||
) -> PropertyCookieWrapper<impl PropertyResolver<Output = MotifWmHints>> {
|
||||
let cookie = self.get_property_cookie(
|
||||
window,
|
||||
self.atoms.motif_wm_hints,
|
||||
self.atoms.motif_wm_hints,
|
||||
5,
|
||||
);
|
||||
let resolver = |reply: x::GetPropertyReply| {
|
||||
let data: &[u32] = reply.value();
|
||||
MotifWmHints::from(data)
|
||||
};
|
||||
|
||||
PropertyCookieWrapper {
|
||||
connection: &self.connection,
|
||||
cookie,
|
||||
resolver,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pid(&self, window: x::Window) -> Option<u32> {
|
||||
let Some(pid) = self
|
||||
.connection
|
||||
|
|
@ -718,6 +751,13 @@ impl XState {
|
|||
unwrap_or_skip_bad_window!(self.get_wm_class(window).resolve()).unwrap();
|
||||
server_state.set_win_class(window, class);
|
||||
}
|
||||
x if x == self.atoms.motif_wm_hints => {
|
||||
let motif_hints =
|
||||
unwrap_or_skip_bad_window!(self.get_motif_wm_hints(window).resolve()).unwrap();
|
||||
if let Some(decorations) = motif_hints.decorations {
|
||||
server_state.set_win_decorations(window, decorations);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if !self.handle_selection_property_change(&event)
|
||||
&& log::log_enabled!(log::Level::Debug)
|
||||
|
|
@ -750,6 +790,7 @@ xcb::atoms_struct! {
|
|||
active_win => b"_NET_ACTIVE_WINDOW" only_if_exists = false,
|
||||
client_list => b"_NET_CLIENT_LIST" only_if_exists = false,
|
||||
supported => b"_NET_SUPPORTED" only_if_exists = false,
|
||||
motif_wm_hints => b"_MOTIF_WM_HINTS" only_if_exists = false,
|
||||
utf8_string => b"UTF8_STRING" only_if_exists = false,
|
||||
clipboard => b"CLIPBOARD" only_if_exists = false,
|
||||
targets => b"TARGETS" only_if_exists = false,
|
||||
|
|
@ -795,6 +836,12 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct MotifWmHintsFlags: u32 {
|
||||
const Decorations = 2;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct WinSize {
|
||||
pub width: i32,
|
||||
|
|
@ -849,6 +896,52 @@ impl From<&[u32]> for WmHints {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Decorations {
|
||||
Client = 0,
|
||||
Server = 1,
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for Decorations {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, ()> {
|
||||
match value {
|
||||
0 => Ok(Self::Client),
|
||||
1 => Ok(Self::Server),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Decorations> for zxdg_toplevel_decoration_v1::Mode {
|
||||
fn from(value: Decorations) -> Self {
|
||||
match value {
|
||||
Decorations::Client => zxdg_toplevel_decoration_v1::Mode::ClientSide,
|
||||
Decorations::Server => zxdg_toplevel_decoration_v1::Mode::ServerSide,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
pub struct MotifWmHints {
|
||||
pub decorations: Option<Decorations>,
|
||||
}
|
||||
|
||||
impl From<&[u32]> for MotifWmHints {
|
||||
fn from(value: &[u32]) -> Self {
|
||||
let mut ret = Self::default();
|
||||
|
||||
let flags = MotifWmHintsFlags::from_bits_truncate(value[0]);
|
||||
|
||||
if flags.contains(MotifWmHintsFlags::Decorations) {
|
||||
ret.decorations = value[2].try_into().ok();
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum SetState {
|
||||
Remove,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue