has NSProcessInfoActivity to disable app nap changed on high sierra?

I just heard back from Ben Bird of bensoftware who I emailed earlier when I found a forum post from him that he had done something to fix it for his app. Ben is a class act i’ve spoken to him before and I like the quality of his software :wink: evidently it has nothing to do with App Nap at all but is a thread contention issue in Apple’s code when making UI updates while the screen is asleep. It blocks your main thread until the screen wakes up, so I’ve been barking up the totally wrong tree by fighting with App Nap settings!

He suggests that the solution is to subscribe to the screen events which I think i can do through some MBS plugins and notification events stuff, and stop all UI updates if thats the case. He also has a background thread that watches for some repeating update in the main thread and if it stops for too long then he programmatically wakes up the screen to force things back to normal. I am about to try experimenting with some of that right now. Even while I’m writing this I can see that my last experiment in looking for idle sleep did not catch anything and the app is hosed right now. If I recall properly there is an “your window is occluded” event that you can get that should fire when the window is behind everything or when the screen is turned off. I wonder if turning the window invisible at that point and back to visible when it turns off will be enough. I will post again if that works or not and if anyone else has any wisdom to add that would be great. Ben says Apple has “acknowledged the problem” and so expects a real fix at some point, but who can wait for that?

Two options, I see.

First is to try your GUI application as a background application, by adding “LSUIElement = 1” to the application plist. The icon doesn’t appear in the dock (but you can use API for that), the main thing is that it doesn’t qualify for App Nap, I know the issue isn’t directly because of App Nap, but it’s a fairly simple (ho ho ho) thing to try.

It seems to me like the most viable solution is to remove all logic from the GUI app, run yet another console application to manage the logic and others, then when the GUI has time, it probes the ‘controller’ for the latest information. Very cumbersome IMHO, but it should prevent this and any possible App Nap related issues in the future.

The fun part is that I could actually do that with this app. It was originally built with the idea of connecting one app as a remote interface to another running elsewhere. That interface has been coopted for other things, but I could revisit it as most of the infrastructure is there. I might actually do that at some point in the future anyway so as to make it run on the raspberry pi or something like that with full remote interfaces. But not as a solution to this problem. By the time I got it all working Apple will have fixed what is a rather large hole in their own code. Probably… Thats the real solution to keep bugging Apple till it’s fixed.

I tested with the 2 suggestions I mentioned above overnight. It didn’t work unfortunately. When I received an event from the system that the screen was going into idle sleep I walked my windows array and called .hide on all of them, and when I received the event telling me the screen was waking up I walked it again and called .show. Additionally I created a background thread that watched a microseconds value set in a timer in the main thread. If it got more than 5 seconds behind I would make the call that I was performing a user activity to wake the screen. I was skeptical about the thread being able to wake up the app once it had hung as in my experience if the main thread is blocked then the other threads don’t get any time either.

So I’m looking at a couple of other things to try. Instead of just hiding my windows I can save off what they are and actually close them. In the case of document windows with unsaved changes that would be tricky and require that I write a lot of code to save off that data and restore it. I could also write a terminal app to send a ping to the shell class every second or so and if it doesn’t get a response then do the call to the user activity method that wakes the screen. Assuming you can even say you’re a terminal app and are doing a user activity, I don’t know about that. Terminal apps are not affected as they have no gui. I’ll experiment with that last one this morning. The other easy “solution” would be to just respond to every event that the screen was going off with a call to turn it back on again. That would be so nasty but would only take 5 minutes of coding and testing. I’m going to see first if I can get a terminal app to wake the screen or not.

Initial test with a terminal app shows it has no problem waking the screen, so thats no problem. I am using the MBS plugins but I’m sure it could be done by declares as well. I am using just this code in my test app:

Dim theID As Integer
Dim err As Integer = IOPMAssertionMBS.DeclareUserActivity( "waking screen for thread contention", IOPMAssertionMBS.kIOPMUserActiveLocal, theID)

And that definitely wakes the screen even from a terminal app. Writing a terminal program that just sends me a ping up through the pipe every so often and responds by issuing that command if it doesn’t get a response, and a shell to run it in should not take long. It will mean bloating my application by the size of another terminal app but thats a drop in the bucket at this point. I am so over screwing around with this anymore.

That’s a awesome that you’ve figured this out; only thing I’d say given the recent battery fiasco, is to either give your customers an option to use this or warn them that your app needs to do this to function correctly because of a bug in the macOS.

You don’t want to freak a user out or make him think that your app is causing problems by waking the screen up all the time.