diff --git a/Platform/macOS/Display/VMDisplayQemuMetalWindowController.swift b/Platform/macOS/Display/VMDisplayQemuMetalWindowController.swift index e8e7da9a6..36dba9387 100644 --- a/Platform/macOS/Display/VMDisplayQemuMetalWindowController.swift +++ b/Platform/macOS/Display/VMDisplayQemuMetalWindowController.swift @@ -60,6 +60,7 @@ class VMDisplayQemuMetalWindowController: VMDisplayQemuWindowController { @Setting("CtrlRightClick") private var isCtrlRightClick: Bool = false @Setting("AlternativeCaptureKey") private var isAlternativeCaptureKey: Bool = false @Setting("IsCapsLockKey") private var isCapsLockKey: Bool = false + @Setting("IsNumLockForced") private var isNumLockForced: Bool = false @Setting("InvertScroll") private var isInvertScroll: Bool = false @Setting("QEMURendererFPSLimit") private var rendererFpsLimit: Int = 0 private var settingObservations = [NSKeyValueObservation]() @@ -535,4 +536,17 @@ extension VMDisplayQemuMetalWindowController: VMMetalViewInputDelegate { } vmInput.keyLock = locks } + + /// Update virtual num lock status if we force num pad to on + func didUseNumericPad() { + guard isNumLockForced else { + return // nothing to do + } + guard let vmInput = vmInput else { + return + } + if !vmInput.keyLock.contains(.num) { + vmInput.keyLock.insert(.num) + } + } } diff --git a/Platform/macOS/Display/VMMetalView.swift b/Platform/macOS/Display/VMMetalView.swift index 44e23edd6..615602506 100644 --- a/Platform/macOS/Display/VMMetalView.swift +++ b/Platform/macOS/Display/VMMetalView.swift @@ -149,6 +149,9 @@ class VMMetalView: MTKView { override func keyDown(with event: NSEvent) { guard !event.isARepeat else { return } logger.trace("key down: \(event.keyCode)") + if event.modifierFlags.contains(.numericPad) { + inputDelegate?.didUseNumericPad() + } lastKeyDown = getScanCodeForEvent(event) inputDelegate?.keyDown(scanCode: lastKeyDown!) } diff --git a/Platform/macOS/Display/VMMetalViewInputDelegate.swift b/Platform/macOS/Display/VMMetalViewInputDelegate.swift index fee57ba68..1cc98883b 100644 --- a/Platform/macOS/Display/VMMetalViewInputDelegate.swift +++ b/Platform/macOS/Display/VMMetalViewInputDelegate.swift @@ -26,4 +26,5 @@ protocol VMMetalViewInputDelegate: class { func syncCapsLock(with modifier: NSEvent.ModifierFlags?) func captureMouse() func releaseMouse() + func didUseNumericPad() } diff --git a/Platform/macOS/SettingsView.swift b/Platform/macOS/SettingsView.swift index 27fa8cb3c..966b06f5f 100644 --- a/Platform/macOS/SettingsView.swift +++ b/Platform/macOS/SettingsView.swift @@ -103,6 +103,7 @@ struct InputSettingsView: View { @AppStorage("CtrlRightClick") var isCtrlRightClick = false @AppStorage("AlternativeCaptureKey") var isAlternativeCaptureKey = false @AppStorage("IsCapsLockKey") var isCapsLockKey = false + @AppStorage("IsNumLockForced") var isNumLockForced = false @AppStorage("InvertScroll") var isInvertScroll = false @AppStorage("NoUsbPrompt") var isNoUsbPrompt = false @@ -124,6 +125,9 @@ struct InputSettingsView: View { Toggle(isOn: $isCapsLockKey, label: { Text("Caps Lock (⇪) is treated as a key") }).help("If enabled, caps lock will be handled like other keys. If disabled, it is treated as a toggle that is synchronized with the host.") + Toggle(isOn: $isNumLockForced, label: { + Text("Num Lock is forced on") + }).help("If enabled, num lock will always be on to the guest. Note this may make your keyboard's num lock indicator out of sync.") } Section(header: Text("QEMU USB")) { @@ -145,6 +149,7 @@ extension UserDefaults { @objc dynamic var NoUsbPrompt: Bool { false } @objc dynamic var AlternativeCaptureKey: Bool { false } @objc dynamic var IsCapsLockKey: Bool { false } + @objc dynamic var IsNumLockForced: Bool { false } @objc dynamic var NoSaveScreenshot: Bool { false } @objc dynamic var InvertScroll: Bool { false } @objc dynamic var QEMURendererBackend: Int { 0 }