Skip to content
Open
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
2 changes: 2 additions & 0 deletions SnowfallApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"SnowfallApp/Preview Content\"";
DEVELOPMENT_TEAM = 694AARYR2X;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
Expand Down Expand Up @@ -317,6 +318,7 @@
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"SnowfallApp/Preview Content\"";
DEVELOPMENT_TEAM = 694AARYR2X;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
Expand Down
2 changes: 2 additions & 0 deletions SnowfallApp/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
<true/>
<key>NSRequiresAquaSystemAppearance</key>
<false/>
<key>LSUIElement</key>
<true/>
</dict>
</plist>
47 changes: 46 additions & 1 deletion SnowfallApp/Sources/SnowfallApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var snowWindows: [NSWindow] = []

func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.setActivationPolicy(.accessory)
setupSnowWindows()
NotificationCenter.default.addObserver(self, selector: #selector(setupSnowWindows), name: NSApplication.didChangeScreenParametersNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(handleSpaceChange), name: NSWorkspace.activeSpaceDidChangeNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(handleSpaceChange), name: NSWorkspace.didActivateApplicationNotification, object: nil)
updateSnowVisibility()
}

@objc private func setupSnowWindows() {
Expand All @@ -42,6 +46,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
for screen in NSScreen.screens {
createSnowWindow(for: screen, in: globalRect)
}

updateSnowVisibility()
}

private func createSnowWindow(for screen: NSScreen, in globalRect: CGRect) {
Expand All @@ -56,7 +62,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
window.hasShadow = false
window.backgroundColor = .clear
window.level = .screenSaver
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary, .ignoresCycle, .transient, .stationary]
window.collectionBehavior = [.ignoresCycle, .transient, .stationary]
window.ignoresMouseEvents = true
window.isReleasedWhenClosed = false
window.setFrame(screenRect, display: true)
Expand All @@ -65,4 +71,43 @@ class AppDelegate: NSObject, NSApplicationDelegate {

snowWindows.append(window)
}

@objc private func handleSpaceChange() {
updateSnowVisibility()
}

private func updateSnowVisibility() {
let shouldHide = isAnyFullscreenWindowPresent()
for window in snowWindows {
if shouldHide {
window.orderOut(nil)
} else {
window.orderFront(nil)
}
}
}

private func isAnyFullscreenWindowPresent() -> Bool {
let options: CGWindowListOption = [.optionOnScreenOnly, .excludeDesktopElements]
guard let windowList = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]] else { return false }

let screenSizes = NSScreen.screens.map { $0.frame.size }
let tolerance: CGFloat = 2.0

for entry in windowList {
guard let layer = entry[kCGWindowLayer as String] as? Int, layer == 0,
let bounds = entry[kCGWindowBounds as String] as? [String: Any],
let width = bounds["Width"] as? CGFloat,
let height = bounds["Height"] as? CGFloat else { continue }

for screenSize in screenSizes {
let widthClose = abs(width - screenSize.width) <= tolerance
let heightClose = abs(height - screenSize.height) <= tolerance
if widthClose && heightClose {
return true
}
}
}
return false
}
}