-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Andre Bocchini
committed
Apr 3, 2024
1 parent
0212c49
commit 6718081
Showing
7 changed files
with
1,063 additions
and
205 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
from windows_types import ( | ||
DISPLAYCONFIG_MODE_INFO, | ||
DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO, | ||
) | ||
|
||
|
||
class DisplayMode: | ||
def __init__(self, width: int, height: int, refresh: int): | ||
self.width: int = width | ||
self.height: int = height | ||
self.refresh: int = refresh | ||
|
||
def __str__(self): | ||
return str(self.width) + "x" + str(self.height) + " @ " + str(self.refresh) + "Hz" | ||
|
||
|
||
class DisplayAdapter: | ||
def __init__( | ||
self, | ||
identifier: str = "", | ||
display_name: str = "", | ||
active_mode: DisplayMode = None, | ||
available_modes: list[DisplayMode] = None, | ||
is_attached: bool = False, | ||
is_primary: bool = False | ||
): | ||
self.identifier: str = identifier | ||
self.display_name: str = display_name | ||
self.active_mode: DisplayMode = active_mode | ||
self.available_modes: list[DisplayMode] = available_modes | ||
self.is_attached: bool = is_attached | ||
self.is_primary: bool = is_primary | ||
|
||
|
||
class DisplayMonitor: | ||
def __init__( | ||
self, | ||
name: str = "", | ||
adapter: DisplayAdapter = DisplayAdapter(), | ||
mode_info: DISPLAYCONFIG_MODE_INFO = None, | ||
color_info: DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO = None | ||
): | ||
self.name: str = name | ||
self.adapter: DisplayAdapter = adapter | ||
self.mode_info: DISPLAYCONFIG_MODE_INFO = mode_info | ||
self.color_info: DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO = color_info | ||
|
||
def identifier(self) -> str: | ||
return self.adapter.identifier | ||
|
||
def active_mode(self) -> DisplayMode: | ||
return self.adapter.active_mode | ||
|
||
def is_primary(self) -> bool: | ||
return self.adapter.is_primary | ||
|
||
def is_attached(self) -> bool: | ||
return self.adapter.is_attached | ||
|
||
def is_hdr_supported(self) -> bool: | ||
return self.color_info.value & 0x1 == 0x1 | ||
|
||
def is_hdr_enabled(self) -> bool: | ||
return self.color_info.value & 0x2 == 0x2 | ||
|
||
|
||
class DisplayMonitorException(Exception): | ||
pass | ||
|
||
|
||
class PrimaryMonitorException(Exception): | ||
pass | ||
|
||
|
||
class HdrException(Exception): | ||
pass | ||
|
||
|
||
class DisplayAdapterException(Exception): | ||
pass |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
from windows_types import ( | ||
DISP_CHANGE_SUCCESSFUL, | ||
DISP_CHANGE_RESTART, | ||
DISP_CHANGE_BADFLAGS, | ||
DISP_CHANGE_BADMODE, | ||
DISP_CHANGE_BADPARAM, | ||
DISP_CHANGE_FAILED, | ||
DISP_CHANGE_NOTUPDATED, | ||
DISP_CHANGE_BADDUALVIEW, | ||
DISPLAY_DEVICE_ATTACHED_TO_DESKTOP, | ||
DISPLAY_DEVICE_PRIMARY_DEVICE, | ||
ENUM_CURRENT_SETTINGS, | ||
DEVMODEW, | ||
DISPLAY_DEVICEW, | ||
ChangeDisplaySettingsExW, | ||
EnumDisplaySettingsW, | ||
EnumDisplayDevicesW, DM_PELSHEIGHT, DM_PELSWIDTH, DM_DISPLAYFREQUENCY | ||
) | ||
from custom_types import DisplayAdapter, DisplayMode, DisplayAdapterException | ||
from ctypes import sizeof, byref | ||
|
||
|
||
def is_attached_to_desktop(adapter: DISPLAY_DEVICEW) -> bool: | ||
state_flags: int = adapter.StateFlags | ||
|
||
return state_flags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP == DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | ||
|
||
|
||
def is_primary_device(adapter: DISPLAY_DEVICEW) -> bool: | ||
state_flags: int = adapter.StateFlags | ||
|
||
return state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE == DISPLAY_DEVICE_PRIMARY_DEVICE | ||
|
||
|
||
def get_all_display_adapters() -> list[DisplayAdapter]: | ||
adapters: list[DisplayAdapter] = [] | ||
|
||
# This will hold display device information on every iteration of the loop | ||
display_device = DISPLAY_DEVICEW() | ||
display_device.cb = sizeof(DISPLAY_DEVICEW) | ||
display_device.StateFlags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | ||
|
||
try: | ||
# Tell Windows to cache display device information before we start looping | ||
EnumDisplayDevicesW(None, 0, byref(display_device)) | ||
except OSError: | ||
raise DisplayAdapterException("Failed to get list of available display devices") | ||
|
||
index_of_current_adapter: int = 0 | ||
finished_searching_for_devices: bool = False | ||
|
||
while not finished_searching_for_devices: | ||
result: int = EnumDisplayDevicesW(None, index_of_current_adapter, byref(display_device)) | ||
|
||
if result == 0: | ||
finished_searching_for_devices = True | ||
else: | ||
try: | ||
display_adapter = DisplayAdapter() | ||
display_adapter.identifier = str(display_device.DeviceName) | ||
display_adapter.display_name = str(display_device.DeviceString) | ||
display_adapter.active_mode = get_active_display_mode_for_adapter(display_device) | ||
display_adapter.available_modes = get_all_available_display_modes_for_adapter(display_device) | ||
display_adapter.is_attached = is_attached_to_desktop(display_device) | ||
display_adapter.is_primary = is_primary_device(display_device) | ||
|
||
adapters.append(display_adapter) | ||
except DisplayAdapterException: | ||
pass | ||
finally: | ||
index_of_current_adapter += 1 | ||
|
||
return adapters | ||
|
||
|
||
def get_all_available_display_modes_for_adapter(adapter: DISPLAY_DEVICEW) -> list[DisplayMode]: | ||
identifier: str = adapter.DeviceName | ||
display_modes: list[DisplayMode] = [] | ||
|
||
# This will store the display mode information on every loop iteration | ||
devmodew: DEVMODEW = DEVMODEW() | ||
devmodew.dmSize = sizeof(DEVMODEW) | ||
|
||
try: | ||
# Tell Windows to cache display mode information before we start looping | ||
result: int = EnumDisplaySettingsW(identifier, 0, devmodew) | ||
|
||
if result == 0: | ||
raise DisplayAdapterException( | ||
f"Failed to get available modes for {identifier}. Failed with result {result}" | ||
) | ||
|
||
except OSError as e: | ||
raise DisplayAdapterException(f"Failed to get available modes for {identifier}. Failed with error {str(e)}") | ||
|
||
index_of_current_mode: int = 1 | ||
finished_getting_modes: bool = False | ||
|
||
while not finished_getting_modes: | ||
try: | ||
result: int = EnumDisplaySettingsW(identifier, index_of_current_mode, byref(devmodew)) | ||
|
||
if result == 0: | ||
finished_getting_modes = True | ||
else: | ||
display_mode = DisplayMode(devmodew.dmPelsWidth, devmodew.dmPelsHeight, devmodew.dmDisplayFrequency) | ||
display_modes.append(display_mode) | ||
|
||
index_of_current_mode += 1 | ||
except OSError: | ||
finished_getting_modes = True | ||
|
||
return display_modes | ||
|
||
|
||
def get_active_display_mode_for_adapter(adapter: DISPLAY_DEVICEW) -> DisplayMode: | ||
identifier = adapter.DeviceName | ||
|
||
try: | ||
display_modew = DEVMODEW() | ||
display_modew.dmSize = sizeof(DEVMODEW) | ||
|
||
result: int = EnumDisplaySettingsW(identifier, ENUM_CURRENT_SETTINGS, byref(display_modew)) | ||
|
||
if result == 0: | ||
raise DisplayAdapterException(f"Failed to get active mode for {identifier}. Failed with result {result}") | ||
|
||
return DisplayMode(display_modew.dmPelsWidth, display_modew.dmPelsHeight, display_modew.dmDisplayFrequency) | ||
except OSError as e: | ||
raise DisplayAdapterException(f"Failed to get active mode for {identifier}. Failed with error {str(e)}") | ||
|
||
|
||
def set_display_mode_for_device(display_mode: DisplayMode, device_identifier: str): | ||
if device_identifier is None: | ||
raise DisplayAdapterException("Device identifier cannot be empty") | ||
|
||
if display_mode is None: | ||
raise DisplayAdapterException("Display settings cannot be empty") | ||
|
||
devmodew = DEVMODEW() | ||
devmodew.dmDeviceName = device_identifier | ||
devmodew.dmSize = sizeof(DEVMODEW) | ||
devmodew.dmPelsWidth = display_mode.width | ||
devmodew.dmPelsHeight = display_mode.height | ||
devmodew.dmDisplayFrequency = display_mode.refresh | ||
devmodew.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | ||
|
||
try: | ||
result: int = ChangeDisplaySettingsExW(device_identifier, byref(devmodew), None, 0, None) | ||
|
||
if result == DISP_CHANGE_SUCCESSFUL: | ||
return | ||
elif result == DISP_CHANGE_RESTART: | ||
raise DisplayAdapterException("The computer must be restarted for the graphics mode to work") | ||
elif result == DISP_CHANGE_BADFLAGS: | ||
raise DisplayAdapterException("An invalid set of flags was passed in") | ||
elif result == DISP_CHANGE_BADMODE: | ||
raise DisplayAdapterException("The graphics mode is not supported") | ||
elif result == DISP_CHANGE_BADPARAM: | ||
raise DisplayAdapterException("An invalid parameter was passed in") | ||
elif result == DISP_CHANGE_FAILED: | ||
raise DisplayAdapterException("The display driver failed the specified graphics mode") | ||
elif result == DISP_CHANGE_NOTUPDATED: | ||
raise DisplayAdapterException("Unable to write settings to the registry") | ||
elif result == DISP_CHANGE_BADDUALVIEW: | ||
raise DisplayAdapterException("The settings change was unsuccessful because the system is DualView capable") | ||
else: | ||
raise DisplayAdapterException("An unknown error occurred") | ||
|
||
except OSError as e: | ||
raise DisplayAdapterException(f"Failed to change display settings. Failed with error {str(e)}") |
Oops, something went wrong.