diff --git a/DynamicIsland/components/Extensions/ExtensionLockScreenWidgetView.swift b/DynamicIsland/components/Extensions/ExtensionLockScreenWidgetView.swift index f12625f..32863b4 100644 --- a/DynamicIsland/components/Extensions/ExtensionLockScreenWidgetView.swift +++ b/DynamicIsland/components/Extensions/ExtensionLockScreenWidgetView.swift @@ -114,8 +114,10 @@ struct ExtensionLockScreenWidgetView: View { @ViewBuilder private func liquidGlassBackground(shape: RoundedRectangle) -> some View { - LiquidGlassBackground(variant: requestedLiquidGlassVariant, cornerRadius: descriptor.cornerRadius) { - Color.black.opacity(0.08) + TimelineView(.periodic(from: .now, by: 1.0 / 30.0)) { context in + LiquidGlassBackground(variant: requestedLiquidGlassVariant, cornerRadius: descriptor.cornerRadius, trigger: context.date.timeIntervalSinceReferenceDate) { + Color.black.opacity(0.08) + } } .overlay { tintOverlay(shape: shape) diff --git a/DynamicIsland/components/LockScreen/LockScreenMusicPanel.swift b/DynamicIsland/components/LockScreen/LockScreenMusicPanel.swift index 6e796e8..590a212 100644 --- a/DynamicIsland/components/LockScreen/LockScreenMusicPanel.swift +++ b/DynamicIsland/components/LockScreen/LockScreenMusicPanel.swift @@ -781,8 +781,10 @@ struct LockScreenMusicPanel: View { @ViewBuilder private var customLiquidPanelBackdrop: some View { - LiquidGlassBackground(variant: musicGlassVariant, cornerRadius: panelCornerRadius) { - Color.white.opacity(0.04) + TimelineView(.periodic(from: .now, by: 1.0 / 30.0)) { context in + LiquidGlassBackground(variant: musicGlassVariant, cornerRadius: panelCornerRadius, trigger: context.date.timeIntervalSinceReferenceDate) { + Color.white.opacity(0.04) + } } .frame(maxWidth: .infinity, maxHeight: .infinity) .allowsHitTesting(false) @@ -860,8 +862,10 @@ struct LockScreenMusicPanel: View { @ViewBuilder private func customLiquidAlbumArtBackground(cornerRadius: CGFloat) -> some View { - LiquidGlassBackground(variant: musicGlassVariant, cornerRadius: cornerRadius) { - Color.clear + TimelineView(.periodic(from: .now, by: 1.0 / 30.0)) { context in + LiquidGlassBackground(variant: musicGlassVariant, cornerRadius: cornerRadius, trigger: context.date.timeIntervalSinceReferenceDate) { + Color.clear + } } } diff --git a/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift b/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift index def01d6..f59c898 100644 --- a/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift +++ b/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift @@ -136,8 +136,10 @@ struct LockScreenTimerWidget: View { } private var customLiquidBackground: some View { - LiquidGlassBackground(variant: timerGlassVariant, cornerRadius: Self.cornerRadius) { - Color.black.opacity(0.12) + TimelineView(.periodic(from: .now, by: 1.0 / 30.0)) { context in + LiquidGlassBackground(variant: timerGlassVariant, cornerRadius: Self.cornerRadius, trigger: context.date.timeIntervalSinceReferenceDate) { + Color.black.opacity(0.12) + } } } diff --git a/DynamicIsland/helpers/LiquidGlassBackground.swift b/DynamicIsland/helpers/LiquidGlassBackground.swift index b8d8f7f..f251b47 100644 --- a/DynamicIsland/helpers/LiquidGlassBackground.swift +++ b/DynamicIsland/helpers/LiquidGlassBackground.swift @@ -44,7 +44,7 @@ public struct LiquidGlassBackground: NSViewRepresentable { private let content: Content private let cornerRadius: CGFloat private let variant: LiquidGlassVariant - + private let trigger: Double /// Creates a new liquid‑glass container. /// - Parameters: /// - variant: Any ``LiquidGlassVariant`` (0–19). Defaults to `.v11`, which is visually super pleasing @@ -53,10 +53,12 @@ public struct LiquidGlassBackground: NSViewRepresentable { public init( variant: LiquidGlassVariant = .defaultVariant, cornerRadius: CGFloat = 10, + trigger: Double = 0, @ViewBuilder content: () -> Content ) { self.variant = variant self.cornerRadius = cornerRadius + self.trigger = trigger self.content = content() } @@ -130,5 +132,11 @@ public struct LiquidGlassBackground: NSViewRepresentable { } nsView.setValue(cornerRadius, forKey: "cornerRadius") callPrivateVariantSetter(on: nsView, value: variant.rawValue) + + // Micro-jitter: imperceptibly adjust opacity so WindowServer re-samples the wallpaper every frame. + let jitter = sin(trigger * 100) * 0.000001 + nsView.alphaValue = 1.0 - CGFloat(abs(jitter)) + + nsView.needsDisplay = true } } diff --git a/DynamicIsland/managers/LockScreenDisplayContextProvider.swift b/DynamicIsland/managers/LockScreenDisplayContextProvider.swift index 933f172..e181185 100644 --- a/DynamicIsland/managers/LockScreenDisplayContextProvider.swift +++ b/DynamicIsland/managers/LockScreenDisplayContextProvider.swift @@ -1,4 +1,5 @@ import AppKit +import CoreGraphics struct LockScreenDisplayContext { let screen: NSScreen @@ -30,7 +31,7 @@ final class LockScreenDisplayContextProvider { @discardableResult func refresh(reason: String) -> LockScreenDisplayContext? { - guard let screen = NSScreen.main ?? NSScreen.screens.first else { + guard let screen = preferredLockScreen() else { context = nil return nil } @@ -52,6 +53,29 @@ final class LockScreenDisplayContextProvider { return refresh(reason: "snapshot-miss") } + private func preferredLockScreen() -> NSScreen? { + if let builtin = NSScreen.screens.first(where: { screen in + guard let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else { + return false + } + return CGDisplayIsBuiltin(CGDirectDisplayID(number.uint32Value)) != 0 + }) { + return builtin + } + + let mainDisplayID = CGMainDisplayID() + if let mainScreen = NSScreen.screens.first(where: { screen in + guard let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else { + return false + } + return CGDirectDisplayID(number.uint32Value) == mainDisplayID + }) { + return mainScreen + } + + return NSScreen.main ?? NSScreen.screens.first + } + private func registerObservers() { screenChangeObserver = NotificationCenter.default.addObserver( forName: NSApplication.didChangeScreenParametersNotification,