Skip to content

Commit

Permalink
Prevent Option key without Command or Control on macOS 15 or later in…
Browse files Browse the repository at this point in the history
… sandboxed apps

Fixes #176
  • Loading branch information
sindresorhus committed Sep 22, 2024
1 parent 68d5d2d commit 425ddf7
Show file tree
Hide file tree
Showing 22 changed files with 62 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Example/KeyboardShortcutsExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
Expand Down Expand Up @@ -343,6 +344,7 @@
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
Expand Down
4 changes: 2 additions & 2 deletions Example/KeyboardShortcutsExample/MainScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ private struct DoubleShortcut: View {
.frame(maxWidth: 300)
.padding()
.padding()
.onKeyboardShortcut(.testShortcut1) {
.onGlobalKeyboardShortcut(.testShortcut1) {
isPressed1 = $0 == .keyDown
}
.onKeyboardShortcut(.testShortcut2, type: .keyDown) {
.onGlobalKeyboardShortcut(.testShortcut2, type: .keyDown) {
isPressed2 = true
}
.task {
Expand Down
2 changes: 2 additions & 0 deletions Sources/KeyboardShortcuts/CarbonKeyboardShortcuts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ enum CarbonKeyboardShortcuts {
registerError == noErr,
let carbonHotKey = eventHotKey
else {
print("Error registering hotkey \(shortcut):", registerError)
return
}

Expand Down Expand Up @@ -181,6 +182,7 @@ enum CarbonKeyboardShortcuts {
error == noErr,
let eventHotKey
else {
print("Error registering hotkey \(hotKey.shortcut):", error)
hotKeys.removeValue(forKey: hotKey.carbonHotKeyId)
continue
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "لا يمكن استخدام اختصار لوحة المفاتيح هذا لأنه مستخدم بواسطة عنصر القائمة “%@”.";
"keyboard_shortcut_used_by_system" = "لا يمكن استخدام اختصار لوحة المفاتيح هذا لأنه مستخدم مسبقاً على مستوى النظام.";
"keyboard_shortcuts_can_be_changed" = "يمكن تغيير معظم اختصارات لوحة المفاتيح على مستوى النظام في “تفضيلات النظام > لوحة المفاتيح > الاختصارات ”.";
"keyboard_shortcut_disallowed" = "يجب دمج مفتاح Option مع Command أو Control.";
"ok" = "موافق";
"space_key" = "مسافة";
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "Tuto zkratku nelze použít, protože je již využívána položkou „%@“";
"keyboard_shortcut_used_by_system" = "Tuto zkratku nelze použít, protože už ji používá systém.";
"keyboard_shortcuts_can_be_changed" = "Většinu systémových zkratek můžete změnit v „Nastavení systému › Klávesnice › Klávesové zkratky“.";
"keyboard_shortcut_disallowed" = "Modifikátor Option musí být kombinován s klávesou Command nebo Control.";
"ok" = "OK";
"space_key" = "Mezera";
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "Dieses Tastaturkürzel kann nicht verwendet werden, da es bereits durch den Menüpunkt „%@” belegt ist.";
"keyboard_shortcut_used_by_system" = "Dieses Tastaturkürzel kann nicht verwendet werden, da es bereits systemweit verwendet wird.";
"keyboard_shortcuts_can_be_changed" = "Die meisten systemweiten Tastaturkürzel können unter „Systemeinstellungen › Tastatur › Tastaturkurzbefehle“ geändert werden.";
"keyboard_shortcut_disallowed" = "Die Option-Taste muss mit der Befehlstaste oder der Steuerungstaste kombiniert werden.";
"ok" = "OK";
"space_key" = "Leer";
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"keyboard_shortcut_used_by_menu_item" = "This keyboard shortcut cannot be used as it’s already used by the “%@” menu item.";
"keyboard_shortcut_used_by_system" = "This keyboard shortcut cannot be used as it’s already a system-wide keyboard shortcut.";
"keyboard_shortcuts_can_be_changed" = "Most system-wide keyboard shortcuts can be changed in “System Settings › Keyboard › Keyboard Shortcuts”.";
"keyboard_shortcut_disallowed" = "Option modifier must be combined with Command or Control.";
"force_use_shortcut" = "Use Anyway";
"ok" = "OK";
"space_key" = "Space";
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "Este atajo de teclado no se puede utilizar ya que está siendo utilizado por el elemento de menú “%@”.";
"keyboard_shortcut_used_by_system" = "Este atajo de teclado no se puede utilizar ya que está siendo utilizado por un atajo del sistema operativo.";
"keyboard_shortcuts_can_be_changed" = "La mayoría de los atajos de teclado del sistema operativo pueden ser modificados en “Configuración del sistema › Teclado › Atajos de teclado“.";
"keyboard_shortcut_disallowed" = "El modificador Option debe combinarse con Command o Control.";
"ok" = "Aceptar";
"space_key" = "Espacio";
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "Ce raccourci ne peut pas être utilisé, car il est déjà utilisé par le menu “%@”.";
"keyboard_shortcut_used_by_system" = "Ce raccourci ne peut pas être utilisé car il s'agit d'un raccourci déjà présent dans le système.";
"keyboard_shortcuts_can_be_changed" = "La plupart des raccourcis clavier de l'ensemble du système peuvent être modifiés en “Réglages du système… › Clavier › Raccourcis clavier…”.";
"keyboard_shortcut_disallowed" = "Le modificateur Option doit être combiné avec Command ou Control.";
"ok" = "OK";
"space_key" = "Espace";
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "Ez a billentyűparancs nem használható mert már a “%@” menü elem használja.";
"keyboard_shortcut_used_by_system" = "Ez a billentyűparancs nem használható mert már egy rendszerszintü billentyűparancs.";
"keyboard_shortcuts_can_be_changed" = "A legtöbb rendszerszintü billentyűparancsot a “Rendszerbeállítások › Billentyűzet › Billentyűparancsok“ menüben meg lehet változtatni";
"keyboard_shortcut_disallowed" = "Az Option módosítót a Command vagy Control billentyűvel együtt kell használni.";
"ok" = "OK";
"space_key" = "Szóköz";
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"keyboard_shortcut_used_by_menu_item" = "このショートカットは既に“%@”で使われており使えません。";
"keyboard_shortcut_used_by_system" = "このショートカットキーは既にシステムで使われており使えません。";
"keyboard_shortcuts_can_be_changed" = "システムで設定されているショートカットキーは“システム設定 › キーボード › キーボードショートカット”で変更できます。";
"keyboard_shortcut_disallowed" = "Optionキーは、CommandキーまたはControlキーと組み合わせる必要があります。";
"force_use_shortcut" = "強制使用";
"ok" = "OK";
"space_key" = "スペース";
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "이 키보드 단축키는 이미 “%@” 메뉴 항목에 사용되고 있으므로 등록할 수 없습니다.";
"keyboard_shortcut_used_by_system" = "이 키보드 단축키는 이미 시스템상에서 사용되고 있으므로 등록할 수 없습니다.";
"keyboard_shortcuts_can_be_changed" = "대부분의 시스템 키보드 단축키는 “시스템 설정 › 키보드 › 키보드 단축키”에서 변경 가능합니다.";
"keyboard_shortcut_disallowed" = "Option 수정자는 Command 또는 Control과 함께 사용해야 합니다.";
"ok" = "확인";
"space_key" = "빈칸";
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "Deze toetscombinatie kan niet worden gebruikt omdat hij al wordt gebruikt door het menu item “%@”.";
"keyboard_shortcut_used_by_system" = "Deze toetscombinatie kan niet worden gebruikt omdat hij al door het systeem gebruikt wordt.";
"keyboard_shortcuts_can_be_changed" = "De meeste systeem toetscombinaties kunnen onder “Systeeminstellingen… > Toetsenbord > Toetscombinaties…” veranderd worden.";
"keyboard_shortcut_disallowed" = "De Option-toets moet worden gecombineerd met Command of Control.";
"ok" = "OK";
"space_key" = "spatie";
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "Este atalho não pode ser usado porque ele já é usado pelo item de menu “%@”.";
"keyboard_shortcut_used_by_system" = "Este atalho não pode ser usado porque ele já é usado por um atalho do sistema.";
"keyboard_shortcuts_can_be_changed" = "A maioria dos atalhos do sistema podem ser alterados em “Ajustes do Sistema › Teclado › Atalhos de Teclado”.";
"keyboard_shortcut_disallowed" = "O modificador Option deve ser combinado com Command ou Control.";
"ok" = "OK";
"space_key" = "Espaço";
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"keyboard_shortcut_used_by_menu_item" = "Это сочетание клавиш нельзя использовать, так как оно уже используется в пункте меню «%@».";
"keyboard_shortcut_used_by_system" = "Это сочетание клавиш нельзя использовать, поскольку оно является системным.";
"keyboard_shortcuts_can_be_changed" = "Большинство системных сочетаний клавиш можно изменить в «Системные настройки › Клавиатура › Сочетания клавиш».";
"keyboard_shortcut_disallowed" = "Клавиша Option должна использоваться в сочетании с Command или Control.";
"ok" = "OK";
"space_key" = "Пробел";
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"keyboard_shortcut_used_by_menu_item" = "Túto klávesovú skratku nemožno použiť, pretože ju už používa položka menu “%@”.";
"keyboard_shortcut_used_by_system" = "Túto klávesovú skratku nemožno použiť, pretože je už použivaná pre celý systém.";
"keyboard_shortcuts_can_be_changed" = "Väčšinu systémových klávesových skratiek môžeš zmeniť v “Systémové nastavenia › Klávesnica › Klávesové skratky”.";
"keyboard_shortcut_disallowed" = "Modifikátor Option musí byť kombinovaný s klávesmi Command alebo Control.";
"force_use_shortcut" = "Použiť napriek tomu";
"ok" = "OK";
"space_key" = "Medzerník";
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"keyboard_shortcut_used_by_menu_item" = "当前快捷键无法使用,因为它已被用作菜单项 “%@” 的快捷键。";
"keyboard_shortcut_used_by_system" = "当前快捷键无法使用,因为它已被用作系统快捷键。";
"keyboard_shortcuts_can_be_changed" = "可以在 “系统设置 › 键盘 › 键盘快捷键” 中更改大多数系统快捷键。";
"keyboard_shortcut_disallowed" = "Option 修飾鍵必須與 Command 或 Control 組合使用。";
"force_use_shortcut" = "强制使用";
"ok" = "好";
"space_key" = "空格";
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"keyboard_shortcut_used_by_menu_item" = "此快速鍵無法使用,因為它已被選單項目「%@」使用。";
"keyboard_shortcut_used_by_system" = "此快速鍵無法使用,因為它已被系統使用。";
"keyboard_shortcuts_can_be_changed" = "可以在「系統設定 › 鍵盤 › 鍵盤快速鍵」中更改大多數的系統快速鍵。";
"keyboard_shortcut_disallowed" = "Option 鍵必須與 Command 或 Control 鍵組合使用。";
"force_use_shortcut" = "強制使用";
"ok" = "好";
"space_key" = "空格";
13 changes: 13 additions & 0 deletions Sources/KeyboardShortcuts/RecorderCocoa.swift
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,19 @@ extension KeyboardShortcuts {
return nil
}

// See: https://developer.apple.com/forums/thread/763878?answerId=804374022#804374022
if shortcut.isDisallowed {
blur()

NSAlert.showModal(
for: window,
title: "keyboard_shortcut_disallowed".localized
)

focus()
return nil
}

if shortcut.isTakenBySystem {
blur()

Expand Down
20 changes: 20 additions & 0 deletions Sources/KeyboardShortcuts/Shortcut.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ extension KeyboardShortcuts {
}
}

enum Constants {
static let isSandboxed = ProcessInfo.processInfo.environment.hasKey("APP_SANDBOX_CONTAINER_ID")
}

extension KeyboardShortcuts.Shortcut {
/**
System-defined keyboard shortcuts.
Expand All @@ -96,6 +100,22 @@ extension KeyboardShortcuts.Shortcut {
CarbonKeyboardShortcuts.system
}

/**
Check whether the keyboard shortcut is disallowed.
*/
var isDisallowed: Bool {
let disallowedModifiers: NSEvent.ModifierFlags = [.option, [.option, .shift]]
if
#available(macOS 15, *),
Constants.isSandboxed,
disallowedModifiers.contains(modifiers)
{
return true
}

return false
}

/**
Check whether the keyboard shortcut is already taken by the system.
*/
Expand Down
7 changes: 7 additions & 0 deletions Sources/KeyboardShortcuts/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -480,4 +480,11 @@ extension View {
.alignmentGuide(.leading) { $0[.controlAlignment] }
}
}


extension Dictionary {
func hasKey(_ key: Key) -> Bool {
index(forKey: key) != nil
}
}
#endif
2 changes: 1 addition & 1 deletion Sources/KeyboardShortcuts/ViewModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ extension View {
/**
Associates a global keyboard shortcut with a control.
This is mostly useful to have the keyboard shortcut show for a `Button` in a `Menu`.
This is mostly useful to have the keyboard shortcut show for a `Button` in a `Menu` or `MenuBarExtra`.
It does not trigger the control's action.
Expand Down

0 comments on commit 425ddf7

Please sign in to comment.