SlimDX Joystick input breaks Sleep in Windows 10

Started by
5 comments, last by bbyam 7 years, 4 months ago

EDIT: It was discovered to be DirectInput, and not SlimDX

Since updating my PC to Windows 10, the PC never goes to Sleep when my application is running, and I've narrowed it down to a single call from SlimDX.

My application is a type of UI browser - think of Kodi or Steam Big Picture mode. It is designed to allow the computer to go to sleep when the interface is up, but not being used (through the normal Windows mechanism of Power Settings).

This was working just fine on Windows 7, but since upgrading to Windows 10 - and with no changes to my application - it no longer sleeps if a gamepad is plugged in (traditional gamepad, not XInput).

I've traced it down to a single call in SlimDX: Joystick.GetCurentState(). If I comment out this single line, leaving in the acquiring of the joysticks, the Poll(), as well as all the rest of my code, the computer then goes to sleep. Of course, the joystick doesn't work.

I threw together a quick pure DirectX C++ app that does the same thing, just runs, scans the joysticks, and prints changes to the state via IDirectInputDevice8::Poll() and IDirectInputDevice8::GetDeviceState(). The app works and shows joystick button presses and DPad movement, and it properly lets the computer sleep if there is no activity for the system sleep timeout time. So it appears this is entirely related to SlimDX on Windows 10.

Has anyone encountered this before? Is there any way to work around it without writing my own joystick input library to connect to my C# code?

Here's some possibly relevant info:

  • I'm using the joystick with cooperative level Nonexclusive | Background (did the same in my test C++ app)
  • I hacked together a quick and dirty Windows Raw Input test, that didn't parse the input but simply printed when a message was received. It shows messages when I push a button, but no Raw Input is received when the gamepads are idle.
  • Calling the Windows GetLastInputInfo() function says no input is received. That function returns the system tick count of the last input event across the whole system, and it doesn't change while the gamepads are idle, but the computer still doesn't sleep.
  • The only thing I got some info from was CallNtPowerInformation(SystemPowerInformation). One of the values returned from that is the number of seconds until the computer goes to sleep. When my app is running, that number does not decrease. When I comment out that single line (Joystick.GetCurrentState()), the counter then does decrease, and the computer goes to sleep when it hits 0.
Advertisement

It's neither an explanation nor an answer but you shouldn't be using Sleep calls to begin with - not even for this kind of program. Standard Windows messaging will be sufficient; GetMessage will block if there are no messages pending and will therefore accomplish what you want (I assume: do not chew up CPU time if there's no work to do).

Also be aware that Sleep calls don't send the entire computer to sleep, they only affect the current thread. So if another thread has work to do, the PC won't go to sleep and that other thread will quite cheerfully chew up CPU time. Another reason to not use Sleep but instead use the standard API calls in the documented, expected manner.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

You've misunderstood which 'sleep' the OP is talking about... this is not the 'sleep' function, but the computer powering down to a low power/off state.

Have you installed all relevant Windows 10 Updates? Some people have reported behaviour like this in other applications (including the screensaver not triggering any more), but also that it was fixed in a Windows update.

This is all GetCurrentState does.


    Result Joystick::GetCurrentState( JoystickState^% data )
    {
        DIJOYSTATE2 joystate;
        HRESULT hr = InternalPointer->GetDeviceState( sizeof( DIJOYSTATE2 ), &joystate );
        if( RecordError( hr ).IsFailure )
            return Result::Last;

        JoystickState^ state = safe_cast<JoystickState^>( data );
        state->AssignState(joystate);

        return Result::Last;
    }

It seems very unlikely that that could prevent the system from sleeping. AssignState is just a dumb copy of variables from the native type to the managed type.

It would be interesting to know if you can reproduce the problem by writing the equivalent native code. I don't really see how this issue is something SlimDX could correct for though.

Thanks for all the suggestions and replies.

It would be interesting to know if you can reproduce the problem by writing the equivalent native code. I don't really see how this issue is something SlimDX could correct for though.

I did try this, I mentioned in my first post describing it as "pure DirectX C++ app that does the same thing"

I also analyzed the SlimDX source, traced everything through RecordError, and AssignState, and I agree - nothing in there seems like it should be able to cause this, when the same thing Native doesn't!

Have you installed all relevant Windows 10 Updates? Some people have reported behaviour like this in other applications (including the screensaver not triggering any more), but also that it was fixed in a Windows update.

The computer is set to fully automatic updates, and has been running for a couple weeks, but I'll be sure to try manually checking for updates.

However, after more investigating today, I discovered that this only occurs on one of my two Windows 10 machines. The HTPC this runs on has all the gamepads connected to it, so I've been doing all my testing there. But I tried it on my development machine, and the computer sleeps fine. I created a brand new C# application that all it does is use SlimDX to acquire and poll one gamepad. I also put a checkbox on it, that simply toggles on or off the call get GetCurrentState() while running everything else. This reproduces the issue on the HTPC only (and I can make it sleep by checking the box and waiting).

So thanks again for all the input, but it seems like since this only happens on one machine, and strangely only with SlimDX and not C++ DirectX, that it's not likely to get resolved. But if anyone has any other ideas to try, I'm all ears.

Cause found! Well, sort of. I did more debugging on this tonight, and discovered 2 things:

1) It is not SlimDX.

2) I can reproduce it on any Windows 10 machine.

So first thing - it's not SlimDX. I carefully examined my native code, c# code, and the SlimDX source. There was one difference - I had copy/pasted my Native code from an earlier project I had which was a background process with no UI. So in calling IDirectInputDevice8::SetCooperativeLevel() I was passing in NULL for the window handle. By creating a window and using that handle, the problem reproduces in Native code. SlimDX doesn't allow passing null in for this.

Second, I did not have the power settings the same between the 2 machines I tried. Making them both the same repro'd the issue on both machines.

So bottom line is it appears to be a Windows 10 issue. Maybe in my free time during this holiday weekend I'll clean up my test spaghetti code and submit a bug to Microsoft. Here's how it works: In order to repro the issue, you have to have "Turn off the display" set to "Never." So that only the Sleep is set to a time value. When I have "Turn off display" set to 1 minute, and "Sleep" set to 1 minute as well, then when a DirectInput application is polling the device, after 1 minute the screen turns off, and after another minute the system goes to sleep. So 2 minutes total to sleep when the app is running, and 1 minute to sleep when it is not. It's as if the display turning off allows the sleep timer to finally start counting down.

This topic is closed to new replies.

Advertisement