Programmatically Turning Off the Screen

I sometimes work on systems that control displays that run for most of the day. But there are “quiet hours” during which those screens show no content, and we would rather them be turned off. Having some downtime saves on resources, including the life of the display. The way this is handled differs from one solution to another. Sometimes we use third-party hardware to control the screens according to a schedule. Other times, we might enter the schedule into the display settings. I’m working on a solution that is a bit more varied from day-to-day. The schedule options don’t work here.

I want a display to turn on at the start of the day and turn off a little after sunset. Calculating the time of sunrise and sunset is pretty easy. I keep a web app on GitHub just for that purpose (Note: since sunrise time is location dependent, this webapp will ask for permission to use your location). The challenge is how do I tell the screen to turn on and off. Ideally, I would use HDMI-CEC. This is the protocol that you see used in many consumer devices (such as a PlayStation or Chromecast) to turn a television on and change the input. Unfortunately, HDMI-CEC is not generally supported by computer video cards. That’s not an option. But many displays have an option to turn off when there is no video signal and to turn on when one is received. An achievable approach would be to have the computer to simply turn off its HDMI output.

On Windows, we can achieve this with a single API call to SendMessage(). The SendMessage() is used by a broad range of Windows components for communicating with each other and invoking functionality. The first argument to this function is a handle for the window that the message is to be sent to. The window handle constant HWND_BROADCAST sends a message to all top-level windows. It also can be used to send messages to the system itself. There are three other parameters in SendMessage(). It also takes the command for the message and two parameters. The command that we need to turn the display off is WM_SYSCOMMAND. This means that we want to execute a system command. The third parameter identifies which command that we want to invoke. In our case, it is SC_MONITORPOWER (System Command Monitor Power). The by the value passed in the fourth position, the command can tell the display output to turn on (-1), off(2), or sleep mode(1).

Using this command, I can turn the display off. It will turn back on if the mouse is moved or a key on the keyboard is pressed. Using the command to turn back on is a bit different. For my scenario, the computers reboot daily at least an hour before they are needed. This is enough to turn the display back on. I’ve tried making the function call with the parameter to turn the display on. But the result varies. I’ve had better success if I make the system call that makes the system process a keyboard input or mouse movement. You would think that the SendMessage() call could be used for this purpose too. You would only use that function to send a key press message to a specific window. To have the system act as though there were a user input I need to use the SendInput() function. A mouse move input is the safest input to generate. Key input might activate functionality in applications that one might not intend to invoke.

INPUT input = { 0 };
input.type = INPUT_MOUSE;
input.mi.dx = 10;
input.mi.dy = 10;
input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
if (SendInput(1, &input, sizeof(INPUT)) == 0)
{
	std::wcerr << L"Error sending mouse move input to wake up the screen. Error code: 0x" << std::hex << GetLastError() << std::endl;
}

The source code and compiled binaries for this program can be found on my GitHub page in this repository. I’ve made a command line utility that takes a single argument. The value of the argument can be on, off, or sleep. I suggest **not** running it on a laptop. When I tried these commands on a laptop I got unusual results. They may generally work better on computers that use external displays.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Bluesky: @j2i.net

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.