I want to simulate the pressing of Fn (Globe)
+ Control
+ arrow keys combo, but I’m encountering an issue where the Fn
modifier seems to be ignored, and the system behaves as if only Control
+ arrow keys are pressed.
In macOS, the combination of Fn
+ Right Arrow
(key code 124
) is treated as End
(key code 119
), and even that didn’t have expected behavior. Instead of moving the window to the right edge of the screen on Sequoia, it switches to the next space, which is the default behavior for Control
+ Right Arrow
.
Demo:
(I included Fn
+ Control
+ C
to show that centering the window works for example.)
import SwiftUI
@main
struct LittleKeypressDemo: App {
var body: some Scene {
Window("Keypress Demo", id: "keypress-demo") {
ContentView()
}
.windowStyle(.hiddenTitleBar)
.windowResizability(.contentSize)
.windowBackgroundDragBehavior(.enabled)
}
}
struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
KeyPressButton(icon: "arrowtriangle.right.fill", keyCode: 124)
KeyPressButton(icon: "arrow.down.to.line", keyCode: 119)
KeyPressButton(label: "C", keyCode: 8)
}
.padding()
}
}
struct KeyPressButton: View {
let icon: String?
let label: String?
let keyCode: CGKeyCode
init(icon: String? = nil, label: String? = nil, keyCode: CGKeyCode) {
self.icon = icon
self.label = label
self.keyCode = keyCode
}
var body: some View {
Button(action: { simulateKeyPress(keyCode) }) {
HStack {
Image(systemName: "globe")
Image(systemName: "control")
if let icon = icon {
Image(systemName: icon)
} else if let label = label {
Text(label)
}
}
}
.buttonStyle(.bordered)
.controlSize(.large)
}
}
func simulateKeyPress(_ keyCode: CGKeyCode) {
let fnKey = VirtualKey(keyCode: 63, flags: .maskSecondaryFn)
let controlKey = VirtualKey(keyCode: 59, flags: [.maskControl, .maskSecondaryFn])
let targetKey = VirtualKey(keyCode: keyCode, flags: [.maskControl, .maskSecondaryFn])
[fnKey, controlKey, targetKey].forEach { $0.pressAndRelease() }
}
struct VirtualKey {
let keyCode: CGKeyCode
let flags: CGEventFlags
func pressAndRelease() {
postKeyEvent(keyDown: true)
postKeyEvent(keyDown: false)
}
private func postKeyEvent(keyDown: Bool) {
guard let event = CGEvent(keyboardEventSource: nil, virtualKey: keyCode, keyDown: keyDown) else { return }
event.flags = flags
event.post(tap: .cghidEventTap)
}
}
Expected behavior:
Simulating the key combo Fn
+ Control
+ Right Arrow
on macOS Sequoia should move the current window to the right half of the screen, instead of switching to the next desktop space.
Questions:
Is CGEventFlags.maskSecondaryFn
enough to simulate the Fn
key in combination with Control
and the arrow keys? Are there alternative approaches or workarounds to correctly simulate this behavior? What’s the obvious thing I missing?
(Btw., window management is completely irrelevant here. I’m specifically asking about simulating these key presses.)
Any insights or suggestions would be greatly appreciated.
Thank you.