Move Wayland backend to winit-wayland (#4252)

This commit is contained in:
Mads Marquart 2025-05-25 16:48:07 +02:00 committed by GitHub
parent 927af44aa4
commit 1126e9ea2f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 181 additions and 138 deletions

View file

@ -0,0 +1,67 @@
use cursor_icon::CursorIcon;
use sctk::reexports::client::protocol::wl_shm::Format;
use sctk::shm::slot::{Buffer, SlotPool};
use winit_core::cursor::{CursorImage, CustomCursorProvider};
// Wrap in our own type to not impl trait on global type.
#[derive(Debug)]
pub struct WaylandCustomCursor(pub(crate) CursorImage);
impl CustomCursorProvider for WaylandCustomCursor {
fn is_animated(&self) -> bool {
false
}
}
#[derive(Debug)]
pub enum SelectedCursor {
Named(CursorIcon),
Custom(CustomCursor),
}
impl Default for SelectedCursor {
fn default() -> Self {
Self::Named(Default::default())
}
}
#[derive(Debug)]
pub struct CustomCursor {
pub buffer: Buffer,
pub w: i32,
pub h: i32,
pub hotspot_x: i32,
pub hotspot_y: i32,
}
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();
}
CustomCursor {
buffer,
w: image.width() as i32,
h: image.height() as i32,
hotspot_x: image.hotspot_x() as i32,
hotspot_y: image.hotspot_y() as i32,
}
}
}

View file

@ -0,0 +1,67 @@
//! Handling of KDE-compatible blur.
use sctk::globals::GlobalData;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur_manager::OrgKdeKwinBlurManager;
use crate::state::WinitState;
/// KWin blur manager.
#[derive(Debug, Clone)]
pub struct KWinBlurManager {
manager: OrgKdeKwinBlurManager,
}
impl KWinBlurManager {
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { manager })
}
pub fn blur(
&self,
surface: &WlSurface,
queue_handle: &QueueHandle<WinitState>,
) -> OrgKdeKwinBlur {
self.manager.create(surface, queue_handle, ())
}
pub fn unset(&self, surface: &WlSurface) {
self.manager.unset(surface)
}
}
impl Dispatch<OrgKdeKwinBlurManager, GlobalData, WinitState> for KWinBlurManager {
fn event(
_: &mut WinitState,
_: &OrgKdeKwinBlurManager,
_: <OrgKdeKwinBlurManager as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
unreachable!("no events defined for org_kde_kwin_blur_manager");
}
}
impl Dispatch<OrgKdeKwinBlur, (), WinitState> for KWinBlurManager {
fn event(
_: &mut WinitState,
_: &OrgKdeKwinBlur,
_: <OrgKdeKwinBlur as Proxy>::Event,
_: &(),
_: &Connection,
_: &QueueHandle<WinitState>,
) {
unreachable!("no events defined for org_kde_kwin_blur");
}
}
delegate_dispatch!(WinitState: [OrgKdeKwinBlurManager: GlobalData] => KWinBlurManager);
delegate_dispatch!(WinitState: [OrgKdeKwinBlur: ()] => KWinBlurManager);

View file

@ -0,0 +1,7 @@
//! Wayland protocol implementation boilerplate.
pub mod cursor;
pub mod kwin_blur;
pub mod wp_fractional_scaling;
pub mod wp_viewporter;
pub mod xdg_activation;

View file

@ -0,0 +1,77 @@
//! Handling of the fractional scaling.
use sctk::globals::GlobalData;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1;
use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::{
Event as FractionalScalingEvent, WpFractionalScaleV1,
};
use crate::state::WinitState;
/// The scaling factor denominator.
const SCALE_DENOMINATOR: f64 = 120.;
/// Fractional scaling manager.
#[derive(Debug)]
pub struct FractionalScalingManager {
manager: WpFractionalScaleManagerV1,
}
pub struct FractionalScaling {
/// The surface used for scaling.
surface: WlSurface,
}
impl FractionalScalingManager {
/// Create new viewporter.
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { manager })
}
pub fn fractional_scaling(
&self,
surface: &WlSurface,
queue_handle: &QueueHandle<WinitState>,
) -> WpFractionalScaleV1 {
let data = FractionalScaling { surface: surface.clone() };
self.manager.get_fractional_scale(surface, queue_handle, data)
}
}
impl Dispatch<WpFractionalScaleManagerV1, GlobalData, WinitState> for FractionalScalingManager {
fn event(
_: &mut WinitState,
_: &WpFractionalScaleManagerV1,
_: <WpFractionalScaleManagerV1 as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
// No events.
}
}
impl Dispatch<WpFractionalScaleV1, FractionalScaling, WinitState> for FractionalScalingManager {
fn event(
state: &mut WinitState,
_: &WpFractionalScaleV1,
event: <WpFractionalScaleV1 as Proxy>::Event,
data: &FractionalScaling,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
if let FractionalScalingEvent::PreferredScale { scale } = event {
state.scale_factor_changed(&data.surface, scale as f64 / SCALE_DENOMINATOR, false);
}
}
}
delegate_dispatch!(WinitState: [WpFractionalScaleManagerV1: GlobalData] => FractionalScalingManager);
delegate_dispatch!(WinitState: [WpFractionalScaleV1: FractionalScaling] => FractionalScalingManager);

View file

@ -0,0 +1,64 @@
//! Handling of the wp-viewporter.
use sctk::globals::GlobalData;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
use sctk::reexports::protocols::wp::viewporter::client::wp_viewporter::WpViewporter;
use crate::state::WinitState;
/// Viewporter.
#[derive(Debug)]
pub struct ViewporterState {
viewporter: WpViewporter,
}
impl ViewporterState {
/// Create new viewporter.
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let viewporter = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { viewporter })
}
/// Get the viewport for the given object.
pub fn get_viewport(
&self,
surface: &WlSurface,
queue_handle: &QueueHandle<WinitState>,
) -> WpViewport {
self.viewporter.get_viewport(surface, queue_handle, GlobalData)
}
}
impl Dispatch<WpViewporter, GlobalData, WinitState> for ViewporterState {
fn event(
_: &mut WinitState,
_: &WpViewporter,
_: <WpViewporter as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
// No events.
}
}
impl Dispatch<WpViewport, GlobalData, WinitState> for ViewporterState {
fn event(
_: &mut WinitState,
_: &WpViewport,
_: <WpViewport as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
// No events.
}
}
delegate_dispatch!(WinitState: [WpViewporter: GlobalData] => ViewporterState);
delegate_dispatch!(WinitState: [WpViewport: GlobalData] => ViewporterState);

View file

@ -0,0 +1,102 @@
//! Handling of xdg activation, which is used for user attention requests.
use std::sync::atomic::AtomicBool;
use std::sync::Weak;
use sctk::globals::GlobalData;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_token_v1::{
Event as ActivationTokenEvent, XdgActivationTokenV1,
};
use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1;
use winit_core::event_loop::AsyncRequestSerial;
use winit_core::window::{ActivationToken, WindowId};
use crate::state::WinitState;
#[derive(Debug)]
pub struct XdgActivationState {
xdg_activation: XdgActivationV1,
}
impl XdgActivationState {
pub fn bind(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let xdg_activation = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { xdg_activation })
}
pub fn global(&self) -> &XdgActivationV1 {
&self.xdg_activation
}
}
impl Dispatch<XdgActivationV1, GlobalData, WinitState> for XdgActivationState {
fn event(
_state: &mut WinitState,
_proxy: &XdgActivationV1,
_event: <XdgActivationV1 as Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
}
}
impl Dispatch<XdgActivationTokenV1, XdgActivationTokenData, WinitState> for XdgActivationState {
fn event(
state: &mut WinitState,
proxy: &XdgActivationTokenV1,
event: <XdgActivationTokenV1 as Proxy>::Event,
data: &XdgActivationTokenData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
let token = match event {
ActivationTokenEvent::Done { token } => token,
_ => return,
};
let global = state
.xdg_activation
.as_ref()
.expect("got xdg_activation event without global.")
.global();
match data {
XdgActivationTokenData::Attention((surface, fence)) => {
global.activate(token, surface);
// Mark that no request attention is in process.
if let Some(attention_requested) = fence.upgrade() {
attention_requested.store(false, std::sync::atomic::Ordering::Relaxed);
}
},
XdgActivationTokenData::Obtain((window_id, serial)) => {
state.events_sink.push_window_event(
winit_core::event::WindowEvent::ActivationTokenDone {
serial: *serial,
token: ActivationToken::from_raw(token),
},
*window_id,
);
},
}
proxy.destroy();
}
}
/// The data associated with the activation request.
pub enum XdgActivationTokenData {
/// Request user attention for the given surface.
Attention((WlSurface, Weak<AtomicBool>)),
/// Get a token to be passed outside of the winit.
Obtain((WindowId, AsyncRequestSerial)),
}
delegate_dispatch!(WinitState: [ XdgActivationV1: GlobalData] => XdgActivationState);
delegate_dispatch!(WinitState: [ XdgActivationTokenV1: XdgActivationTokenData] => XdgActivationState);