Important screensaver tip for Windows emulator authors

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Important screensaver tip for Windows emulator authors
by on (#118407)
This is something I have seen happen over the past, oh I dunno, 10+ years in all sorts of games (many commercial and from very large companies): the games do not inhibit screen savers or monitor blanking (power-off) from occurring while the emulator/game is running.

The solution for this is incredibly simple and takes virtually zero CPU time. Some emulators like Nestopia implement this methodology. I thought I'd document it for those writing Win32-based emulators so that they know how simple it is to do this and what the proper methodology is. I recently had to describe it over on the Steam forums for the recent Angry Video Game Nerd game that came out yesterday:

http://steamcommunity.com/app/237740/di ... 769796832/

I hope this benefits folks here. Please implement this when doing a Windows-based emulator. If your programming language or environment doesn't give you this degree of control, then start hounding on the language or framework authors to provide it. Every Win32 application has a WndProc equivalent, so every Win32 application should be able to accomplish this -- just that many crappy frameworks and abstract garbage don't give you this degree of control, and for no justified reason.

If you need further workarounds I can provide one (it does involve changing the power scheme, I can provide code for such -- but I STRONGLY do not recommend it, as it makes changes to the users' profile without their consent, and if your game/program crashes it can leave the profile indefinitely with no blanking/screen saver configured, which is bad. The method I described in the above URL is indeed the best/proper solution).

HTH.
Re: An important tip for Windows emulator authors
by on (#118431)
In case that post gets deleted (or paywalled or moved to another URL with no redirect), may I reproduce that post somewhere?
Re: An important tip for Windows emulator authors
by on (#118435)
Sure thing. I wasn't sure what page on the Wiki would be most relevant for it, sadly. :(

And if needed I can add the code for the power profile adjustment I mentioned (though again, really should not do this).
Re: An important tip for Windows emulator authors
by on (#118448)
I really think the problem is that Windows doesn't treat gamepad input like it does keyboard and mouse input. It should also restart the screen saver counter, which would solve this problem for everyone. (And sure, ignore small analog movements since low-quality gamepads can stutter with no actual user input. Or even just ignore analog axes entirely.)

I never like 'solutions' that require every individual piece of software in a given category to implement the 'fix'. We should be putting the pressure on OS vendors to do the right thing as well.

That said, I'll share the Linux equivalent. Here, you want to use XTestFakeKeyEvent, and call it every few seconds. Nothing else is reliable. Sample code:

Code:
//call this once during each frame
//preferably only if your emulator has window focus
void supressScreenSaver() {
  //XSetScreenSaver(timeout = 0) does not work
  //XResetScreenSaver() does not work
  //XScreenSaverSuspend() does not work
  //DPMSDisable() does not work
  //XSendEvent(KeyPressMask) does not work
  //xdg-screensaver often isn't available
  //use XTest extension to send fake keypress every few seconds.
  //keycode of 255 does not map to any actual key,
  //but it will block screensaver and power management.
  Display *display = XOpenDisplay(0);
  XTestFakeKeyEvent(display, 255, True,  0);
  XTestFakeKeyEvent(display, 255, False, 0);
  XCloseDisplay(display);
}
Re: An important tip for Windows emulator authors
by on (#118456)
koitsu wrote:
And if needed I can add the code for the power profile adjustment I mentioned (though again, really should not do this).


Changing a user's settings without asking first, especially something like power settings, is never, ever acceptable IMO. If you include code to adjust these settings, the need to make the behavior opt-in can't be understated.
Re: An important tip for Windows emulator authors
by on (#118475)
In some cases it might be a cross-platform library such as SDL or whatever, so the programmer may not want to normally to include such things (unless the library used already has such a function; even then there ought to be a way to turn off the suppress screen saver function since it isn't always wanted).
Re: An important tip for Windows emulator authors
by on (#118507)
SDL has SDL_DisableScreensaver() which does what koitsu suggested for Windows. (I dunno what it does on other platforms.)
Re: An important tip for Windows emulator authors
by on (#118881)
Good link, i tought about this issue and were going to google it. Thanks!!
Re: An important tip for Windows emulator authors
by on (#118914)
There's actually two minor flaws in the sample code you posted on the Steam forums:
1. wParam needs to be ANDed with 0xFFF0 first.
MSDN wrote:
In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter are used internally by the system. To obtain the correct result when testing the value of wParam, an application must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator.

2. The checks should only be done when emulation is actually active, otherwise if you leave emulation paused and walk away it'll never go into power saving mode.

There's also the minor detail that if you've got Windows set to lock the workstation when the screen saver kicks in, it will ignore your program's request to not do so.
MSDN wrote:
If password protection is enabled by policy, the screen saver is started regardless of what an application does with the SC_SCREENSAVE notification—even if fails to pass it to DefWindowProc.


Thus, the correct code should probably look something like this (copy/pasted from Nintendulator):
Code:
   case WM_SYSCOMMAND:
      // disallow screen saver while emulating (doesn't work if password protected)
      if (running && (((wParam & 0xFFF0) == SC_SCREENSAVE) || ((wParam & 0xFFF0) == SC_MONITORPOWER)))
         return 0;
      // otherwise, proceed to DefWindowProc
Re: Important screensaver tip for Windows emulator authors
by on (#118917)
The following works even when Windows is set to lock the workstation:

Set a timer somewhere
Code:
SetTimer(hWnd, 0, 30000, NULL);


And in your WindowProc
Code:
   case WM_TIMER:
      switch (wParam)
      {
      case 0:
         INPUT input;
         input.type = INPUT_KEYBOARD;
         input.ki.wVk = 0x88; //Unassigned virtual keycode
         input.ki.dwFlags = KEYEVENTF_KEYUP;
         SendInput(1, &input, sizeof(INPUT));
         return 0;
      }


I suppose there is some risk that your choice of keycode conflicts with some other program that's using it, but I haven't had or heard of any problems so far.
Re: Important screensaver tip for Windows emulator authors
by on (#118918)
So you reset the timer by periodically sending a keypress through the Windows event loop. I can think of a couple improvements. One would be to send the keypress only when the emulator is focused, ensuring that the key code won't conflict. Another would be to send the keypress whenever a button is pressed on the joystick, keeping the screensaver working as expected should the user walk away from a paused game. But then I don't program in Win32 currently; are there problems with my suggestion?
Re: Important screensaver tip for Windows emulator authors
by on (#118919)
tepples wrote:
So you reset the timer by periodically sending a keypress through the Windows event loop. I can think of a couple improvements. One would be to send the keypress only when the emulator is focused, ensuring that the key code won't conflict. Another would be to send the keypress whenever a button is pressed on the joystick, keeping the screensaver working as expected should the user walk away from a paused game. But then I don't program in Win32 currently; are there problems with my suggestion?

Nope. All of these are fine suggestions.

re: SendInput focus. I think my concern was with applications that listen to all keyboard events (e.g., hotkey type stuff). Last time I did any testing with this was probably ~8 years ago, so I really have no idea. :lol:
Re: Important screensaver tip for Windows emulator authors
by on (#118920)
Then send something innocent like the down arrow or the spacebar or the X key, as that's what players would already be pressing when playing an NES game with a keyboard.

In any case, Windows Vista didn't exist 8 years ago, and your product requires it or newer.
Re: Important screensaver tip for Windows emulator authors
by on (#118923)
tepples wrote:
Then send something innocent like the down arrow or the spacebar or the X key, as that's what players would already be pressing when playing an NES game with a keyboard.

So every x seconds, the emulator would 'see' you pressing down. How does that make sense?

tepples wrote:
In any case, Windows Vista didn't exist 8 years ago, and your product requires it or newer.

In it's current incarnation, sure. Prior to 2.0, it ran on XP. But I guess you would know better. :roll:
Re: Important screensaver tip for Windows emulator authors
by on (#118927)
James wrote:
tepples wrote:
Then send something innocent like the down arrow or the spacebar or the X key, as that's what players would already be pressing when playing an NES game with a keyboard.

So every x seconds, the emulator would 'see' you pressing down. How does that make sense?

The window system would see a key being pressed, but the emulator would ignore it. Perhaps use a key that happens not to be bound to a game button.
Re: Important screensaver tip for Windows emulator authors
by on (#118928)
tepples wrote:
Then send something innocent like the down arrow or the spacebar or the X key, as that's what players would already be pressing when playing an NES game with a keyboard.

In any case, Windows Vista didn't exist 8 years ago, and your product requires it or newer.


Would the same effect be achieved by sending a mouse wheel event with delta of 0?
Re: Important screensaver tip for Windows emulator authors
by on (#118929)
cpow wrote:
Would the same effect be achieved by sending a mouse wheel event with delta of 0?

I just tested that and it does appear to work as well. Good call!
Re: Important screensaver tip for Windows emulator authors
by on (#118932)
James wrote:
cpow wrote:
Would the same effect be achieved by sending a mouse wheel event with delta of 0?

I just tested that and it does appear to work as well. Good call!

Nice! I knew I still had a few good ideas in me. :lol:
Re: An important tip for Windows emulator authors
by on (#119039)
Quietust wrote:
There's actually two minor flaws in the sample code you posted on the Steam forums:
1. wParam needs to be ANDed with 0xFFF0 first.
MSDN wrote:
In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter are used internally by the system. To obtain the correct result when testing the value of wParam, an application must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator.


You're right -- thanks, I've updated my post on Steam. I believe this also means the Nestopia code is wrong too (lacking & 0xfff0), but the updated unofficial version in git has it fixed.
Re: Important screensaver tip for Windows emulator authors
by on (#119061)
SDL2 has functions for disabling the screensaver, but does anyone know how it does it? Hopefully, it's the way mentioned in this thread and not by changing the power profile.
Re: Important screensaver tip for Windows emulator authors
by on (#119064)
Drag wrote:
SDL2 has functions for disabling the screensaver, but does anyone know how it does it?

Code:
    case WM_SYSCOMMAND:
        {
            /* Don't start the screensaver or blank the monitor in fullscreen apps */
            if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
                (wParam & 0xFFF0) == SC_MONITORPOWER) {
                if (SDL_GetVideoDevice()->suspend_screensaver) {
                    return (0);
                }
            }
        }
        break;
Re: Important screensaver tip for Windows emulator authors
by on (#119093)
Ah, excellent. Thanks for digging that up. It's only after this post that I remember that SDL is open source and I could've looked it up myself. D'oh!
Re: Important screensaver tip for Windows emulator authors
by on (#120868)
Who still use screensavers anyway?
Re: Important screensaver tip for Windows emulator authors
by on (#120869)
It's not just for screen savers anymore, darkhog, that's just what we call it now. It's about a timer on inactive input, and lots of computers are set to turn the monitor off or go to sleep after a certain time.
Re: Important screensaver tip for Windows emulator authors
by on (#120872)
Aren't these things like a mouse wheel movement of 0 just hacks, i.e. they will break at some point in the future? It's a sad situation that the OS would suspend a system even though a USB HID was generating input.
Re: Important screensaver tip for Windows emulator authors
by on (#120873)
Expanding on what rainwarrior wrote: The effort formerly put into screensavers has forked into two areas.

Screensaver as art: The display hacks common in the screensaver era appear to have folded into the demoscene.

Screensaver as blanking: With CRTs and plasma planels giving way to LCDs that have less of a burn-in problem, and with an increased focus on conserving electric power, it's not quite as necessary to waste computing cycles on rendering visual effects. But it's still common to have the system blank the screen when a terminal has become idle, lock the input devices, and possibly show a simple moving image to show that the computer is still in use. This improves security, as eavesdroppers have less of a chance to read sensitive information from the screen. It also gracefully leads into lower-power states: in monitor power save, the graphics processor cuts off the signal and the monitor turns off, and in suspend, everything but the DRAM refresh and the keyboard controller turns off.
Windows Forms implementation in C#
by on (#174557)
The only change needed to adapt it for your implementation is the condition:
Quote:
if (gameState == GameState.Running)


(edited - constant for invalid handle value added)

Code:
        /// <summary>
        /// Windows message handler
        /// </summary>
        /// <param name="message">Windows message to process</param>
        protected override void WndProc(ref Message message)
        {
            // check if screen saver message dispatched
            if (message.Msg == WM_SYSCOMMAND && (int)message.WParam == SC_SCREENSAVE)
            {
                // if game is running, suppress screen saver request
                if (gameState == GameState.Running)
                {
                    message.Result = INVALID_HANDLE_VALUE;
                    return;
                }
            }

            // fall back to default message processor
            base.WndProc(ref message);
        }

        // window message processing
        private static readonly int SC_SCREENSAVE = 0xF140;
        private static readonly int WM_SYSCOMMAND = 0x0112;
        private static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
Re: Important screensaver tip for Windows emulator authors
by on (#174582)
Hardcoding the NULL Pointer to -1 instead of just using NULL? I've seen it all, as long as that isn't how Microsoft tells you how to do it.
Re: Important screensaver tip for Windows emulator authors
by on (#174587)
3gengames wrote:
Hardcoding the NULL Pointer to -1 instead of just using NULL? I've seen it all, as long as that isn't how Microsoft tells you how to do it.


Apparently it's the managed code equivalent of the Win32 C++ constant INVALID_HANDLE_VALUE. It serves a different purpose than NULL.
Re: Important screensaver tip for Windows emulator authors
by on (#174592)
I should note there's also a "newer" way to to this (Windows 7 and up only; older Windows releases need to use the methodology already described) through Windows' Power Management API. Specifically I'm referring to PowerCreateRequest() and PowerSetRequest() for the PowerRequestDisplayRequired RequestType.

The reason I mention this is that use of this API to inhibit display blanking/power-down/sleep allows a user to do powercfg.exe -requests from the CLI and see which applications and/or drivers are inhibiting said condition.
Re: Important screensaver tip for Windows emulator authors
by on (#174598)
cool -- just tested the Power Management API and it seems to work well. Even with my workstation set to lock. Thanks!