Czasem zachodzi potrzeba zmiany ustawień monitora z poziomu aplikacji. Czasem chodzi nam o rozdzielczość, czasem o odświeżanie. Mi akurat chodziło o odświeżanie – aplikacja wyświetla materiał video w różnych formatach i potrzebowałem dostosować monitor do materiału, aby osiągnąć jak najlepszą synchronizację.
Moja aplikacja jest w Qt (a jakże), więc zaczynam od sprawdzenia, na którym monitorze jest wyświetlona moja aplikacja:
int monitor = QApplication::desktop()->screenNumber(ui.centralWidget);
Aby napisać działającą poprawnie metodę zmieniającą parametry wyświetlania pod Windows 7, musimy podać dokładną nazwę urządzenia. Podawanie NULL, która wg dokumentacji MSDN powinna zadziałać na domyślnym urządzeniu, pod Windows 7 kończy się błędem niepoprawnego parametru. Dlatego najpierw listujemy wszystkie dostępne urządzenia – i jeśli jest to nasze urządzenie, pobieramy jego aktualne ustawienia i modyfikujemy je:
DEVMODE winMode; DISPLAY_DEVICE displayDevice; memset(&displayDevice, 0, sizeof(DISPLAY_DEVICE)); displayDevice.cb = sizeof(displayDevice); DWORD iDevNum = 0; if (EnumDisplayDevices(NULL, iDevNum, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME)) { if (EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &winMode) != 0) { winMode.dmPelsWidth = aWidth; winMode.dmPelsHeight = aHeight; winMode.dmDisplayFrequency = aFreq; ChangeDisplaySettingsEx(displayDevice.DeviceName, &winMode, NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL); } }
Wg dokumentacji, po użyciu ChangeDisplaySettingsEx powinniśmy jeszcze spowodować rozesłanie komunikatu do wszystkich aplikacji, wywołując ją jeszcze raz w ten sposób:
ChangeDisplaySettings(NULL, 0);
Składając wszystko do kupy, napisałem sobie metodę, która bierze parametry ustawień (rozdzielczość i odświeżanie) i sprawdza, czy podałem wartość większą od zera (gdybym chciał ustawić tylko niektóre z tych parametrów):
//! Change display (monitor) settings. If you don't want to change any of the values, pass 0 for them. static bool setCurrentDisplayMode(int aWidth, int aHeight, int aFreq, int aDisplay = 0);
bool sysWindowsApi::setCurrentDisplayMode(int aWidth, int aHeight, int aFreq, int aDisplay) { bool ret = false; DEVMODE winMode; DISPLAY_DEVICE displayDevice; memset(&displayDevice, 0, sizeof(DISPLAY_DEVICE)); displayDevice.cb = sizeof(displayDevice); if (EnumDisplayDevices(NULL, aDisplay, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME)) { if (EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &winMode) != 0) { if (aWidth > 0) winMode.dmPelsWidth = aWidth; if (aHeight > 0) winMode.dmPelsHeight = aHeight; if (aFreq > 0) winMode.dmDisplayFrequency = aFreq; if (ChangeDisplaySettingsEx(displayDevice.DeviceName, &winMode, NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL) { ChangeDisplaySettings(NULL, 0); ret = true; } } } return ret; }
Z głównej aplikacji wywołuję ją np. tak:
sysWindowsApi::setCurrentDisplayMode(0, 0, 60, QApplication::desktop()->screenNumber(ui.centralWidget));
powoduje to zmianę tylko częstotliwości odświeżania, pozostawiając rozdzielczość bez zmian – i tylko na tym monitorze, na którym znajduje się nasza aplikacja.