fix: use more permissive denylist for bare shortcut key validation

This commit is contained in:
Shane Stanley 2026-04-02 15:26:18 -04:00 committed by GitHub
parent 98fc3d5952
commit 18ff4d01de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 414 additions and 496 deletions

856
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,7 @@ pub enum ShortcutMessage {
ResetBindings,
ShowShortcut(usize, String),
SubmitBinding(usize),
TabPressed,
Inhibited(bool),
ProtocolUnavailable,
ModifiersChanged(Modifiers),
@ -545,6 +546,23 @@ impl Model {
shortcut.input = shortcut.pending.to_string();
}
}
// libcosmic requires we set on_tab() and manually process here
// otherwise it'll consume the tab key event for navigation
ShortcutMessage::TabPressed => {
if let Some((short_id, id)) = self.shortcut_context.zip(self.editing)
&& let Some(model) = self.shortcut_models.get_mut(short_id)
&& let Some(shortcut) = model.bindings.get_mut(id)
{
shortcut.pending.key = Some(xkeysym::Keysym::Tab);
shortcut.pending.keycode = None;
shortcut.input = shortcut.pending.to_string();
return Task::batch(vec![
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(false).discard(),
self.submit_binding(id),
cosmic::widget::text_input::focus(self.add_keybindings_button_id.clone()),
]);
}
}
ShortcutMessage::KeyReleased(keycode, _, _) => {
if let Some((short_id, id)) = self.shortcut_context.zip(self.editing)
&& let Some(model) = self.shortcut_models.get_mut(short_id)
@ -557,8 +575,9 @@ impl Model {
if shortcut.pending.modifiers
!= cosmic_settings_config::shortcuts::Modifiers::new()
|| shortcut.pending.key.is_some_and(|key| {
key.is_misc_function_key()
|| matches!(key.raw(), 0x10080001..=0x1008FFFF)
!cosmic_settings_config::shortcuts::is_forbidden_unmodified_keysym(
key,
)
})
{
shortcut.input = shortcut.pending.to_string();
@ -629,8 +648,7 @@ impl Model {
if matches!(
key,
Key::Named(Named::Super | Named::Alt | Named::Control | Named::Shift)
) || matches!((&key, modifiers), (Key::Named(Named::Tab), modifiers) if modifiers.is_empty() || modifiers == Modifiers::SHIFT)
{
) {
return None;
}
cosmic::iced_winit::conversion::physical_to_scancode(physical_key)
@ -788,6 +806,7 @@ fn context_drawer<'a>(
.on_input(move |text| ShortcutMessage::InputBinding(bind_id, text))
.on_unfocus(ShortcutMessage::SubmitBinding(bind_id))
.on_submit(move |_| ShortcutMessage::SubmitBinding(bind_id))
.on_tab(ShortcutMessage::TabPressed) // capture Tab to prevent focus navigation
.padding([0, space_xs])
.id(shortcut.id.clone())
.into();

View file

@ -67,6 +67,7 @@ pub enum Message {
Shortcut(ShortcutMessage),
/// Open the add shortcut context drawer
ShortcutContext,
TabPressed,
ModifiersChanged(Modifiers),
KeyReleased(u32, Key, Location),
KeyPressed(u32, Key, Location, Modifiers),
@ -297,6 +298,26 @@ impl Page {
}
}
}
// libcosmic requires we set on_tab() and manually process here
// otherwise it'll consume the tab key event for navigation
Message::TabPressed => {
if self.add_shortcut.editing.is_some() && self.add_shortcut.active {
self.add_shortcut.binding.key = Some(xkeysym::Keysym::Tab);
self.add_shortcut.binding.keycode = Some(0);
if let Some(k) = self
.add_shortcut
.keys
.get_mut(self.add_shortcut.editing.unwrap())
{
k.0 = self.add_shortcut.binding.to_string();
}
return self.update(Message::KeyReleased(
0,
Key::Named(Named::Tab),
Location::Standard,
));
}
}
Message::KeyReleased(keycode, _, _) => {
// if the currently selected shortcut matches, finish selecting shortcut
if self.add_shortcut.editing.is_some()
@ -310,7 +331,7 @@ impl Page {
&& self.add_shortcut.binding.modifiers
!= cosmic_settings_config::shortcuts::Modifiers::new()
|| self.add_shortcut.binding.key.is_some_and(|key| {
key.is_misc_function_key() || matches!(key.raw(), 0x10080001..=0x1008FFFF)
!cosmic_settings_config::shortcuts::is_forbidden_unmodified_keysym(key)
})
{
// XX for now avoid applying the keycode
@ -452,6 +473,7 @@ impl Page {
.select_on_focus(true)
.on_input(move |input| Message::KeyInput(id, input))
.on_submit(|_| Message::AddKeybinding)
.on_tab(Message::TabPressed) // capture Tab to prevent focus navigation
.padding([0, 12])
.id(widget_id.clone())
.apply(widget::container)
@ -599,8 +621,7 @@ impl page::Page<crate::pages::Message> for Page {
if matches!(
key,
Key::Named(Named::Super | Named::Alt | Named::Control | Named::Shift)
) || matches!((&key, modifiers), (Key::Named(Named::Tab), modifiers) if modifiers.is_empty() || modifiers == Modifiers::SHIFT)
{
) {
return None;
}
cosmic::iced_winit::conversion::physical_to_scancode(physical_key).map(