I'm working on a screen saver with (optional) audio. But whenever i deactivate it by swiping the mouse, the audio keeps playing for a few more seconds. This does not happen when clicking preview in sys preferences though. My theory is that when moving the mouse in preview, the screensaver process gets instantly killed, but not when it's not running it as preview. Is there a way i can sense mouse activity and stop the audio myself by running a function? I have supplied some code below: https://gist.github.com/MaxTechnics/3d4280fabc4da53b6df1022864d1bf23 Will provide more if requested. Thanks in advance!
Updated: this is what i think is the main part of the problem since the audio never stops in time
// MARK: - Lifecycle
extension VideoView {
override func startAnimation() {
super.startAnimation()
manager.player.play()
}
override func stopAnimation() {
super.stopAnimation()
manager.player.pause()
}
}
After some fiddling, while we can't intercept keyboard (and shouldn't to reliably detect a quit), it's actually possible to listen to some distributed notifications in the screensaver that tells us when the user gets logged in, and use that to call our code to stop animations/sound, as there's a sizable lag between the moment we are effectively killed and the moment the user gets back to desktop.
I did find some events in this answer here : Monitoring Screensaver Events in OSX and while it's a bit dated those events are still fired and can be intercepted inside the screensaver despite the sandboxing. I did found a few new ones though that may be interesting for other purposes.
Anyway, here's what's working for me :
class AerialView: ScreenSaverView {
...
func setNotifications {
DistributedNotificationCenter.default.addObserver(self, selector: #selector(AerialView.willStop(_:)), name: Notification.Name("com.apple.screensaver.willstop"), object: nil)
}
@objc func willStop(_ aNotification: Notification) {
NSLog("############ willStop")
// Put your stop audio/animation code here
}
}
(you will have to call setNotifications yourself or put that code somewhere else).
In practice, there are multiple events that are fired one after the other when the screensaver exits:
com.apple.screensaver.willstop
com.apple.screensaver.didstop
com.apple.screenIsUnlocked
com.apple.screenLockUIIsShown
Here's some logging of those events
2021-04-27 14:52:00.564 : ############ screenLockUIIsShown
2021-04-27 14:52:01.873 : ############ screenIsUnlocked
2021-04-27 14:52:01.873 : ############ willStop
2021-04-27 14:52:01.879 : ############ didStop
The first event was when I pressed a key, about 1.5s later the watch unlock unlocked the screen and you can see the 3 events being fired in succession.
The actual termination of the screensaver only happened at 14:52:07 in that case.
For completeness, some new events to detect when the login UI is up and dismissed (could be useful for some other things). This is in 11.4 and I have no idea if those events are fired in previous versions of macOS. In firing order :
com.apple.shieldWindowRaised
com.apple.screenIsLocked
com.apple.screenLockUIIsShown
com.apple.screenLockUIIsHidden
com.apple.shieldWindowLowered
com.apple.screenIsUnlocked