Support xdg decorations

This commit is contained in:
bbb651 2025-03-20 15:49:35 +02:00 committed by Shawn Wallace
parent b2613aec05
commit 0559ace758
5 changed files with 328 additions and 4 deletions

View file

@ -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,