feat(wayland): corner-radius protocol support
This commit is contained in:
parent
54a69a0523
commit
e73bbddbca
10 changed files with 252 additions and 62 deletions
|
|
@ -278,7 +278,7 @@ tiny-skia = { version = "0.11", default-features = false, features = [
|
|||
"std",
|
||||
"simd",
|
||||
] }
|
||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "178eb0b" }
|
||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "160b086" }
|
||||
softbuffer = { git = "https://github.com/pop-os/softbuffer", tag = "cosmic-4.0" }
|
||||
syntect = "5.2"
|
||||
tokio = "1.0"
|
||||
|
|
|
|||
|
|
@ -33,6 +33,16 @@ pub enum Action {
|
|||
Subsurface(subsurface::Action),
|
||||
/// Keyboard inhibit shortcuts
|
||||
InhibitShortcuts(bool),
|
||||
/// Rounded corners in logical space
|
||||
RoundedCorners(iced_core::window::Id, Option<CornerRadius>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct CornerRadius {
|
||||
pub top_left: u32,
|
||||
pub top_right: u32,
|
||||
pub bottom_left: u32,
|
||||
pub bottom_right: u32,
|
||||
}
|
||||
|
||||
impl Debug for Action {
|
||||
|
|
@ -57,6 +67,9 @@ impl Debug for Action {
|
|||
Action::InhibitShortcuts(v) => {
|
||||
f.debug_tuple("InhibitShortcuts").field(v).finish()
|
||||
}
|
||||
Action::RoundedCorners(id, v) => {
|
||||
f.debug_tuple("RoundedCorners").field(id).field(v).finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
use iced_futures::core::{border::Radius, window};
|
||||
use iced_runtime::{
|
||||
self,
|
||||
platform_specific::{
|
||||
self,
|
||||
wayland::{self, CornerRadius},
|
||||
},
|
||||
task, Action, Task,
|
||||
};
|
||||
|
||||
pub fn corner_radius(
|
||||
id: window::Id,
|
||||
corners: Option<CornerRadius>,
|
||||
) -> Task<()> {
|
||||
task::oneshot(|_| {
|
||||
Action::PlatformSpecific(platform_specific::Action::Wayland(
|
||||
wayland::Action::RoundedCorners(id, corners),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
//! Interact with the wayland objects of your application.
|
||||
|
||||
pub mod activation;
|
||||
pub mod corner_radius;
|
||||
pub mod keyboard_shortcuts_inhibit;
|
||||
pub mod layer_surface;
|
||||
pub mod overlap_notify;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ use crate::{
|
|||
subsurface_widget::SubsurfaceState,
|
||||
};
|
||||
|
||||
use cctk::sctk::reexports::calloop_wayland_source::WaylandSource;
|
||||
use cctk::{
|
||||
cosmic_protocols::corner_radius::v1::client::cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, sctk::reexports::calloop_wayland_source::WaylandSource, toplevel_info::ToplevelInfoState
|
||||
};
|
||||
use cctk::{
|
||||
sctk::{
|
||||
activation::ActivationState,
|
||||
|
|
@ -37,7 +39,6 @@ use cctk::{
|
|||
shell::{WaylandSurface, wlr_layer::LayerShell, xdg::XdgShell},
|
||||
shm::Shm,
|
||||
},
|
||||
toplevel_info::ToplevelInfoState,
|
||||
toplevel_management::ToplevelManagerState,
|
||||
};
|
||||
use raw_window_handle::HasDisplayHandle;
|
||||
|
|
@ -126,7 +127,10 @@ impl SctkEventLoop {
|
|||
window,
|
||||
id,
|
||||
) => {
|
||||
state.windows.push(SctkWindow { window, id });
|
||||
state.windows.push(SctkWindow { window, id, corner_radius: Default::default() });
|
||||
if let Some(v) = state.pending_corner_radius.remove(&id) {
|
||||
_ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, Some(v)));
|
||||
}
|
||||
}
|
||||
crate::platform_specific::Action::RemoveWindow(
|
||||
id,
|
||||
|
|
@ -201,7 +205,7 @@ impl SctkEventLoop {
|
|||
let half_h = size.height / 2.;
|
||||
match settings.gravity {
|
||||
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::None => {
|
||||
// center on
|
||||
// center on
|
||||
loc.x -= half_w;
|
||||
loc.y -= half_h;
|
||||
},
|
||||
|
|
@ -306,6 +310,11 @@ impl SctkEventLoop {
|
|||
®istry_state,
|
||||
&qh,
|
||||
),
|
||||
corner_radius_manager: registry_state.bind_one::<CosmicCornerRadiusManagerV1, _, _>(
|
||||
&qh,
|
||||
1..=1,
|
||||
(),
|
||||
).ok(),
|
||||
toplevel_manager: ToplevelManagerState::try_new(
|
||||
®istry_state,
|
||||
&qh,
|
||||
|
|
@ -348,6 +357,7 @@ impl SctkEventLoop {
|
|||
token_senders: HashMap::new(),
|
||||
overlap_notifications: HashMap::new(),
|
||||
subsurface_state: None,
|
||||
pending_corner_radius: HashMap::new(),
|
||||
},
|
||||
_features: Default::default(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
use crate::{
|
||||
Control,
|
||||
sctk_event::KeyboardEventVariant,
|
||||
subsurface_widget::SubsurfaceState,
|
||||
wayland::SubsurfaceInstance,
|
||||
handlers::{
|
||||
activation::IcedRequestData,
|
||||
overlap::{OverlapNotificationV1, OverlapNotifyV1},
|
||||
|
|
@ -17,6 +14,9 @@ use crate::{
|
|||
sctk_event::{LayerSurfaceEventVariant, SctkEvent},
|
||||
},
|
||||
},
|
||||
sctk_event::KeyboardEventVariant,
|
||||
subsurface_widget::SubsurfaceState,
|
||||
wayland::SubsurfaceInstance,
|
||||
};
|
||||
use iced_futures::{
|
||||
core::{Rectangle, Size},
|
||||
|
|
@ -27,7 +27,7 @@ use std::{
|
|||
collections::{HashMap, HashSet},
|
||||
convert::Infallible,
|
||||
fmt::Debug,
|
||||
sync::{atomic::AtomicU32, Arc, Mutex},
|
||||
sync::{Arc, Mutex, atomic::AtomicU32},
|
||||
thread::panicking,
|
||||
time::Duration,
|
||||
};
|
||||
|
|
@ -37,66 +37,85 @@ use winit::{
|
|||
platform::wayland::WindowExtWayland,
|
||||
};
|
||||
|
||||
use cctk::{
|
||||
cosmic_protocols::{
|
||||
corner_radius::v1::client::{
|
||||
cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
|
||||
cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
||||
},
|
||||
overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1,
|
||||
},
|
||||
sctk::{
|
||||
activation::{ActivationState, RequestData},
|
||||
compositor::CompositorState,
|
||||
error::GlobalError,
|
||||
globals::GlobalData,
|
||||
output::OutputState,
|
||||
reexports::{
|
||||
calloop::{LoopHandle, timer::TimeoutAction},
|
||||
client::{
|
||||
Connection, Proxy, QueueHandle, delegate_noop,
|
||||
protocol::{
|
||||
wl_keyboard::WlKeyboard,
|
||||
wl_output::WlOutput,
|
||||
wl_region::WlRegion,
|
||||
wl_seat::WlSeat,
|
||||
wl_subsurface::WlSubsurface,
|
||||
wl_surface::{self, WlSurface},
|
||||
wl_touch::WlTouch,
|
||||
},
|
||||
},
|
||||
},
|
||||
registry::RegistryState,
|
||||
seat::{
|
||||
SeatState,
|
||||
keyboard::KeyEvent,
|
||||
pointer::{CursorIcon, PointerData, ThemedPointer},
|
||||
touch::TouchData,
|
||||
},
|
||||
session_lock::{
|
||||
SessionLock, SessionLockState, SessionLockSurface,
|
||||
SessionLockSurfaceConfigure,
|
||||
},
|
||||
shell::{
|
||||
WaylandSurface,
|
||||
wlr_layer::{
|
||||
Anchor, KeyboardInteractivity, Layer, LayerShell, LayerSurface,
|
||||
LayerSurfaceConfigure, SurfaceKind,
|
||||
},
|
||||
xdg::{
|
||||
XdgPositioner, XdgShell,
|
||||
popup::{Popup, PopupConfigure},
|
||||
},
|
||||
},
|
||||
shm::{Shm, multi::MultiPool},
|
||||
},
|
||||
toplevel_info::ToplevelInfoState,
|
||||
toplevel_management::ToplevelManagerState,
|
||||
};
|
||||
use iced_runtime::{
|
||||
core::{self, Point, touch},
|
||||
keyboard::Modifiers,
|
||||
platform_specific::{
|
||||
self,
|
||||
wayland::{
|
||||
layer_surface::{IcedMargin, IcedOutput, SctkLayerSurfaceSettings}, popup::SctkPopupSettings, subsurface::{self, SctkSubsurfaceSettings}, Action
|
||||
Action, CornerRadius,
|
||||
layer_surface::{IcedMargin, IcedOutput, SctkLayerSurfaceSettings},
|
||||
popup::SctkPopupSettings,
|
||||
subsurface::{self, SctkSubsurfaceSettings},
|
||||
},
|
||||
},
|
||||
};
|
||||
use cctk::{cosmic_protocols::overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1, sctk::{
|
||||
activation::{ActivationState, RequestData},
|
||||
compositor::CompositorState,
|
||||
error::GlobalError,
|
||||
globals::GlobalData,
|
||||
output::OutputState,
|
||||
reexports::{
|
||||
calloop::{LoopHandle, timer::TimeoutAction},
|
||||
client::{
|
||||
Connection, Proxy, QueueHandle, delegate_noop,
|
||||
protocol::{
|
||||
wl_keyboard::WlKeyboard,
|
||||
wl_output::WlOutput,
|
||||
wl_region::WlRegion,
|
||||
wl_seat::WlSeat,
|
||||
wl_subsurface::WlSubsurface,
|
||||
wl_surface::{self, WlSurface},
|
||||
wl_touch::WlTouch,
|
||||
},
|
||||
},
|
||||
},
|
||||
registry::RegistryState,
|
||||
seat::{
|
||||
SeatState,
|
||||
keyboard::KeyEvent,
|
||||
pointer::{CursorIcon, PointerData, ThemedPointer},
|
||||
touch::TouchData,
|
||||
},
|
||||
session_lock::{
|
||||
SessionLock, SessionLockState, SessionLockSurface,
|
||||
SessionLockSurfaceConfigure,
|
||||
},
|
||||
shell::{
|
||||
WaylandSurface,
|
||||
wlr_layer::{
|
||||
Anchor, KeyboardInteractivity, Layer, LayerShell, LayerSurface,
|
||||
LayerSurfaceConfigure, SurfaceKind,
|
||||
},
|
||||
xdg::{
|
||||
XdgPositioner, XdgShell,
|
||||
popup::{Popup, PopupConfigure},
|
||||
},
|
||||
},
|
||||
shm::{multi::MultiPool, Shm},
|
||||
}, toplevel_info::ToplevelInfoState, toplevel_management::ToplevelManagerState};
|
||||
use wayland_protocols::{
|
||||
wp::{
|
||||
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1, keyboard_shortcuts_inhibit::zv1::client::{zwp_keyboard_shortcuts_inhibit_manager_v1, zwp_keyboard_shortcuts_inhibitor_v1}, viewporter::client::wp_viewport::WpViewport
|
||||
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||
keyboard_shortcuts_inhibit::zv1::client::{
|
||||
zwp_keyboard_shortcuts_inhibit_manager_v1,
|
||||
zwp_keyboard_shortcuts_inhibitor_v1,
|
||||
},
|
||||
viewporter::client::wp_viewport::WpViewport,
|
||||
},
|
||||
xdg::shell::client::xdg_surface::XdgSurface,
|
||||
xdg::shell::client::{xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel},
|
||||
};
|
||||
|
||||
pub static TOKEN_CTR: AtomicU32 = AtomicU32::new(0);
|
||||
|
|
@ -311,9 +330,22 @@ pub struct SctkPopupData {
|
|||
pub(crate) grab: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MyCosmicCornerRadiusToplevelV1(CosmicCornerRadiusToplevelV1);
|
||||
|
||||
impl Drop for MyCosmicCornerRadiusToplevelV1 {
|
||||
fn drop(&mut self) {
|
||||
self.0.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SctkCornerRadius(Arc<MyCosmicCornerRadiusToplevelV1>);
|
||||
|
||||
pub struct SctkWindow {
|
||||
pub(crate) window: Arc<dyn winit::window::Window>,
|
||||
pub(crate) id: core::window::Id,
|
||||
pub(crate) corner_radius: Option<(SctkCornerRadius, CornerRadius)>,
|
||||
}
|
||||
|
||||
impl SctkWindow {
|
||||
|
|
@ -348,6 +380,19 @@ impl SctkWindow {
|
|||
.unwrap();
|
||||
XdgSurface::from_id(conn, id).unwrap()
|
||||
}
|
||||
|
||||
pub fn xdg_toplevel(&self, conn: &Connection) -> XdgToplevel {
|
||||
let window_handle = self.window.xdg_toplevel().unwrap();
|
||||
|
||||
let id = unsafe {
|
||||
ObjectId::from_ptr(
|
||||
XdgToplevel::interface(),
|
||||
window_handle.as_ptr().cast(),
|
||||
)
|
||||
}
|
||||
.unwrap();
|
||||
XdgToplevel::from_id(conn, id).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum FrameStatus {
|
||||
|
|
@ -436,6 +481,9 @@ pub struct SctkState {
|
|||
pub(crate) inhibitor: Option<zwp_keyboard_shortcuts_inhibitor_v1::ZwpKeyboardShortcutsInhibitorV1>,
|
||||
pub(crate) inhibited: bool,
|
||||
pub(crate) inhibitor_manager: Option<zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1>,
|
||||
|
||||
pub(crate) corner_radius_manager: Option<CosmicCornerRadiusManagerV1>,
|
||||
pub(crate) pending_corner_radius: HashMap<core::window::Id, CornerRadius>
|
||||
}
|
||||
|
||||
/// An error that occurred while running an application.
|
||||
|
|
@ -722,6 +770,7 @@ impl SctkState {
|
|||
else {
|
||||
return Err(PopupCreationError::ParentMissing);
|
||||
};
|
||||
|
||||
(
|
||||
&parent_window.wl_surface(&self.connection),
|
||||
Popup::from_surface(
|
||||
|
|
@ -1468,7 +1517,7 @@ impl SctkState {
|
|||
Action::InhibitShortcuts(v) => {
|
||||
if let Some(manager) = self.inhibitor_manager.as_ref() {
|
||||
if let Some(inhibit) = self.inhibitor.take() {
|
||||
inhibit.destroy();
|
||||
inhibit.destroy();
|
||||
}
|
||||
if v {
|
||||
self.inhibitor = self.seats.iter().next()
|
||||
|
|
@ -1476,6 +1525,45 @@ impl SctkState {
|
|||
}
|
||||
}
|
||||
}
|
||||
Action::RoundedCorners(id, v) => {
|
||||
if let Some(manager) = self.corner_radius_manager.as_ref() {
|
||||
if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) {
|
||||
if let Some(radii) = v {
|
||||
if let Some((protocol_object, corner_radii)) = w.corner_radius.as_mut() {
|
||||
if *corner_radii != radii {
|
||||
protocol_object.0.0.set_radius(radii.top_left,
|
||||
radii.top_right,
|
||||
radii.bottom_right,
|
||||
radii.bottom_left,);
|
||||
*corner_radii = radii.clone();
|
||||
}
|
||||
} else {
|
||||
let toplevel = w.xdg_toplevel(&self.connection);
|
||||
|
||||
let protocol_object = manager.get_corner_radius(&toplevel, &self.queue_handle, ());
|
||||
protocol_object.set_radius(
|
||||
radii.top_left,
|
||||
radii.top_right,
|
||||
radii.bottom_right,
|
||||
radii.bottom_left,
|
||||
);
|
||||
w.corner_radius = Some((SctkCornerRadius(Arc::new(MyCosmicCornerRadiusToplevelV1( protocol_object))), radii.clone()));
|
||||
}
|
||||
} else {
|
||||
if let Some(old) = w.corner_radius.take() {
|
||||
old.0.0.as_ref().0.unset_radius();
|
||||
}
|
||||
w.corner_radius = None;
|
||||
}
|
||||
} else {
|
||||
if let Some(v) = v{
|
||||
_ = self.pending_corner_radius.insert(id, v);
|
||||
} else {
|
||||
_ = self.pending_corner_radius.remove(&id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ pub mod toplevel;
|
|||
pub mod wp_fractional_scaling;
|
||||
pub mod wp_viewporter;
|
||||
|
||||
use cctk::sctk::{
|
||||
use cctk::{sctk::{
|
||||
delegate_registry, delegate_shm,
|
||||
output::OutputState,
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
registry_handlers,
|
||||
seat::SeatState,
|
||||
shm::{Shm, ShmHandler},
|
||||
};
|
||||
}};
|
||||
|
||||
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ use crate::platform_specific::wayland::{
|
|||
event_loop::state::SctkState,
|
||||
sctk_event::{KeyboardEventVariant, SctkEvent},
|
||||
};
|
||||
use cctk::sctk::reexports::client::Proxy;
|
||||
use cctk::sctk::{
|
||||
delegate_keyboard,
|
||||
seat::keyboard::{KeyboardHandler, Keysym},
|
||||
};
|
||||
use cctk::sctk::{reexports::client::Proxy, seat::keyboard::RawModifiers};
|
||||
|
||||
impl KeyboardHandler for SctkState {
|
||||
fn enter(
|
||||
|
|
@ -232,7 +232,8 @@ impl KeyboardHandler for SctkState {
|
|||
keyboard: &cctk::sctk::reexports::client::protocol::wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
modifiers: cctk::sctk::seat::keyboard::Modifiers,
|
||||
layout: u32,
|
||||
_raw_modifiers: RawModifiers,
|
||||
_layout: u32,
|
||||
) {
|
||||
let (is_active, my_seat) =
|
||||
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
||||
|
|
@ -268,6 +269,17 @@ impl KeyboardHandler for SctkState {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn repeat_key(
|
||||
&mut self,
|
||||
_conn: &wayland_client::Connection,
|
||||
_qh: &wayland_client::QueueHandle<Self>,
|
||||
_keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
_event: cctk::sctk::seat::keyboard::KeyEvent,
|
||||
) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
delegate_keyboard!(SctkState);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
use cctk::{sctk, cosmic_protocols::{
|
||||
corner_radius::v1::client::{
|
||||
cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
|
||||
cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
||||
},
|
||||
overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1,
|
||||
}};
|
||||
use sctk::reexports::{
|
||||
client::{Connection, Dispatch, Proxy},
|
||||
|
||||
};
|
||||
|
||||
use crate::event_loop::state::SctkState;
|
||||
use crate::platform_specific::wayland::SctkEvent;
|
||||
|
||||
impl Dispatch<CosmicCornerRadiusManagerV1, ()> for SctkState {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &CosmicCornerRadiusManagerV1,
|
||||
_event: <CosmicCornerRadiusManagerV1 as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
|
||||
) {}
|
||||
}
|
||||
|
||||
impl
|
||||
Dispatch<
|
||||
CosmicCornerRadiusToplevelV1,
|
||||
(),
|
||||
> for SctkState
|
||||
{
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_proxy: &CosmicCornerRadiusToplevelV1,
|
||||
event: <CosmicCornerRadiusToplevelV1 as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
|
||||
) {
|
||||
match event {
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod layer;
|
||||
pub mod xdg_popup;
|
||||
pub mod xdg_window;
|
||||
pub mod corner_radius;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue