Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

My screensaver is stopping screen suspend but I don't want it to

I made a screensaver in .Net 4.0. It basically just moves the bits in an image around and displays it using .Invalidate on a timer and overriding the onPaint event.

So far, it works great - however - I noticed one problem with it.

It's stopping the monitor from suspending after the suspend timeout. Since I installed it, my monitor stays on 24/7 now.

The thing is, I didn't do anything to specifically stop power savings features - and I've made sure that my computer's power savings settings are set (they are). So then I chose another screensaver just to be sure the settings were still working. The monitor suspended after the timeout.

What do I need to do then to play nice with power management? I searched for this answer on Google and everything I found is how to block power management and I didn't explicitly block it! I just want the suspend to be allowed when it's time.

like image 648
Dracorat Avatar asked Jun 05 '12 15:06

Dracorat


2 Answers

Do you by any chance accidentally trap the 0xF170 (SC_MONITORPOWER) sub-command of WM_SYSCOMMAND?

like image 159
500 - Internal Server Error Avatar answered Nov 10 '22 21:11

500 - Internal Server Error


I was able to get my program to "play nice". I don't know why this works and the original code didn't, but this not only works - it actually makes the program more "power savings" friendly because it reduces CPU cycles by not doing calculations after the screen suspends. In short, I preview the WndProc messages and look for the monitor is being suspended message and once it's received, I stop redrawing until it resumes (it's possible to resume and have the screensaver still active).

Code changes:

    // Field Definitions
    /// <summary>
    /// Constants that relate to the WndProc messages we wish to intercept and evaluate.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Standard practice to use this naming style for Win32 API Constants.")]
    private const int WM_SYSCOMMAND = 0x0112, SC_MONITORPOWER = 0xF170;

    /// <summary>
    /// Gets or sets whether we are suspended. Should coincide with whether the display is turned on or not.
    /// </summary>
    private bool isSuspended = false;


    // New overridden method
    /// <summary>
    /// Intercepts WndProc messages. We are looking for the screen suspend activity. From it, we will return that we are able to suspend and we ourselves will suspend.
    /// </summary>
    /// <param name="m">Message to be checked.</param>
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_SYSCOMMAND)
        {
            // The 0x000F bits are used to indicate the specific state and must be ignored to see if this is a monitor power event.
            if ((m.WParam.ToInt32() & 0xFFF0) == SC_MONITORPOWER)
            {
                switch (m.WParam.ToInt32() & 0x000F)
                {
                    case -1:
                        // Display powering on - resume operation
#if DEBUG
                        System.Diagnostics.Debug.WriteLine("Display powered on.");
#endif
                        this.isSuspended = false;
                        break;
                    case 0:
                    case 1:
                    case 2:
                        // Display being powered off - suspend operation
#if DEBUG
                        System.Diagnostics.Debug.WriteLine("Display suspended");
#endif
                        this.isSuspended = true;
                        break;
                    default:
#if DEBUG
                        System.Diagnostics.Debug.WriteLine(string.Format("Unknown power state: {0}", (m.WParam.ToInt32() & 0x000F).ToString("0")));
#endif
                        // Assuming that unknown values mean to power off. This is a WAG.
                        this.isSuspended = true;
                        break;
                }
            }
        }

        base.WndProc(ref m);
    }


    // Change to my refreshing timer.
    /// <summary>
    /// Called when the refresh timer ticks. This invalidates the form, forcing it to be redrawn, which creates a framerate for us.
    /// </summary>
    /// <param name="sender">Who called this method.</param>
    /// <param name="e">Event Arguments.</param>
    private void RefreshTimer_Tick(object sender, EventArgs e)
    {
        if (this.isSuspended)
        {
            // Program is in suspended mode, so don't do anything this update.
            return;
        }

        // Program is not suspended, so invalidate the client area so it can be painted again.
        this.Invalidate();
    }

Making this change stops all the redrawing when a suspend is called (and stops the GDI+ calculations) and after making it, the screensaver "behaves" with power management settings.

like image 29
Dracorat Avatar answered Nov 10 '22 20:11

Dracorat