Last active
November 4, 2025 11:03
-
-
Save rajtilakjee/9475ec06e8f61b1c4696e4b662de19a8 to your computer and use it in GitHub Desktop.
Python script to change the display's refresh rate to 60 Hz for Windows 11 PC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import ctypes | |
| from ctypes import wintypes | |
| # Constants | |
| CDS_UPDATEREGISTRY = 0x01 | |
| DISP_CHANGE_SUCCESSFUL = 0 | |
| ENUM_CURRENT_SETTINGS = -1 | |
| class POINTL(ctypes.Structure): | |
| _fields_ = [("x", wintypes.LONG), ("y", wintypes.LONG)] | |
| class DEVMODE(ctypes.Structure): | |
| _fields_ = [ | |
| ("dmDeviceName", ctypes.c_wchar * 32), | |
| ("dmSpecVersion", wintypes.WORD), | |
| ("dmDriverVersion", wintypes.WORD), | |
| ("dmSize", wintypes.WORD), | |
| ("dmDriverExtra", wintypes.WORD), | |
| ("dmFields", wintypes.DWORD), | |
| ("dmPosition", POINTL), | |
| ("dmDisplayOrientation", wintypes.DWORD), | |
| ("dmDisplayFixedOutput", wintypes.DWORD), | |
| ("dmColor", wintypes.WORD), | |
| ("dmDuplex", wintypes.WORD), | |
| ("dmYResolution", wintypes.WORD), | |
| ("dmTTOption", wintypes.WORD), | |
| ("dmCollate", wintypes.WORD), | |
| ("dmFormName", ctypes.c_wchar * 32), | |
| ("dmLogPixels", wintypes.WORD), | |
| ("dmBitsPerPel", wintypes.DWORD), | |
| ("dmPelsWidth", wintypes.DWORD), | |
| ("dmPelsHeight", wintypes.DWORD), | |
| ("dmDisplayFlags", wintypes.DWORD), | |
| ("dmDisplayFrequency", wintypes.DWORD), | |
| ("dmICMMethod", wintypes.DWORD), | |
| ("dmICMIntent", wintypes.DWORD), | |
| ("dmMediaType", wintypes.DWORD), | |
| ("dmDitherType", wintypes.DWORD), | |
| ("dmReserved1", wintypes.DWORD), | |
| ("dmReserved2", wintypes.DWORD), | |
| ("dmPanningWidth", wintypes.DWORD), | |
| ("dmPanningHeight", wintypes.DWORD), | |
| ] | |
| def set_refresh_rate(refresh_rate): | |
| devmode = DEVMODE() | |
| devmode.dmSize = ctypes.sizeof(DEVMODE) | |
| # Retrieve current settings | |
| if ctypes.windll.user32.EnumDisplaySettingsW(None, ENUM_CURRENT_SETTINGS, ctypes.byref(devmode)) != 0: | |
| # Set the refresh rate | |
| devmode.dmDisplayFrequency = refresh_rate | |
| devmode.dmFields = 0x400000 # DM_DISPLAYFREQUENCY | |
| # Apply the settings | |
| result = ctypes.windll.user32.ChangeDisplaySettingsW(ctypes.byref(devmode), CDS_UPDATEREGISTRY) | |
| if result == DISP_CHANGE_SUCCESSFUL: | |
| print(f"Refresh rate set to {refresh_rate} Hz successfully.") | |
| else: | |
| print(f"Failed to set refresh rate. Error code: {result}") | |
| else: | |
| print("Failed to retrieve current display settings.") | |
| # Example usage | |
| set_refresh_rate(60) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import ctypes | |
| from ctypes import wintypes | |
| # Constants | |
| CDS_UPDATEREGISTRY = 0x01 | |
| DISP_CHANGE_SUCCESSFUL = 0 | |
| ENUM_CURRENT_SETTINGS = -1 | |
| # Structures | |
| class POINTL(ctypes.Structure): | |
| _fields_ = [("x", wintypes.LONG), ("y", wintypes.LONG)] | |
| class DEVMODE(ctypes.Structure): | |
| _fields_ = [ | |
| ("dmDeviceName", ctypes.c_wchar * 32), | |
| ("dmSpecVersion", wintypes.WORD), | |
| ("dmDriverVersion", wintypes.WORD), | |
| ("dmSize", wintypes.WORD), | |
| ("dmDriverExtra", wintypes.WORD), | |
| ("dmFields", wintypes.DWORD), | |
| ("dmPosition", POINTL), | |
| ("dmDisplayOrientation", wintypes.DWORD), | |
| ("dmDisplayFixedOutput", wintypes.DWORD), | |
| ("dmColor", wintypes.WORD), | |
| ("dmDuplex", wintypes.WORD), | |
| ("dmYResolution", wintypes.WORD), | |
| ("dmTTOption", wintypes.WORD), | |
| ("dmCollate", wintypes.WORD), | |
| ("dmFormName", ctypes.c_wchar * 32), | |
| ("dmLogPixels", wintypes.WORD), | |
| ("dmBitsPerPel", wintypes.DWORD), | |
| ("dmPelsWidth", wintypes.DWORD), | |
| ("dmPelsHeight", wintypes.DWORD), | |
| ("dmDisplayFlags", wintypes.DWORD), | |
| ("dmDisplayFrequency", wintypes.DWORD), | |
| ("dmICMMethod", wintypes.DWORD), | |
| ("dmICMIntent", wintypes.DWORD), | |
| ("dmMediaType", wintypes.DWORD), | |
| ("dmDitherType", wintypes.DWORD), | |
| ("dmReserved1", wintypes.DWORD), | |
| ("dmReserved2", wintypes.DWORD), | |
| ("dmPanningWidth", wintypes.DWORD), | |
| ("dmPanningHeight", wintypes.DWORD), | |
| ] | |
| class DISPLAY_DEVICE(ctypes.Structure): | |
| _fields_ = [ | |
| ("cb", wintypes.DWORD), | |
| ("DeviceName", ctypes.c_wchar * 32), | |
| ("DeviceString", ctypes.c_wchar * 128), | |
| ("StateFlags", wintypes.DWORD), | |
| ("DeviceID", ctypes.c_wchar * 128), | |
| ("DeviceKey", ctypes.c_wchar * 128), | |
| ] | |
| def list_displays(): | |
| """List all connected display devices.""" | |
| displays = [] | |
| i = 0 | |
| display = DISPLAY_DEVICE() | |
| display.cb = ctypes.sizeof(display) | |
| while ctypes.windll.user32.EnumDisplayDevicesW(None, i, ctypes.byref(display), 0): | |
| if display.StateFlags & 0x1: # DISPLAY_DEVICE_ACTIVE | |
| displays.append(display.DeviceName) | |
| i += 1 | |
| return displays | |
| def set_refresh_rate_for_display(device_name, refresh_rate): | |
| """Set refresh rate for a specific display.""" | |
| devmode = DEVMODE() | |
| devmode.dmSize = ctypes.sizeof(DEVMODE) | |
| if ctypes.windll.user32.EnumDisplaySettingsW(device_name, ENUM_CURRENT_SETTINGS, ctypes.byref(devmode)) != 0: | |
| devmode.dmDisplayFrequency = refresh_rate | |
| devmode.dmFields = 0x400000 # DM_DISPLAYFREQUENCY | |
| result = ctypes.windll.user32.ChangeDisplaySettingsExW(device_name, ctypes.byref(devmode), None, CDS_UPDATEREGISTRY, None) | |
| if result == DISP_CHANGE_SUCCESSFUL: | |
| print(f"{device_name}: Refresh rate set to {refresh_rate} Hz successfully.") | |
| else: | |
| print(f"{device_name}: Failed to set refresh rate. Error code: {result}") | |
| else: | |
| print(f"{device_name}: Failed to retrieve display settings.") | |
| def set_refresh_rate_all(refresh_rate): | |
| """Set refresh rate for all active displays.""" | |
| for device_name in list_displays(): | |
| set_refresh_rate_for_display(device_name, refresh_rate) | |
| # Example usage | |
| set_refresh_rate_all(60) |
Author
Thanks!
You might wanna set it to launch on startup. That's what I did and it works like a charm.
Stumbled onto this via Google, thanks, it worked perfectly!
@rajtilakjee - how will you handle multiple monitors?
Author
@emesiace I have updated the GIST for multiple monitors. This code targets all available monitors even if they are inactive at the moment and then runs the code for those that are active.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks!