TASEmulators / BizHawk

BizHawk is a multi-system emulator written in C#. BizHawk provides nice features for casual gamers such as full screen, and joypad support in addition to full rerecording and debugging tools for all system cores.
http://tasvideos.org/BizHawk.html
Other
2.19k stars 382 forks source link

BizHawk does not inhibit display or system sleep while running #3644

Closed koitsu closed 1 year ago

koitsu commented 1 year ago

Summary

BizHawk does not inhibit display or system sleep while running. This is a common problem that plagues even AAA-gaming-industry titles, so don't feel bad.

Repro

Demonstrating lack of display sleep inhibition:

  1. Enable display sleep in Windows. In Windows 10: System -> Power and Sleep -> Screen -> When plugged in, turn off after: N minutes (example: 2 minutes)
  2. Launch BizHawk + load/run a game
  3. Wait 2 minutes
  4. Display will go into sleep mode

Solutions

There's 3 main methodologies you can use. I am not familiar with BizHawk's code so I don't know which is easiest to implement.

It's important to give this feature a GUI setting/toggle somewhere (probably under Config -> Customize -> General or Advanced), and the feature default to disabled (since that's how BizHawk has been behaving all this time).

Newer method (supported by Windows 7 onward)

Leverage the Windows Power Management API, specifically functions PowerCreateRequest() and PowerSetRequest() for a RequestType of PowerRequestDisplayRequired (inhibit display sleep) or PowerRequestSystemRequired (inhibit system sleep).

You can verify things are inhibited by launching a Command Prompt as Administrator and using powercfg.exe /requests. You should see the BizHawk process listed under DISPLAY, with the line "Video Wake Lock" under it. (For PowerRequestSystemRequired, it would be under SYSTEM).

If you want to see how this manifests today, run Chrome and watch a video somewhere (YouTube, Twitch, etc.): you'll find chrome.exe listed under DISPLAY and SYSTEM (thus, inhibiting display and system sleep).

Older method 1 (supported by Windows XP onward)

Use SetThreadExecutionState(). This is described at the end of the nesdev forum thread mentioned below.

Older method 2 (supported by Windows 2000 onward)

In your main window handler, add support for handling the WM_SYSCOMMAND message to detect SC_SCREENSAVE or SC_MONITORPOWER messages:

case WM_SYSCOMMAND:
    if (running && (((wParam & 0xFFF0) == SC_SCREENSAVE) || ((wParam & 0xFFF0) == SC_MONITORPOWER)))
        return 0;
    // otherwise, proceed to DefWindowProc

You cannot verify this method using powercfg.exe /requests, given that window message handlers do not "register" with the Power Management API, but it (obviously) does work.

I forget how to inhibit system sleep using this method.

SDL2 and/or Linux

There is discussion of how to implement this functionality in SDL2 and/or Linux in a nesdev forum thread I started nearly 10 years ago.

Important note

Please DO NOT implement a solution by changing system settings (Windows power profiles). I've lectured several emulator authors about this in the past. It is not the right solution and is extremely inappropriate to do, plus creates numerous problems in the case of an application crash or force-kill.

Host environment

CasualPokePlayer commented 1 year ago

To be clear, we do not support Windows XP and below at all (and Windows 7-8.1 is ""unsupported""), so the older solutions wouldn't really be applicable to us.

On Linux usage, going off SDL's source, it seems XScreenSaverSuspend/XResetScreenSaver or (if wayland is ever supported) zwp_idle_inhibit_manager_v1_create_inhibitor/zwp_idle_inhibitor_v1_destroy is the "correct" API to use. Although in that forum post, Near seems to note that XScreenSaverSuspend "does not work" (odd then that SDL would use it?), I suppose then they might have not checked if that extension was available (I would guess it wasn't), or maybe it was just bugged for them.

nattthebear commented 1 year ago

I'd say this is working as intended. If the user is interacting with the game and making inputs, then we won't go to screensaver, just like any other application. If the user is not interacting with the game, then we should go to screensaver just like any other application.

The applications that should call these special APIs are ones where the user might spend a long time passively viewing the screen without making any inputs. Video players, sure, but how does an emulator fit into that?

YoshiRulz commented 1 year ago

I think your PC can still go to sleep if you play with gamepad only.

nattthebear commented 1 year ago

That seems to be an OS bug, it should have the high level responsibility of enumerating input devices and counting, across all of them, what counts as idle or not.

koitsu commented 1 year ago

@nattthebear Your argument is ridiculous.

koitsu commented 1 year ago

I will not be commenting on this ticket going forward. BizHawk team can decide what to do with it. I've done all I can to present the technical information on how to solve this, and advocating for the behaviour to default to off (to remain compatible with pre-existing approach). Happy hacking!

CasualPokePlayer commented 11 months ago

To be clear, this (I guess unintentionally) is now implemented in practice with SDL2 now (or well, since 78f5e755343d279f6c05821751f14cea021b7672) being used for the OpenGL and D3D9 display methods, as on SDL video init, SDL will disable the screensaver by default.

This probably should be adjusted in the future so we have some setting to un-disable the screensaver / give user control over this.

EDIT: There's apparently also already code in BizHawk (present since 2011?) which appears to be intended to suppress the screensaver: https://github.com/TASEmulators/BizHawk/blob/20defc890bf4a257a057acd8f997ca81d3a3da11/src/BizHawk.Client.EmuHawk/ScreenSaver.cs

Although it's Windows only and I don't know if it actually works (the fact that this issue exists probably indicates that it doesn't even work).

YoshiRulz commented 11 months ago

SDL has an implementation for Linux but it doesn't seem to be working. (edit: This is in Plasma X11.) Firefox will trigger what KDE Plasma calls "Presentation Mode" when playing video (there's an additional global sleep toggle which seems to serve the same purpose, not sure what the deal is there), but EmuHawk dev builds do not.

CasualPokePlayer commented 11 months ago

I suspect Linux might not work for all cases or maybe just requires some extra package (likely would need the libxss1 package installed)

YoshiRulz commented 11 months ago

It is "installed" (there's a libXScrnSaver in SDL2's closure which provides libXss.so.1.0.0). It's definitely not inhibiting sleep. I'll add this to #1430.

CasualPokePlayer commented 11 months ago

Not sure what you're talking about "closure", it's just calling a function pointer it gets from dlopen'ing that .so, and it doesn't do that if it's not available.

YoshiRulz commented 11 months ago

It's Nix jargon, referring to the set of rundeps including transitive deps.

nattthebear commented 10 months ago

I guess unintentionally

I never had a particularly strong opinion about this one way or the other. There was only one person advocating for, and in all of their posts they never gave a single reason why this should be done, only how. It didn't seem that useful to me, and in the absence of any other advocates, I just closed it.

CasualPokePlayer commented 10 months ago

fwiw, the fact we already had code to do this (but it seemed to just be broken) seems to mean SDL has fixed what was supposed to be happening (well, at least for Windows, Linux has seemingly gone from unimplemented to some broken implementation?).