I have a little movie and tv-show collection and use the popular open-source mediacenter software Kodi to organize it. But as some tv-shows/movies have other image frequencies/fps than others, it necessary to change the screen refresh rate accordingly. Kodi already delivers an integrated functionality to automatically change the refresh rate as required.
Personally, I like to have some control over this, so I decided to develope a small windows application in C++ (with some C in order to work with the Win32 API) to accomplish this. Doing so a is definitely not a big task.
Initial requirements
As we make use of the windows API, we need to include the windows header files.
1 |
#include <windows.h> |
Taking a quick look at the documentations of a few functions we’ll use, shows that we need to include the library user32 into the compiler arguments. When using MinGW, the call will look something like this.
1 |
g++ -o frequency.exe -std=c++11 -luser32 frequency.cpp |
As the examples will contain some C++11 syntax, I also added the proper parameter to compile these sources.
Changing the refresh rate
In order to change the refresh rate, you’ll first have to retrieve the available displays. You can do this by using the EnumDisplayDevices function. The displayId represents the index of the display. These indexes start with 0.
1 2 3 4 5 6 7 8 9 10 11 12 |
DISPLAY_DEVICE getDisplayDevice(DWORD displayId) { // Setting cb is important // otherwise the query will succeed but won't have any data DISPLAY_DEVICE device{}; device.cb = sizeof(DISPLAY_DEVICE); if(!EnumDisplayDevices(NULL, displayId, &device, 0)) { throw std::invalid_argument("invalid displayId"); } return device; } |
Next thing we need are the current display settings as we do not want to change anything but the display frequency. To accomplish this, we need to use EnumDisplaySettings function. As we’d like to query a specific display, we provide the DeviceName of the retrieved display.
1 2 3 4 5 6 7 8 9 10 |
DEVMODE getDisplaySettings(DWORD displayId) { // Same as in EnumDisplaySettings, dmSize is important. DEVMODE deviceMode{}; deviceMode.dmSize = sizeof(DEVMODE); if(!EnumDisplaySettings(getDisplayDevice(displayId).DeviceName, ENUM_CURRENT_SETTINGS, &deviceMode)) { throw std::invalid_argument("failed to retrieve display settings"); } return deviceMode; } |
Now having the currently active settings, we can simply change the property dmDisplayFrequency and call ChangeDisplaySettings with the updated struct.
1 2 3 4 5 6 7 |
void setDisplayFrequency(DWORD displayId, DWORD frequency) { DEVMODE deviceMode = getDisplaySettings(displayId); deviceMode.dmDisplayFrequency = frequency; if(ChangeDisplaySettings(&deviceMode, 0) != DISP_CHANGE_SUCCESSFUL) { throw std::runtime_error("failed to change display settings"); } } |
Registering the keyboard shortcut
To register a keyboard shortcut, we need the RegisterHotKey function.
1 2 3 4 5 6 7 |
void registerShortcut(int shortcutId, UINT key) { // registers a shorctut ALT + key // the shortcutId is used to distinguish between multiple keys if(!RegisterHotKey(NULL, shortcutId, MOD_ALT | MOD_NOREPEAT, key)) { throw std::runtime_error("failed to register hotkey"); } } |
After registering the shortcut, we need to process particular events. To do this, we now create an event loop within our application. The struct variable wParam will hold the shortcut id. In this example, we won’t use it to actually distinguish between the different shortcuts but to directly provide the desired display frequency.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void runEventLoop(DWORD displayId) { MSG msg{}; while(GetMessage(&msg, NULL, 0, 0) > 0) { switch(msg.message) { case WM_HOTKEY: // msg.wParam holds the shortcutid std::cout << "switching display frequency to " << msg.wParam << "\n"; setDisplayFrequency(displayId, msg.wParam); break; } } } |
As the MinGW version I was using was missing the MOD_NOREPEAT definition, it had to be added manually.
1 2 3 |
#ifndef MOD_NOREPEAT #define MOD_NOREPEAT 0x4000 #endif |
Launching application
Now, we’re still missing the main function to fire everything up.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int main() { try { // Make shortcut ALT + B change the frequency to 23 registerShortcut(23, 0x42); // Make shortcut ALT + V change the frequency to 60 registerShortcut(60, 0x56); // Make keypresses switch the frequency of the display at index 1 runEventLoop(1); } catch(std::exception const& e) { std::cout << "Error during execution: " << e.what() << "\n"; } } |
Create an executable without console
Finally we have to build our little application. As we’d like to have the application silently sit in the background, we don’t want a console to be displayed. To achieve this, we have to pass the argument -mwindows to our compiler.
1 |
g++ -std=c++11 -o frequency.exe -mwindows frequency.cpp |
This argument also ensures that the required windows libraries will be linked.
Example Code
I have attached two example codes. One which simply summarizes the above setup.
The other one is the actual implementation I’m using for my mediacenter setup. It also features small on-screen notifications showing the refresh rate that has been switched to. Rather than switching the frequency through multiple shortcuts, it cycles through a configured list.