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
37 changes: 34 additions & 3 deletions Platform/macOS/Display/VMMetalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,25 @@ class VMMetalView: MTKView {
private(set) var isMouseInWindow = false
@Setting("HandleInitialClick") private var isHandleInitialClick: Bool = false
@Setting("IsCtrlCmdSwapped") private var isCtrlCmdSwapped = false
@Setting("IsCmdOptSwapped") private var isCmdOptSwapped = false
@Setting("IsISOKeySwapped") private var isISOKeySwapped = false

private enum ModifierKeySwap {
case none
case ctrlCmd
case cmdOpt
}

private var modifierKeySwap: ModifierKeySwap {
if isCtrlCmdSwapped {
return .ctrlCmd
} else if isCmdOptSwapped {
return .cmdOpt
} else {
return .none
}
}

/// On ISO keyboards we have to switch `kVK_ISO_Section` and `kVK_ANSI_Grave`
/// from: https://chromium.googlesource.com/chromium/src/+/lkgr/ui/events/keycodes/keyboard_code_conversion_mac.mm
private func convertToCurrentLayout(for keycode: Int) -> Int {
Expand Down Expand Up @@ -199,8 +216,16 @@ class VMMetalView: MTKView {
}
if !modifier.isDisjoint(with: [.command, .leftCommand, .rightCommand]) {
let vk = modifier.contains(.rightCommand) ? kVK_RightCommand : kVK_Command
let vkSwapped = modifier.contains(.rightCommand) ? kVK_RightControl : kVK_Control
let sc = Int(KeyCodeMap.keyCodeToScanCodes[isCtrlCmdSwapped ? vkSwapped : vk]!.down)
let vkSwapped: Int
switch modifierKeySwap {
case .ctrlCmd:
vkSwapped = modifier.contains(.rightCommand) ? kVK_RightControl : kVK_Control
case .cmdOpt:
vkSwapped = modifier.contains(.rightCommand) ? kVK_RightOption : kVK_Option
case .none:
vkSwapped = vk
}
let sc = Int(KeyCodeMap.keyCodeToScanCodes[vkSwapped]!.down)
if press {
inputDelegate?.keyDown(scanCode: sc)
} else {
Expand All @@ -227,7 +252,13 @@ class VMMetalView: MTKView {
}
if !modifier.isDisjoint(with: [.option, .leftOption, .rightOption]) {
let vk = modifier.contains(.rightOption) ? kVK_RightOption : kVK_Option
let sc = Int(KeyCodeMap.keyCodeToScanCodes[vk]!.down)
let vkSwapped: Int
if modifierKeySwap == .cmdOpt {
vkSwapped = modifier.contains(.rightOption) ? kVK_RightCommand : kVK_Command
} else {
vkSwapped = vk
}
let sc = Int(KeyCodeMap.keyCodeToScanCodes[vkSwapped]!.down)
if press {
inputDelegate?.keyDown(scanCode: sc)
} else {
Expand Down
30 changes: 27 additions & 3 deletions Platform/macOS/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ struct SoundSettingsView: View {
}

struct InputSettingsView: View {
private enum ModifierKeyMapping: Hashable {
case none
case ctrlCmd
case cmdOpt
}

@AppStorage("FullScreenAutoCapture") var isFullScreenAutoCapture = false
@AppStorage("WindowFocusAutoCapture") var isWindowFocusAutoCapture = false
@AppStorage("OptionAsMetaKey") var isOptionAsMetaKey = false
Expand All @@ -325,11 +331,27 @@ struct InputSettingsView: View {
@AppStorage("IsCapsLockKey") var isCapsLockKey = false
@AppStorage("IsNumLockForced") var isNumLockForced = false
@AppStorage("IsCtrlCmdSwapped") var isCtrlCmdSwapped = false
@AppStorage("IsCmdOptSwapped") var isCmdOptSwapped = false
@AppStorage("InvertScroll") var isInvertScroll = false
@AppStorage("HandleInitialClick") var isHandleInitialClick = false
@AppStorage("IsISOKeySwapped") var isISOKeySwapped = false

@State private var isKeyboardShortcutsShown = false

private var modifierKeyMappingBinding: Binding<ModifierKeyMapping> {
Binding(get: {
if isCtrlCmdSwapped {
return .ctrlCmd
} else if isCmdOptSwapped {
return .cmdOpt
} else {
return .none
}
}, set: { mapping in
isCtrlCmdSwapped = mapping == .ctrlCmd
isCmdOptSwapped = mapping == .cmdOpt
})
}

var body: some View {
Form {
Expand Down Expand Up @@ -373,9 +395,11 @@ struct InputSettingsView: View {
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.")
Toggle(isOn: $isCtrlCmdSwapped, label: {
Text("Swap Control (⌃) and Command (⌘) keys")
}).help("This does not apply to key binding outside the guest.")
Picker("Control, Option, and Command keys", selection: modifierKeyMappingBinding) {
Text("Default").tag(ModifierKeyMapping.none)
Text("Swap Control (⌃) and Command (⌘)").tag(ModifierKeyMapping.ctrlCmd)
Text("Swap Command (⌘) and Option (⌥)").tag(ModifierKeyMapping.cmdOpt)
}.help("This does not apply to key binding outside the guest.")
Toggle(isOn: $isISOKeySwapped) {
Text("Swap the leftmost key on the number row and the key next to left shift on ISO keyboards")
}.help("This only applies to ISO layout keyboards.")
Expand Down
Loading