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.
Do you by any chance accidentally trap the 0xF170 (SC_MONITORPOWER) sub-command of WM_SYSCOMMAND?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With