Possible to run an .exe while minimized?

I’m working on porting a game to Steam in the idle/clicker genre. The kind where you can check in now and then while it “plays” itself.

However, my app will automatically pause when I minimize it on windows. I’d like the game to play regardless of window state. I know this is possible to achieve, because I’ve seen other steam games run while minimized.

Am I looking at some kind of threading solution? Like mentioned here: http://www.openfl.org/blog/2015/10/06/october-update/

I was hoping for a flag I could just stick in my application.xml file, but I’m guessing it’s not going to be that easy. Also, I’m currently locked into openFL 3.6.1 + lime 2.9.1

tl;dr: I don’t think I found anything relevant.


I did a little digging in Lime and tracked update events to SDLApplication::HandleEvent(), which is called from SDLApplication::Update().

So already we have a few reasons this could be failing:

if (active && (firstTime || WaitEvent (&event)))

firstTime only applies once, so that isn’t it, but maybe active is false or WaitEvent() fails. But it turns out that active is true for the entire lifespan of the app, so that isn’t it either.

What about WaitEvent()?

Based on the documentation, this looks mostly fine. PumpEvents() adds the latest events to the queue, then PeepEvents() reads from the queue.

It’s odd that they chose not to use the existing function, but this looks like it would work.


So the next question is, how do USEREVENT events get added to the queue? If you recall, those are the ones that turn into update events, and SDL certainly doesn’t dispatch them. Turns out, it’s the OnTimer() function, which is called from the end of SDLApplication::Update().

Since OnTimer() only queues an event and Update() doesn’t check the queue at the end, the USEREVENT will wait until the start of the next Update() call. However, if somehow it got cleared from the queue, then Update() could get stuck on that first WaitEvent() call, never calling OnTimer(). It would stay this way until a WINDOW_MAXIMIZED event allowed WaitEvent() to finish.

That said, I’m not sure if that situation could happen. The only way to set it up is by calling PeepEvent(), PollEvent(), or WaitEvent() outside of Update(), and there’s no opportunity for that. Not unless it happens on a separate thread, but (1) I can’t find any instances of that, and (2) the problem would be way less consistent if threads were involved.

Some pretty dense stuff here.

Maybe you’re already a couple steps ahead of me, but do we know of a situation in which SDL_PeepEvents would return -1, causing WaitEvent to return -1, halting the main loop? Or does PeepEvents simply return -1 when there are no events left in the queue?

Outside of that, are you saying that the WINDOW_MINIMIZED event is somehow translated into ‘don’t update?’ I didn’t notice any instance of WINDOW_MINIMIZED or MAXIMIZED in the code base.

Was lime written entirely by @singmajesty? Would he have more insight?

From the old documentation, it returns -1 in the case of an error and otherwise returns the number of events found.

That’s what I was looking for evidence of, but it doesn’t look like it.

It’s actually SDL_WINDOW_MINIMIZED; I cut off the “SDL_” for brevity. I think the reason you couldn’t find it is because GitHub’s search only returns whole-word matches.

I don’t think he wrote the whole thing, but he certainly knows more about it than I do.

Lib.current.stage.pauseWhenDeactivated = false;
?

That’s probably it. Guess I should have looked at OpenFL implementation rather than assuming it was lower-level.

1 Like

Had me excited for a minute… that line compiles just fine, but doesn’t seem to do anything for the final build? Still playing around.

EDIT: Some interesting observations:

pauseWhenDeactivated does not show up in my autocomplete list at all, like the other stage properties. However I can still read/write it, and I can confirm that the value stays to what I set it.

What’s interesting is that I’m compiling in legacy mode:
<set name="openfl-legacy" />

If I deactivate legacy mode, accessing the property just returns null. Attempting to set it to true or false results in a compiler error.

But we can still clearly see the property is referenced in the Stage class, shown here: https://github.com/jgranick/openfl-native/blob/master/flash/display/Stage.hx

More digging: I can confirm that the pauseWhenDeactivated property just doesn’t exist in openFL next, at least as of version 3.6.1. That explains why it won’t compile with legacy turned off. Which is fine, because I need legacy anyway.

That being said, still not sure why the property doesn’t actually seem to do anything. I even tried modifying the openFL source code to initialize the property itself to false, but nothing changed.

It definitely ought to affect when the next frame happens

Maybe try adding a trace statement to see when that line is run?

I think I have a solution here. I’m a little nervous, because I’ve never really modified source like this before. Also screwing up the activate/deactivate/update cycle can have huge repercussions.

Anyway, I noticed this line in Stage.hx:

case 21: // etDeactivate
__setActive (false);

When it processed the stage event of ‘deactivate,’ it deactivates the whole application by calling __setActive(false);

I commented out this line, and sure enough, the app ran in the background without pausing like expected. This was too broad of a stroke however- we still want most of the logic in __setActive(false) to process, because it includes re-broadcasting the deactivate event for other scenarios (and I do process a bit of logic when the deactivate event is called in my main app)

So instead I made two small changes to make proper use of the pauseWhenDeactivated field. First off we have this segment inside of __setActive:

if (!active) {
	__lastRender = Timer.stamp ();
        __nextRender = __lastRender + __framePeriod;
}

I’m not 100% sure what the effect of this is, but since it seems to have something to do with delaying the next render, I changed it to:

if (!active && pauseWhenDeactivated) {
		__lastRender = Timer.stamp ();
		__nextRender = __lastRender + __framePeriod;
}

So now that will only be executed if we call setActive(false) AND we still want to pause when deactivated

Lastly, I just added this line to the end of the function right before it exits:

if(!pauseWhenDeactivated)active = true;

so while we may still set the ‘active’ field to false to process some logic inside the function, the end result is that active will always remain true if pauseWhenDeactivated is false. Are there additional repercussions for doing something like this? … maybe? but everything seems to run fine so far :x

Sorry. Yes, I used this a long ago (like 2 years ago) in legacy, but I forgot that I had a modified Stage.hx.

I modified the __render function like this so you still do the broadcast of events

@:noCompletion public function __render (sendEnterFrame:Bool):Void {

#if (!custom_dont_pause_on_deactivate)
		if (!active) {

		return;

		}
    #end
		if (sendEnterFrame) {

			__broadcast (new Event (Event.ENTER_FRAME));

		}
#if custom_dont_pause_on_deactivate
		if (!active) {

			return;

		}
#end
		if (__invalid) {

			__invalid = false;
			__broadcast (new Event (Event.RENDER));

	}

	lime_render_stage (__handle);

	}

Interesting- so your code just ensures that the enter frame event continues to broadcast, while mine stops the game from being ‘inactive’ on window minimized. Both are kind of hacky solutions, I wonder which is safer : ( Yours seems to have a smaller impact on the overall process.

Hey guys

I did write Lime 2

I don’t remember how much of the pause logic is in SDL

Would it be possible to allow sleep in the background, then handle the time change when you return? Do you just need music when you’re minimized?

Nah, I need the whole game to process unfortunately. Since it has ‘set and forget’ gameplay, our users will definitely want to minimize and come back.

Do the psuedo-solutions posted by either myself or sea_jackal seem safe enough?