Sat
11
Mar 2017
If you write a graphics application or a game, you may want to make it fullscreen and set specific screen resolution. In DirectX there are functions for that, but if you use OpenGL or Vulkan, you need another way to accomplish that. I've researched the topic recently and I've found that Windows API supports enumerating display devices and modes with functions: EnumDisplayDevices
, EnumDisplaySettings
, as well as changing mode with function ChangeDisplaySettingsEx
. It's a programatic access to more or less the same set of features that you can access manually by going to "Display settings" system window.
I've prepared an example C program demonstrating how to use these functions:
DisplaySettingsTest - github.com/sawickiap
First you may want to enumerate available Adapters. To do this, call function EnumDisplayDevices
multiple times. Pass NULL
as first parameter (LPCWSTR lpDevice
). As the second parameter pass subsequent DWORD
Adapter index, starting from 0. Enumeration should continue as long as the function returns BOOL
nonzero. When it returns zero, it means there are no more Adapters and that Adapter with given index and any higher index could not be retrieved.
For each successfully retrieved Adapter, DISPLAY_DEVICE
structure is filled by the function. It contains following members:
WCHAR DeviceName[32]
- string with name of the Adapter, like "\\.\DISPLAY1".WCHAR DeviceString[128]
- string with more user-friendly name of the Adapter, like "AMD Radeon (TM) RX 480".DWORD StateFlags
- various flags, like DISPLAY_DEVICE_ACTIVE
if the device is on, or DISPLAY_DEVICE_PRIMARY_DEVICE
if this is the primary device.There is a second level: Adapters contain Display Devices. To enumerate them, use the same function EnumDisplayDevices
, but this time pass Adapter DeviceName
as first parameter. This way you will enumerate Display Devices inside that Adapter, described by the same structure DISPLAY_DEVICE
. For example, my system returns DeviceName
= "\\.\DISPLAY1\Monitor0", DeviceString
= "Generic PnP Monitor".
The meaning and the difference between "Adapter" and "Display Device" is not fully clear to me. You may think that Adapter is a single GPU (graphics card), but it turns out not to be the case. I have a single graphics card and yet my system reports 6 Adapters, each having 0 or 1 Display Device. That can mean Adapter is more like a single monitor output (e.g. HDMI, DisplayPort, VGA) on the graphics card. This seems true unless you have two monitors running in "Duplicate" mode - then two Display Devices are reported inside one Adapter.
Then there is a list of supported Display Settings (or Modes). You can enumerate them in similar fashion using EnumDisplaySettings
function, which fills DEVMODE
structure. It seems that Modes belong to an Adapter, not a Display Device, so as first parameter to this function you must to pass DISPLAY_DEVICE::DeviceName
returned by EnumDisplayDevices(NULL, ...)
, not EnumDisplaySettings(adapter.DeviceName, ...)
. The structure is quite complex, but the function fills only following members:
DWORD dmPelsWidth, dmPelsHeight
- resolution, in pixels.DWORD dmBitsPerPel
- bits per pixel (all Modes have 32 in my case).DWORD dmDisplayFrequency
- refresh rate, in Hz.DWORD dmDisplayFlags
- additional flags, like DM_INTERLACED
for interlaced mode.I have a single graphics card (AMD Radeon RX 480) with two Full HD (1920 x 1080) monitors connected. You can see example output of the program from my system here: ExampleOutput.txt.
To change display mode, use function ChangeDisplaySettingsEx
.
LPCTSTR lpszDeviceName
), pass DeviceName
of the chosen Adapter (again, not Display Device!).DEVMODE *lpDevMode
), pass structure filled with desired Display Settings. You can fill it by yourself, but Microsoft recommends to pass the copy of the structure as it was retrieved from function EnumDisplaySettings
.DWORD dwFlags
), you can pass various flags, e.g. whether new settings should be saved in the registry.The function returns DISP_CHANGE_SUCCESSFUL
if display mode was successfully changed and one of other DISP_CHANGE_*
constants if it failed.
To restore original display mode, call the function like this:
ChangeDisplaySettingsEx(targetDeviceName, NULL, NULL, 0, NULL);
Unfortunately, display mode changed in the way described here is not automatically restored after user switches to some other application (e.g. using Alt+Tab), like in DirectX fullscreen mode, but you can handle it yourself. Good news is that if you pass CDS_FULLSCREEN
flag to ChangeDisplaySettingsEx
, the previous mode is automatically restored by the system when your application exits or crashes.
Comments | #windows #graphics Share