wayland: add xdg_toplevel_icon_v1 support

Closes: #3859.
This commit is contained in:
Enn3Developer
2025-06-22 12:40:05 +02:00
committed by GitHub
parent 552c7a6252
commit fe1eab07ae
9 changed files with 227 additions and 32 deletions

View File

@@ -3,6 +3,8 @@ use sctk::reexports::client::protocol::wl_shm::Format;
use sctk::shm::slot::{Buffer, SlotPool};
use winit_core::cursor::{CursorImage, CustomCursorProvider};
use crate::image_to_buffer;
// Wrap in our own type to not impl trait on global type.
#[derive(Debug)]
pub struct WaylandCustomCursor(pub(crate) CursorImage);
@@ -36,25 +38,14 @@ pub struct CustomCursor {
impl CustomCursor {
pub(crate) fn new(pool: &mut SlotPool, image: &WaylandCustomCursor) -> Self {
let image = &image.0;
let (buffer, canvas) = pool
.create_buffer(
image.width() as i32,
image.height() as i32,
4 * (image.width() as i32),
Format::Argb8888,
)
.unwrap();
for (canvas_chunk, rgba) in canvas.chunks_exact_mut(4).zip(image.buffer().chunks_exact(4)) {
// Alpha in buffer is premultiplied.
let alpha = rgba[3] as f32 / 255.;
let r = (rgba[0] as f32 * alpha) as u32;
let g = (rgba[1] as f32 * alpha) as u32;
let b = (rgba[2] as f32 * alpha) as u32;
let color = ((rgba[3] as u32) << 24) + (r << 16) + (g << 8) + b;
let array: &mut [u8; 4] = canvas_chunk.try_into().unwrap();
*array = color.to_le_bytes();
}
let buffer = image_to_buffer(
image.width() as i32,
image.height() as i32,
image.buffer(),
Format::Argb8888,
pool,
)
.unwrap();
CustomCursor {
buffer,

View File

@@ -5,3 +5,4 @@ pub mod kwin_blur;
pub mod wp_fractional_scaling;
pub mod wp_viewporter;
pub mod xdg_activation;
pub mod xdg_toplevel_icon_manager;

View File

@@ -0,0 +1,108 @@
//! Handling of xdg toplevel icon manager, which is used for icon setting requests.
use std::fmt;
use std::fmt::Formatter;
use sctk::globals::GlobalData;
use sctk::shm::slot::{Buffer, SlotPool};
use wayland_client::globals::{BindError, GlobalList};
use wayland_client::protocol::wl_shm::Format;
use wayland_client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use wayland_protocols::xdg::toplevel_icon::v1::client::xdg_toplevel_icon_manager_v1::XdgToplevelIconManagerV1;
use wayland_protocols::xdg::toplevel_icon::v1::client::xdg_toplevel_icon_v1::XdgToplevelIconV1;
use winit_core::icon::{Icon, RgbaIcon};
use crate::image_to_buffer;
use crate::state::WinitState;
#[derive(Debug)]
pub struct XdgToplevelIconManagerState {
xdg_toplevel_icon_manager: XdgToplevelIconManagerV1,
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum ToplevelIconError {
/// The icon's unsupported
Unsupported,
}
#[derive(Debug)]
pub struct ToplevelIcon {
buffer: Buffer,
}
impl ToplevelIcon {
pub fn new(icon: Icon, pool: &mut SlotPool) -> Result<Self, ToplevelIconError> {
let icon = match icon.cast_ref::<RgbaIcon>() {
Some(icon) => icon,
None => return Err(ToplevelIconError::Unsupported),
};
let buffer = image_to_buffer(
icon.width() as i32,
icon.height() as i32,
icon.buffer(),
Format::Argb8888,
pool,
)
.unwrap();
Ok(Self { buffer })
}
pub fn add_buffer(&self, xdg_toplevel_icon: &XdgToplevelIconV1) {
xdg_toplevel_icon.add_buffer(self.buffer.wl_buffer(), 1);
}
}
impl fmt::Display for ToplevelIconError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ToplevelIconError::Unsupported => write!(f, "this icon is unsupported on Wayland"),
}
}
}
impl XdgToplevelIconManagerState {
pub fn bind(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let xdg_toplevel_icon_manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { xdg_toplevel_icon_manager })
}
pub fn global(&self) -> &XdgToplevelIconManagerV1 {
&self.xdg_toplevel_icon_manager
}
}
impl Dispatch<XdgToplevelIconManagerV1, GlobalData, WinitState> for XdgToplevelIconManagerState {
fn event(
_state: &mut WinitState,
_proxy: &XdgToplevelIconManagerV1,
_event: <XdgToplevelIconManagerV1 as Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
// No events.
}
}
impl Dispatch<XdgToplevelIconV1, GlobalData, WinitState> for XdgToplevelIconManagerState {
fn event(
_state: &mut WinitState,
_proxy: &XdgToplevelIconV1,
_event: <XdgToplevelIconV1 as Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
// No events.
}
}
delegate_dispatch!(WinitState: [XdgToplevelIconManagerV1: GlobalData] => XdgToplevelIconManagerState);
delegate_dispatch!(WinitState: [XdgToplevelIconV1: GlobalData] => XdgToplevelIconManagerState);