Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions Sources/Vorssaint/Core/SuperKeyStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ struct SuperKeyStrings {
let soloInputSource: String
let mappingForeignMapping: String
let mappingSystemRefused: String
let keyboardTapRefused: String

/// What to show when the key mapping was refused. Every refusal names one
/// thing to change; none of them is visible in the key itself.
func mappingFailure(_ failure: SuperKeyMappingFailure) -> String {
switch failure {
case .foreignMapping: return mappingForeignMapping
case .systemRefused: return mappingSystemRefused
case .keyboardTapRefused: return keyboardTapRefused
}
}
}
Expand Down Expand Up @@ -81,7 +83,8 @@ extension SuperKeyStrings {
manageButton: "Set up…",
soloInputSource: "Switch input source; hold for Caps Lock",
mappingForeignMapping: "Another app’s key mapping uses the selected key. Remove it in that app: quitting it is not enough.",
mappingSystemRefused: "macOS refused the key mapping. Reconnect the keyboard or restart the Mac, then switch this on again."
mappingSystemRefused: "macOS refused the key mapping. Reconnect the keyboard or restart the Mac, then switch this on again.",
keyboardTapRefused: "macOS would not let Vorssaint watch the keyboard. Turn Vorssaint off and on in System Settings › Privacy & Security › Accessibility, then switch this on again."
)

static let ptBR = SuperKeyStrings(
Expand All @@ -104,7 +107,8 @@ extension SuperKeyStrings {
manageButton: "Configurar…",
soloInputSource: "Trocar fonte de entrada; segure para Caps Lock",
mappingForeignMapping: "O mapeamento de outro app usa a tecla selecionada. Remova-o naquele app: sair dele não basta.",
mappingSystemRefused: "O macOS recusou o mapeamento de teclas. Reconecte o teclado ou reinicie o Mac e ligue isto de novo."
mappingSystemRefused: "O macOS recusou o mapeamento de teclas. Reconecte o teclado ou reinicie o Mac e ligue isto de novo.",
keyboardTapRefused: "O macOS não deixou o Vorssaint observar o teclado. Desligue e ligue o Vorssaint em Ajustes do Sistema › Privacidade e Segurança › Acessibilidade e ligue isto de novo."
)

static let tr = SuperKeyStrings(
Expand All @@ -127,7 +131,8 @@ extension SuperKeyStrings {
manageButton: "Ayarla…",
soloInputSource: "Giriş kaynağını değiştir; Caps Lock için basılı tut",
mappingForeignMapping: "Başka bir uygulamanın tuş eşlemesi seçili tuşu kullanıyor. Eşlemeyi o uygulamada kaldırın: çıkmak yetmez.",
mappingSystemRefused: "macOS tuş eşlemesini kabul etmedi. Klavyeyi yeniden bağlayın veya Mac’i yeniden başlatın, sonra bunu tekrar açın."
mappingSystemRefused: "macOS tuş eşlemesini kabul etmedi. Klavyeyi yeniden bağlayın veya Mac’i yeniden başlatın, sonra bunu tekrar açın.",
keyboardTapRefused: "macOS, Vorssaint’in klavyeyi izlemesine izin vermedi. Sistem Ayarları › Gizlilik ve Güvenlik › Erişilebilirlik bölümünde Vorssaint’i kapatıp açın, sonra bunu tekrar açın."
)

static let ru = SuperKeyStrings(
Expand All @@ -150,7 +155,8 @@ extension SuperKeyStrings {
manageButton: "Настроить…",
soloInputSource: "Сменить источник ввода; удерживать для Caps Lock",
mappingForeignMapping: "Другая программа переназначила выбранную клавишу. Удалите назначение в той программе: завершить её недостаточно.",
mappingSystemRefused: "macOS отклонил переназначение клавиш. Переподключите клавиатуру или перезапустите Mac и включите это снова."
mappingSystemRefused: "macOS отклонил переназначение клавиш. Переподключите клавиатуру или перезапустите Mac и включите это снова.",
keyboardTapRefused: "macOS не разрешил Vorssaint следить за клавиатурой. Выключите и снова включите Vorssaint в Системных настройках › Конфиденциальность и безопасность › Универсальный доступ и включите это снова."
)

static let es = SuperKeyStrings(
Expand All @@ -173,7 +179,8 @@ extension SuperKeyStrings {
manageButton: "Configurar…",
soloInputSource: "Cambiar fuente de entrada; mantener para Bloq Mayús",
mappingForeignMapping: "La reasignación de otra app usa la tecla seleccionada. Elimínala en esa app: salir de ella no basta.",
mappingSystemRefused: "macOS rechazó la reasignación de teclas. Vuelve a conectar el teclado o reinicia el Mac y activa esto de nuevo."
mappingSystemRefused: "macOS rechazó la reasignación de teclas. Vuelve a conectar el teclado o reinicia el Mac y activa esto de nuevo.",
keyboardTapRefused: "macOS no dejó que Vorssaint vigilara el teclado. Desactiva y vuelve a activar Vorssaint en Ajustes del Sistema › Privacidad y seguridad › Accesibilidad y activa esto de nuevo."
)

static let de = SuperKeyStrings(
Expand All @@ -196,7 +203,8 @@ extension SuperKeyStrings {
manageButton: "Einrichten…",
soloInputSource: "Eingabequelle wechseln; für Feststelltaste halten",
mappingForeignMapping: "Die Tastenbelegung einer anderen App verwendet die ausgewählte Taste. Entferne sie in dieser App: Beenden reicht nicht.",
mappingSystemRefused: "macOS hat die Tastenbelegung abgelehnt. Schließe die Tastatur neu an oder starte den Mac neu und schalte dies wieder ein."
mappingSystemRefused: "macOS hat die Tastenbelegung abgelehnt. Schließe die Tastatur neu an oder starte den Mac neu und schalte dies wieder ein.",
keyboardTapRefused: "macOS hat Vorssaint nicht erlaubt, die Tastatur zu beobachten. Schalte Vorssaint unter Systemeinstellungen › Datenschutz & Sicherheit › Bedienungshilfen aus und wieder ein und schalte dies dann wieder ein."
)

static let fr = SuperKeyStrings(
Expand All @@ -219,7 +227,8 @@ extension SuperKeyStrings {
manageButton: "Configurer…",
soloInputSource: "Changer de source d’entrée\u{00A0}; maintenir pour Verr. Maj",
mappingForeignMapping: "Le remappage d’une autre app utilise la touche sélectionnée. Supprimez-le dans cette app\u{00A0}: la quitter ne suffit pas.",
mappingSystemRefused: "macOS a refusé le remappage. Rebranchez le clavier ou redémarrez le Mac, puis réactivez ceci."
mappingSystemRefused: "macOS a refusé le remappage. Rebranchez le clavier ou redémarrez le Mac, puis réactivez ceci.",
keyboardTapRefused: "macOS n’a pas autorisé Vorssaint à surveiller le clavier. Désactivez puis réactivez Vorssaint dans Réglages Système › Confidentialité et sécurité › Accessibilité, puis réactivez ceci."
)

static let it = SuperKeyStrings(
Expand All @@ -242,7 +251,8 @@ extension SuperKeyStrings {
manageButton: "Configura…",
soloInputSource: "Cambia sorgente di input; tieni premuto per Blocco Maiuscole",
mappingForeignMapping: "La rimappatura di un’altra app usa il tasto selezionato. Rimuovila in quell’app: chiuderla non basta.",
mappingSystemRefused: "macOS ha rifiutato la rimappatura. Ricollega la tastiera o riavvia il Mac, poi riattiva questa funzione."
mappingSystemRefused: "macOS ha rifiutato la rimappatura. Ricollega la tastiera o riavvia il Mac, poi riattiva questa funzione.",
keyboardTapRefused: "macOS non ha permesso a Vorssaint di osservare la tastiera. Disattiva e riattiva Vorssaint in Impostazioni di Sistema › Privacy e sicurezza › Accessibilità, poi riattiva questa funzione."
)

static let ja = SuperKeyStrings(
Expand All @@ -265,7 +275,8 @@ extension SuperKeyStrings {
manageButton: "設定…",
soloInputSource: "入力ソースを切り替え(長押しで Caps Lock)",
mappingForeignMapping: "他のアプリのキー割り当てが選択したキーを使っています。そのアプリで割り当てを削除してください。終了するだけでは残ります。",
mappingSystemRefused: "macOS がキー割り当てを受け付けませんでした。キーボードを接続し直すか Mac を再起動してから、もう一度オンにしてください。"
mappingSystemRefused: "macOS がキー割り当てを受け付けませんでした。キーボードを接続し直すか Mac を再起動してから、もう一度オンにしてください。",
keyboardTapRefused: "macOS が Vorssaint にキーボードの監視を許可しませんでした。システム設定 › プライバシーとセキュリティ › アクセシビリティで Vorssaint をオフにしてからオンに戻し、もう一度オンにしてください。"
)

static let ko = SuperKeyStrings(
Expand All @@ -288,7 +299,8 @@ extension SuperKeyStrings {
manageButton: "설정…",
soloInputSource: "입력 소스 전환(길게 눌러 Caps Lock)",
mappingForeignMapping: "다른 앱의 키 매핑이 선택한 키를 사용하고 있습니다. 그 앱에서 매핑을 지우세요. 종료만으로는 사라지지 않습니다.",
mappingSystemRefused: "macOS가 키 매핑을 거부했습니다. 키보드를 다시 연결하거나 Mac을 재시동한 뒤 이 기능을 켜세요."
mappingSystemRefused: "macOS가 키 매핑을 거부했습니다. 키보드를 다시 연결하거나 Mac을 재시동한 뒤 이 기능을 켜세요.",
keyboardTapRefused: "macOS가 Vorssaint의 키보드 감시를 허용하지 않았습니다. 시스템 설정 › 개인정보 보호 및 보안 › 손쉬운 사용에서 Vorssaint를 껐다가 다시 켠 뒤 이 기능을 켜세요."
)

static let zhHans = SuperKeyStrings(
Expand All @@ -311,7 +323,8 @@ extension SuperKeyStrings {
manageButton: "设置…",
soloInputSource: "切换输入法;长按开关大写锁定",
mappingForeignMapping: "另一个 App 的按键映射使用了所选按键。请在那个 App 里删除映射:仅退出它并不够。",
mappingSystemRefused: "macOS 拒绝了按键映射。请重新连接键盘或重启 Mac,然后重新打开此功能。"
mappingSystemRefused: "macOS 拒绝了按键映射。请重新连接键盘或重启 Mac,然后重新打开此功能。",
keyboardTapRefused: "macOS 不允许 Vorssaint 监听键盘。请在系统设置 › 隐私与安全性 › 辅助功能中关闭再打开 Vorssaint,然后重新打开此功能。"
)

static let zhTW = SuperKeyStrings(
Expand All @@ -334,7 +347,8 @@ extension SuperKeyStrings {
manageButton: "設定…",
soloInputSource: "切換輸入法;長按切換大寫鎖定",
mappingForeignMapping: "另一個 App 的按鍵對應使用了所選按鍵。請在那個 App 裡移除對應:只結束它並不夠。",
mappingSystemRefused: "macOS 拒絕了按鍵對應。請重新連接鍵盤或重新啟動 Mac,然後重新開啟此功能。"
mappingSystemRefused: "macOS 拒絕了按鍵對應。請重新連接鍵盤或重新啟動 Mac,然後重新開啟此功能。",
keyboardTapRefused: "macOS 不允許 Vorssaint 監聽鍵盤。請在系統設定 › 隱私權與安全性 › 輔助使用中關閉再開啟 Vorssaint,然後重新開啟此功能。"
)

static let zhHK = SuperKeyStrings(
Expand All @@ -357,6 +371,7 @@ extension SuperKeyStrings {
manageButton: "設定…",
soloInputSource: "切換輸入法;長撳切換大寫鎖定",
mappingForeignMapping: "另一個 App 嘅按鍵對應用咗所選按鍵。請喺嗰個 App 度移除對應:淨係結束佢唔夠。",
mappingSystemRefused: "macOS 拒絕咗按鍵對應。請重新接駁鍵盤或者重新啟動 Mac,然後重新開啟呢個功能。"
mappingSystemRefused: "macOS 拒絕咗按鍵對應。請重新接駁鍵盤或者重新啟動 Mac,然後重新開啟呢個功能。",
keyboardTapRefused: "macOS 唔俾 Vorssaint 監聽鍵盤。請喺系統設定 › 隱私權與安全性 › 輔助使用入面關閉再開啟 Vorssaint,然後重新開啟呢個功能。"
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ final class SuperKeyService: ObservableObject {
self.clearLeftoverMapping()
self.isRunning = false
Self.isEngaged = false
self.setMappingFailure(.keyboardTapRefused)
}
return
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/Vorssaint/Services/SuperKey/SuperKeySupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ enum SuperKeyMappingFailure: Equatable, CaseIterable {
case foreignMapping
/// hidutil refused the read, the write, or the readback.
case systemRefused
/// The keyboard event tap could not be created, so the key cannot add modifiers.
case keyboardTapRefused
}

/// The pure half of the super key: which keys are involved, how the mapping
Expand Down
5 changes: 4 additions & 1 deletion Tests/MetricsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ struct MetricsTests {
}),
("metrics", { MetricsFeatureTests.run(suite) }),
("clipboard", { ClipboardFeatureTests.run(suite) }),
("pointer-input", { PointerInputFeatureTests.run(suite) }),
("pointer-input", {
PointerInputFeatureTests.run(suite)
SuperKeyTapContract.run(suite)
}),
("scroll-modifier", { ScrollHorizontalModifierTests.run(suite) }),
("preferences", { PreferencesFeatureTests.run(suite) }),
("app-management", { AppManagementFeatureTests.run(suite) }),
Expand Down
54 changes: 54 additions & 0 deletions Tests/SuperKeyTapContract.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2026 Vorssaint

import CoreGraphics
import Foundation

/// The real tap thread body runs against a macOS that refuses every event tap.
/// No tap, key mapping or run loop is created.
enum SuperKeyTapContract {
enum Tap {
static var requests = 0
static func create(tap: CGEventTapLocation, place: CGEventTapPlacement, options: CGEventTapOptions,
eventsOfInterest: CGEventMask, callback: CGEventTapCallBack,
userInfo: UnsafeMutableRawPointer?) -> CFMachPort? {
requests += 1
return nil
}
}
enum Queue {
static var pending: [() -> Void] = []
static var main: Queue.Type { Self.self }
static func async(execute: @escaping () -> Void) { pending.append(execute) }
}
class State {
typealias DispatchQueue = Queue
static var isEngaged = true
static let mouseDownTypes: [CGEventType] = [.leftMouseDown]
let lifecycleLock = NSLock()
var tapRunLoop: CFRunLoop?
var tapThread: Thread?
var shouldStopTapThread = false
var tap: CFMachPort?
var mouseTap: CFMachPort?
var mouseTapRefusals = 0
var isRunning = true
var mappingFailure: SuperKeyMappingFailure?
var clearedMappings = 0
func clearEventTapThread() -> Bool { false }
func startOnMain() {}
func clearLeftoverMapping() { clearedMappings += 1 }
func tapDidStart(_ tap: CFMachPort) {}
func handle(type: CGEventType, event: CGEvent) -> Unmanaged<CGEvent>? { nil }
}

static func run(_ suite: TestSuite) {
let service = SuperKeyService()
service.runEventTap()
Queue.pending.forEach { $0() }
Queue.pending = []
suite.expect(Tap.requests == 1 && !service.isRunning && !State.isEngaged && service.clearedMappings == 1
&& service.mappingFailure == .keyboardTapRefused,
"a refused keyboard tap stops the key and reports why, found \(String(describing: service.mappingFailure))")
}
}
8 changes: 8 additions & 0 deletions Tests/generate_sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,14 @@ def main():
+ "static func canRemove(_ item: Item, installed: Set<String> = []) -> Bool {\n"
+ "mayRemove(item, installed: installed)\n}\n}\n")

super_key = "Sources/Vorssaint/Services/SuperKey/SuperKeyService.swift"
write("SuperKeyTap.swift", "import CoreGraphics\nimport Foundation\n"
+ "extension SuperKeyTapContract {\nfinal class SuperKeyService: State {\n"
+ "".join(declaration(super_key, prefix).replace("private func", "func", 1)
for prefix in [" private func runEventTap()", " private func setMappingFailure("])
.replace("CGEvent.tapCreate(", "Tap.create(")
+ "}\n}\n")

updates = "Sources/Vorssaint/Services/AppUpdates/AppUpdatesService.swift"
loader = "Sources/Vorssaint/Services/AppUpdates/AppUpdateFeedLoader.swift"
# Only the network configuration, clock and declaration visibility change.
Expand Down
Loading