feat(wayland): shortcut inhibit

This commit is contained in:
Ashley Wulber 2025-09-09 19:50:35 -04:00
parent 8e7b7e586f
commit 54a69a0523
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
11 changed files with 360 additions and 8 deletions

View file

@ -44,4 +44,6 @@ pub enum Event {
RequestResize,
/// Subsurface
Subsurface(SubsurfaceEvent),
/// Keyboard inhibit shortcuts
ShortcutsInhibited(bool),
}

View file

@ -31,6 +31,8 @@ pub enum Action {
OverlapNotify(Id, bool),
/// Subsurfaces
Subsurface(subsurface::Action),
/// Keyboard inhibit shortcuts
InhibitShortcuts(bool),
}
impl Debug for Action {
@ -52,6 +54,9 @@ impl Debug for Action {
Action::Subsurface(action) => {
f.debug_tuple("Subsurface").field(action).finish()
}
Action::InhibitShortcuts(v) => {
f.debug_tuple("InhibitShortcuts").field(v).finish()
}
}
}
}

View file

@ -430,9 +430,9 @@ pub fn window_event(
Size::new(size.width, size.height)
});
Some(Event::PlatformSpecific(PlatformSpecific::Wayland(
Some(Event::PlatformSpecific(iced_futures::core::event::PlatformSpecific::Wayland(
iced_runtime::core::event::wayland::Event::Window(
iced_runtime::core::event::wayland::WindowEvent::SuggestedBounds(size),
iced_runtime::core::event::wayland::WindowEvent::SuggestedBounds(size)
),
)))
}
@ -1273,6 +1273,254 @@ pub fn key_code(
})
}
pub fn winit_key_code(
key_code: keyboard::key::Code,
) -> Option<winit::keyboard::KeyCode> {
use winit::keyboard::KeyCode;
Some(match key_code {
keyboard::key::Code::Backquote => KeyCode::Backquote,
keyboard::key::Code::Backslash => KeyCode::Backslash,
keyboard::key::Code::BracketLeft => KeyCode::BracketLeft,
keyboard::key::Code::BracketRight => KeyCode::BracketRight,
keyboard::key::Code::Comma => KeyCode::Comma,
keyboard::key::Code::Digit0 => KeyCode::Digit0,
keyboard::key::Code::Digit1 => KeyCode::Digit1,
keyboard::key::Code::Digit2 => KeyCode::Digit2,
keyboard::key::Code::Digit3 => KeyCode::Digit3,
keyboard::key::Code::Digit4 => KeyCode::Digit4,
keyboard::key::Code::Digit5 => KeyCode::Digit5,
keyboard::key::Code::Digit6 => KeyCode::Digit6,
keyboard::key::Code::Digit7 => KeyCode::Digit7,
keyboard::key::Code::Digit8 => KeyCode::Digit8,
keyboard::key::Code::Digit9 => KeyCode::Digit9,
keyboard::key::Code::Equal => KeyCode::Equal,
keyboard::key::Code::IntlBackslash => KeyCode::IntlBackslash,
keyboard::key::Code::IntlRo => KeyCode::IntlRo,
keyboard::key::Code::IntlYen => KeyCode::IntlYen,
keyboard::key::Code::KeyA => KeyCode::KeyA,
keyboard::key::Code::KeyB => KeyCode::KeyB,
keyboard::key::Code::KeyC => KeyCode::KeyC,
keyboard::key::Code::KeyD => KeyCode::KeyD,
keyboard::key::Code::KeyE => KeyCode::KeyE,
keyboard::key::Code::KeyF => KeyCode::KeyF,
keyboard::key::Code::KeyG => KeyCode::KeyG,
keyboard::key::Code::KeyH => KeyCode::KeyH,
keyboard::key::Code::KeyI => KeyCode::KeyI,
keyboard::key::Code::KeyJ => KeyCode::KeyJ,
keyboard::key::Code::KeyK => KeyCode::KeyK,
keyboard::key::Code::KeyL => KeyCode::KeyL,
keyboard::key::Code::KeyM => KeyCode::KeyM,
keyboard::key::Code::KeyN => KeyCode::KeyN,
keyboard::key::Code::KeyO => KeyCode::KeyO,
keyboard::key::Code::KeyP => KeyCode::KeyP,
keyboard::key::Code::KeyQ => KeyCode::KeyQ,
keyboard::key::Code::KeyR => KeyCode::KeyR,
keyboard::key::Code::KeyS => KeyCode::KeyS,
keyboard::key::Code::KeyT => KeyCode::KeyT,
keyboard::key::Code::KeyU => KeyCode::KeyU,
keyboard::key::Code::KeyV => KeyCode::KeyV,
keyboard::key::Code::KeyW => KeyCode::KeyW,
keyboard::key::Code::KeyX => KeyCode::KeyX,
keyboard::key::Code::KeyY => KeyCode::KeyY,
keyboard::key::Code::KeyZ => KeyCode::KeyZ,
keyboard::key::Code::Minus => KeyCode::Minus,
keyboard::key::Code::Period => KeyCode::Period,
keyboard::key::Code::Quote => KeyCode::Quote,
keyboard::key::Code::Semicolon => KeyCode::Semicolon,
keyboard::key::Code::Slash => KeyCode::Slash,
keyboard::key::Code::AltLeft => KeyCode::AltLeft,
keyboard::key::Code::AltRight => KeyCode::AltRight,
keyboard::key::Code::Backspace => KeyCode::Backspace,
keyboard::key::Code::CapsLock => KeyCode::CapsLock,
keyboard::key::Code::ContextMenu => KeyCode::ContextMenu,
keyboard::key::Code::ControlLeft => KeyCode::ControlLeft,
keyboard::key::Code::ControlRight => KeyCode::ControlRight,
keyboard::key::Code::Enter => KeyCode::Enter,
keyboard::key::Code::SuperLeft => KeyCode::MetaLeft,
keyboard::key::Code::SuperRight => KeyCode::MetaRight,
keyboard::key::Code::ShiftLeft => KeyCode::ShiftLeft,
keyboard::key::Code::ShiftRight => KeyCode::ShiftRight,
keyboard::key::Code::Space => KeyCode::Space,
keyboard::key::Code::Tab => KeyCode::Tab,
keyboard::key::Code::Convert => KeyCode::Convert,
keyboard::key::Code::KanaMode => KeyCode::KanaMode,
keyboard::key::Code::Lang1 => KeyCode::Lang1,
keyboard::key::Code::Lang2 => KeyCode::Lang2,
keyboard::key::Code::Lang3 => KeyCode::Lang3,
keyboard::key::Code::Lang4 => KeyCode::Lang4,
keyboard::key::Code::Lang5 => KeyCode::Lang5,
keyboard::key::Code::NonConvert => KeyCode::NonConvert,
keyboard::key::Code::Delete => KeyCode::Delete,
keyboard::key::Code::End => KeyCode::End,
keyboard::key::Code::Help => KeyCode::Help,
keyboard::key::Code::Home => KeyCode::Home,
keyboard::key::Code::Insert => KeyCode::Insert,
keyboard::key::Code::PageDown => KeyCode::PageDown,
keyboard::key::Code::PageUp => KeyCode::PageUp,
keyboard::key::Code::ArrowDown => KeyCode::ArrowDown,
keyboard::key::Code::ArrowLeft => KeyCode::ArrowLeft,
keyboard::key::Code::ArrowRight => KeyCode::ArrowRight,
keyboard::key::Code::ArrowUp => KeyCode::ArrowUp,
keyboard::key::Code::NumLock => KeyCode::NumLock,
keyboard::key::Code::Numpad0 => KeyCode::Numpad0,
keyboard::key::Code::Numpad1 => KeyCode::Numpad1,
keyboard::key::Code::Numpad2 => KeyCode::Numpad2,
keyboard::key::Code::Numpad3 => KeyCode::Numpad3,
keyboard::key::Code::Numpad4 => KeyCode::Numpad4,
keyboard::key::Code::Numpad5 => KeyCode::Numpad5,
keyboard::key::Code::Numpad6 => KeyCode::Numpad6,
keyboard::key::Code::Numpad7 => KeyCode::Numpad7,
keyboard::key::Code::Numpad8 => KeyCode::Numpad8,
keyboard::key::Code::Numpad9 => KeyCode::Numpad9,
keyboard::key::Code::NumpadAdd => KeyCode::NumpadAdd,
keyboard::key::Code::NumpadBackspace => KeyCode::NumpadBackspace,
keyboard::key::Code::NumpadClear => KeyCode::NumpadClear,
keyboard::key::Code::NumpadClearEntry => KeyCode::NumpadClearEntry,
keyboard::key::Code::NumpadComma => KeyCode::NumpadComma,
keyboard::key::Code::NumpadDecimal => KeyCode::NumpadDecimal,
keyboard::key::Code::NumpadDivide => KeyCode::NumpadDivide,
keyboard::key::Code::NumpadEnter => KeyCode::NumpadEnter,
keyboard::key::Code::NumpadEqual => KeyCode::NumpadEqual,
keyboard::key::Code::NumpadHash => KeyCode::NumpadHash,
keyboard::key::Code::NumpadMemoryAdd => KeyCode::NumpadMemoryAdd,
keyboard::key::Code::NumpadMemoryClear => KeyCode::NumpadMemoryClear,
keyboard::key::Code::NumpadMemoryRecall => KeyCode::NumpadMemoryRecall,
keyboard::key::Code::NumpadMemoryStore => KeyCode::NumpadMemoryStore,
keyboard::key::Code::NumpadMemorySubtract => {
KeyCode::NumpadMemorySubtract
}
keyboard::key::Code::NumpadMultiply => KeyCode::NumpadMultiply,
keyboard::key::Code::NumpadParenLeft => KeyCode::NumpadParenLeft,
keyboard::key::Code::NumpadParenRight => KeyCode::NumpadParenRight,
keyboard::key::Code::NumpadStar => KeyCode::NumpadStar,
keyboard::key::Code::NumpadSubtract => KeyCode::NumpadSubtract,
keyboard::key::Code::Escape => KeyCode::Escape,
keyboard::key::Code::Fn => KeyCode::Fn,
keyboard::key::Code::FnLock => KeyCode::FnLock,
keyboard::key::Code::PrintScreen => KeyCode::PrintScreen,
keyboard::key::Code::ScrollLock => KeyCode::ScrollLock,
keyboard::key::Code::Pause => KeyCode::Pause,
keyboard::key::Code::BrowserBack => KeyCode::BrowserBack,
keyboard::key::Code::BrowserFavorites => KeyCode::BrowserFavorites,
keyboard::key::Code::BrowserForward => KeyCode::BrowserForward,
keyboard::key::Code::BrowserHome => KeyCode::BrowserHome,
keyboard::key::Code::BrowserRefresh => KeyCode::BrowserRefresh,
keyboard::key::Code::BrowserSearch => KeyCode::BrowserSearch,
keyboard::key::Code::BrowserStop => KeyCode::BrowserStop,
keyboard::key::Code::Eject => KeyCode::Eject,
keyboard::key::Code::LaunchApp1 => KeyCode::LaunchApp1,
keyboard::key::Code::LaunchApp2 => KeyCode::LaunchApp2,
keyboard::key::Code::LaunchMail => KeyCode::LaunchMail,
keyboard::key::Code::MediaPlayPause => KeyCode::MediaPlayPause,
keyboard::key::Code::MediaSelect => KeyCode::MediaSelect,
keyboard::key::Code::MediaStop => KeyCode::MediaStop,
keyboard::key::Code::MediaTrackNext => KeyCode::MediaTrackNext,
keyboard::key::Code::MediaTrackPrevious => KeyCode::MediaTrackPrevious,
keyboard::key::Code::Power => KeyCode::Power,
keyboard::key::Code::Sleep => KeyCode::Sleep,
keyboard::key::Code::AudioVolumeDown => KeyCode::AudioVolumeDown,
keyboard::key::Code::AudioVolumeMute => KeyCode::AudioVolumeMute,
keyboard::key::Code::AudioVolumeUp => KeyCode::AudioVolumeUp,
keyboard::key::Code::WakeUp => KeyCode::WakeUp,
keyboard::key::Code::Meta => KeyCode::Super,
keyboard::key::Code::Hyper => KeyCode::Hyper,
keyboard::key::Code::Turbo => KeyCode::Turbo,
keyboard::key::Code::Abort => KeyCode::Abort,
keyboard::key::Code::Resume => KeyCode::Resume,
keyboard::key::Code::Suspend => KeyCode::Suspend,
keyboard::key::Code::Again => KeyCode::Again,
keyboard::key::Code::Copy => KeyCode::Copy,
keyboard::key::Code::Cut => KeyCode::Cut,
keyboard::key::Code::Find => KeyCode::Find,
keyboard::key::Code::Open => KeyCode::Open,
keyboard::key::Code::Paste => KeyCode::Paste,
keyboard::key::Code::Props => KeyCode::Props,
keyboard::key::Code::Select => KeyCode::Select,
keyboard::key::Code::Undo => KeyCode::Undo,
keyboard::key::Code::Hiragana => KeyCode::Hiragana,
keyboard::key::Code::Katakana => KeyCode::Katakana,
keyboard::key::Code::F1 => KeyCode::F1,
keyboard::key::Code::F2 => KeyCode::F2,
keyboard::key::Code::F3 => KeyCode::F3,
keyboard::key::Code::F4 => KeyCode::F4,
keyboard::key::Code::F5 => KeyCode::F5,
keyboard::key::Code::F6 => KeyCode::F6,
keyboard::key::Code::F7 => KeyCode::F7,
keyboard::key::Code::F8 => KeyCode::F8,
keyboard::key::Code::F9 => KeyCode::F9,
keyboard::key::Code::F10 => KeyCode::F10,
keyboard::key::Code::F11 => KeyCode::F11,
keyboard::key::Code::F12 => KeyCode::F12,
keyboard::key::Code::F13 => KeyCode::F13,
keyboard::key::Code::F14 => KeyCode::F14,
keyboard::key::Code::F15 => KeyCode::F15,
keyboard::key::Code::F16 => KeyCode::F16,
keyboard::key::Code::F17 => KeyCode::F17,
keyboard::key::Code::F18 => KeyCode::F18,
keyboard::key::Code::F19 => KeyCode::F19,
keyboard::key::Code::F20 => KeyCode::F20,
keyboard::key::Code::F21 => KeyCode::F21,
keyboard::key::Code::F22 => KeyCode::F22,
keyboard::key::Code::F23 => KeyCode::F23,
keyboard::key::Code::F24 => KeyCode::F24,
keyboard::key::Code::F25 => KeyCode::F25,
keyboard::key::Code::F26 => KeyCode::F26,
keyboard::key::Code::F27 => KeyCode::F27,
keyboard::key::Code::F28 => KeyCode::F28,
keyboard::key::Code::F29 => KeyCode::F29,
keyboard::key::Code::F30 => KeyCode::F30,
keyboard::key::Code::F31 => KeyCode::F31,
keyboard::key::Code::F32 => KeyCode::F32,
keyboard::key::Code::F33 => KeyCode::F33,
keyboard::key::Code::F34 => KeyCode::F34,
keyboard::key::Code::F35 => KeyCode::F35,
_ => None?,
})
}
#[cfg(feature = "wayland")]
fn winit_native_key_code(
keycode: keyboard::key::NativeCode,
) -> winit::keyboard::NativeKeyCode {
match keycode {
keyboard::key::NativeCode::Unidentified => {
winit::keyboard::NativeKeyCode::Unidentified
}
keyboard::key::NativeCode::Android(k) => {
winit::keyboard::NativeKeyCode::Android(k)
}
keyboard::key::NativeCode::MacOS(k) => {
winit::keyboard::NativeKeyCode::MacOS(k)
}
keyboard::key::NativeCode::Windows(k) => {
winit::keyboard::NativeKeyCode::Windows(k)
}
keyboard::key::NativeCode::Xkb(k) => {
winit::keyboard::NativeKeyCode::Xkb(k)
}
}
}
/// Reconstruct the raw keycode
#[cfg(feature = "wayland")]
pub fn physical_to_scancode(physical: keyboard::key::Physical) -> Option<u32> {
let Some(physical_key) = (match physical {
keyboard::key::Physical::Code(code) => {
winit_key_code(code).map(|k| winit::keyboard::PhysicalKey::Code(k))
}
keyboard::key::Physical::Unidentified(native_code) => {
Some(winit::keyboard::PhysicalKey::Unidentified(
winit_native_key_code(native_code),
))
}
}) else {
return None;
};
crate::keymap::physicalkey_to_scancode(physical_key)
}
/// Converts a `NativeKeyCode` from [`winit`] to an [`iced`] native key code.
///
/// [`winit`]: https://github.com/rust-windowing/winit

View file

@ -0,0 +1,13 @@
use iced_runtime::{
self,
platform_specific::{self, wayland},
task, Action, Task,
};
pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> {
task::oneshot(|_| {
Action::PlatformSpecific(platform_specific::Action::Wayland(
wayland::Action::InhibitShortcuts(inhibit),
))
})
}

View file

@ -1,6 +1,7 @@
//! Interact with the wayland objects of your application.
pub mod activation;
pub mod keyboard_shortcuts_inhibit;
pub mod layer_surface;
pub mod overlap_notify;
pub mod popup;

View file

@ -51,6 +51,7 @@ use std::{
use tracing::error;
use wayland_backend::client::Backend;
use wayland_client::globals::GlobalError;
use wayland_protocols::wp::keyboard_shortcuts_inhibit::zv1::client::zwp_keyboard_shortcuts_inhibit_manager_v1;
use winit::{dpi::LogicalSize, event_loop::OwnedDisplayHandle};
use self::state::SctkState;
@ -309,11 +310,19 @@ impl SctkEventLoop {
&registry_state,
&qh,
),
inhibitor_manager: registry_state.bind_one::<zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1, _, _>(
&qh,
1..=1,
(),
).ok(),
registry_state,
queue_handle: qh,
loop_handle,
inhibitor: None,
inhibited: false,
_cursor_surface: None,
_multipool: None,
outputs: Vec::new(),
@ -323,7 +332,6 @@ impl SctkEventLoop {
popups: Vec::new(),
lock_surfaces: Vec::new(),
subsurfaces: Vec::new(),
_kbd_focus: None,
touch_points: HashMap::new(),
sctk_events: Vec::new(),
frame_status: HashMap::new(),

View file

@ -94,8 +94,7 @@ use cctk::{cosmic_protocols::overlap_notify::v1::client::zcosmic_overlap_notific
}, toplevel_info::ToplevelInfoState, toplevel_management::ToplevelManagerState};
use wayland_protocols::{
wp::{
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1,
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,
};
@ -388,7 +387,6 @@ pub struct SctkState {
pub(crate) popups: Vec<SctkPopup>,
pub(crate) subsurfaces: Vec<SctkSubsurface>,
pub(crate) lock_surfaces: Vec<SctkLockSurface>,
pub(crate) _kbd_focus: Option<WlSurface>,
pub(crate) touch_points: HashMap<touch::Finger, (WlSurface, Point)>,
/// Window updates, which are coming from SCTK or the compositor, which require
@ -434,6 +432,10 @@ pub struct SctkState {
pub(crate) activation_token_ctr: u32,
pub(crate) token_senders: HashMap<u32, oneshot::Sender<Option<String>>>,
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>,
}
/// An error that occurred while running an application.
@ -1463,6 +1465,17 @@ impl SctkState {
}
},
},
Action::InhibitShortcuts(v) => {
if let Some(manager) = self.inhibitor_manager.as_ref() {
if let Some(inhibit) = self.inhibitor.take() {
inhibit.destroy();
}
if v {
self.inhibitor = self.seats.iter().next()
.and_then(|s| s.kbd_focus.as_ref().map(|surface| manager.inhibit_shortcuts(surface, &s.seat, &self.queue_handle, ())));
}
}
}
};
Ok(())
}

View file

@ -0,0 +1,52 @@
use cctk::sctk;
use sctk::reexports::{
client::{Connection, Dispatch, Proxy},
protocols::wp::keyboard_shortcuts_inhibit::{
self, zv1::client::zwp_keyboard_shortcuts_inhibitor_v1,
},
};
use crate::event_loop::state::SctkState;
use crate::platform_specific::wayland::SctkEvent;
impl Dispatch<keyboard_shortcuts_inhibit::zv1::client::zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1, ()> for SctkState {
fn event(
_state: &mut Self,
_proxy: &keyboard_shortcuts_inhibit::zv1::client::zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1,
_event: <keyboard_shortcuts_inhibit::zv1::client::zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1 as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
) {}
}
impl
Dispatch<
zwp_keyboard_shortcuts_inhibitor_v1::ZwpKeyboardShortcutsInhibitorV1,
(),
> for SctkState
{
fn event(
state: &mut Self,
_proxy: &zwp_keyboard_shortcuts_inhibitor_v1::ZwpKeyboardShortcutsInhibitorV1,
event: <zwp_keyboard_shortcuts_inhibitor_v1::ZwpKeyboardShortcutsInhibitorV1 as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
) {
match event {
zwp_keyboard_shortcuts_inhibitor_v1::Event::Active => {
state.sctk_events.push(SctkEvent::ShortcutsInhibited(true));
state.inhibited = true;
}
zwp_keyboard_shortcuts_inhibitor_v1::Event::Inactive => {
state.sctk_events.push(SctkEvent::ShortcutsInhibited(false));
state.inhibited = false;
if let Some(inhibitor) = state.inhibitor.take() {
inhibitor.destroy();
}
}
_ => unimplemented!(),
}
}
}

View file

@ -1,5 +1,6 @@
// TODO support multi-seat handling
pub mod keyboard;
pub mod keyboard_shortcuts_inhibit;
pub mod pointer;
pub mod seat;
pub mod touch;

View file

@ -1,5 +1,7 @@
// Borrowed from winit
use iced_runtime::keyboard::{Key, Location, key::Named};
use winit::keyboard::{KeyCode, NativeKeyCode, PhysicalKey};
/// Map the raw X11-style keycode to the `KeyCode` enum.
///
/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses.
@ -838,8 +840,6 @@ pub fn keysym_to_key(keysym: u32) -> Key {
})
}
use iced_runtime::keyboard::{Key, Location, key::Named};
pub fn keysym_location(keysym: u32) -> Location {
use xkbcommon_dl::keysyms;
match keysym {

View file

@ -211,6 +211,7 @@ pub enum SctkEvent {
SurfaceScaleFactorChanged(f64, WlSurface, window::Id),
Winit(WindowId, WindowEvent),
Subcompositor(SubsurfaceState),
ShortcutsInhibited(bool),
}
#[cfg(feature = "a11y")]
@ -1697,6 +1698,14 @@ impl SctkEvent {
}
}
},
SctkEvent::ShortcutsInhibited(v) => events.push((
None,
iced_runtime::core::Event::PlatformSpecific(
PlatformSpecific::Wayland(
wayland::Event::ShortcutsInhibited(v),
),
),
)),
}
}
}