You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Musubi/Packages/MusubiKit/Sources/VideoOutput/DisplayModeSwitcher.swift

56 lines
2.7 KiB

import CoreGraphics
import MusubiCore
import OSLog
/// Switches a display to a target refresh rate for the lifetime of the app and restores it on request.
public enum DisplayModeSwitcher {
private static let log = Logger(subsystem: "studio.zielke.musubi", category: "display")
/// Returns the refresh rate in effect afterwards, and the original mode to restore later (nil if unchanged).
@discardableResult
public static func setRefreshRate(_ hz: Double, on display: CGDirectDisplayID) -> (refreshRate: Double, original: CGDisplayMode?) {
guard let current = CGDisplayCopyDisplayMode(display) else { return (0, nil) }
let options = [kCGDisplayShowDuplicateLowResolutionModes: true] as CFDictionary
let modes = (CGDisplayCopyAllDisplayModes(display, options) as? [CGDisplayMode]) ?? []
guard let target = DisplayModeSelection.mode(matching: hz, current: info(current), available: modes.map(info)),
let mode = modes.first(where: { info($0) == target })
else {
if abs(current.refreshRate - hz) > 0.1 {
log.notice("Display \(display): no \(hz) Hz mode, staying at \(current.refreshRate) Hz")
}
return (current.refreshRate, nil)
}
guard apply(mode, to: display) else { return (current.refreshRate, nil) }
log.info("Display \(display): \(current.refreshRate) Hz → \(mode.refreshRate) Hz")
return (mode.refreshRate, current)
}
public static func restore(_ mode: CGDisplayMode, on display: CGDirectDisplayID) {
if apply(mode, to: display) {
log.info("Display \(display): restored \(mode.refreshRate) Hz")
}
}
public static func currentRefreshRate(of display: CGDirectDisplayID) -> Double {
CGDisplayCopyDisplayMode(display)?.refreshRate ?? 0
}
private static func apply(_ mode: CGDisplayMode, to display: CGDirectDisplayID) -> Bool {
var config: CGDisplayConfigRef?
guard CGBeginDisplayConfiguration(&config) == .success else { return false }
CGConfigureDisplayWithDisplayMode(config, display, mode, nil)
// .forAppOnly: macOS reverts the mode when Musubi quits or crashes.
let result = CGCompleteDisplayConfiguration(config, .forAppOnly)
if result != .success { log.error("Display \(display): mode change failed (\(result.rawValue))") }
return result == .success
}
private static func info(_ mode: CGDisplayMode) -> DisplayModeInfo {
DisplayModeInfo(width: mode.width, height: mode.height,
pixelWidth: mode.pixelWidth, pixelHeight: mode.pixelHeight,
refreshRate: mode.refreshRate)
}
}

Powered by TurnKey Linux.