mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-11-10 04:46:07 -08:00
fix(controller): pick up macOS presses while mpv is in front (#321)
This commit is contained in:
1 parent
fa3051f9cb
commit
8e29cb3c55
17 files changed
+614
-63
No files matched your search
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env swift
|
||||
//
|
||||
// macos-controller-watch.swift
|
||||
// SubMiner - Report controller button presses on macOS while SubMiner is in the background
|
||||
//
|
||||
// macOS only delivers GameController input to the frontmost app, so the overlay sees no
|
||||
// presses while mpv is in front. This helper opts in to background events and prints one
|
||||
// line per press so SubMiner can focus the overlay and replay the press there.
|
||||
//
|
||||
// Usage: macos-controller-watch (long-running; exits when stdin closes)
|
||||
// Output: "press <index> <connected pads>" per button press, using the W3C standard gamepad
|
||||
// button indices. SubMiner only replays a press when one pad is connected: nothing identifies
|
||||
// a pad to both GameController and Chromium, so with several it could hit the wrong one.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import Foundation
|
||||
import GameController
|
||||
|
||||
private func emit(_ line: String) {
|
||||
FileHandle.standardOutput.write(Data((line + "\n").utf8))
|
||||
}
|
||||
|
||||
/// Standard gamepad mapping (https://w3c.github.io/gamepad/#remapping), matching the
|
||||
/// indices Chromium reports for the same pad.
|
||||
private func standardButtons(_ pad: GCExtendedGamepad) -> [(Int, GCControllerButtonInput?)] {
|
||||
return [
|
||||
(0, pad.buttonA),
|
||||
(1, pad.buttonB),
|
||||
(2, pad.buttonX),
|
||||
(3, pad.buttonY),
|
||||
(4, pad.leftShoulder),
|
||||
(5, pad.rightShoulder),
|
||||
(6, pad.leftTrigger),
|
||||
(7, pad.rightTrigger),
|
||||
(8, pad.buttonOptions),
|
||||
(9, pad.buttonMenu),
|
||||
(10, pad.leftThumbstickButton),
|
||||
(11, pad.rightThumbstickButton),
|
||||
(12, pad.dpad.up),
|
||||
(13, pad.dpad.down),
|
||||
(14, pad.dpad.left),
|
||||
(15, pad.dpad.right),
|
||||
(16, pad.buttonHome),
|
||||
]
|
||||
}
|
||||
|
||||
private func connectedPadCount() -> Int {
|
||||
return GCController.controllers().filter { $0.extendedGamepad != nil }.count
|
||||
}
|
||||
|
||||
private func watch(_ controller: GCController) {
|
||||
guard let pad = controller.extendedGamepad else { return }
|
||||
for (index, button) in standardButtons(pad) {
|
||||
button?.pressedChangedHandler = { _, _, pressed in
|
||||
if pressed {
|
||||
emit("press \(index) \(connectedPadCount())")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit with SubMiner: it holds our stdin open for as long as it runs.
|
||||
private func exitWhenStdinCloses() {
|
||||
Thread.detachNewThread {
|
||||
while !FileHandle.standardInput.availableData.isEmpty {}
|
||||
exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
let app = NSApplication.shared
|
||||
app.setActivationPolicy(.prohibited)
|
||||
GCController.shouldMonitorBackgroundEvents = true
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: .GCControllerDidConnect,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { notification in
|
||||
if let controller = notification.object as? GCController {
|
||||
watch(controller)
|
||||
}
|
||||
}
|
||||
GCController.controllers().forEach(watch)
|
||||
|
||||
exitWhenStdinCloses()
|
||||
app.run()
|
||||
Reference in new issue
Block a user