screen_brightness_control.windows
View Source
import wmi import threading import pythoncom import win32api import time import ctypes from ctypes import windll, byref, Structure, WinError, POINTER, WINFUNCTYPE from ctypes.wintypes import BOOL, HMONITOR, HDC, RECT, LPARAM, DWORD, BYTE, WCHAR, HANDLE from . import flatten_list, _monitor_brand_lookup, filter_monitors, __cache__, platform from typing import List, Union, Optional # a bunch of typing classes were deprecated in Python 3.9 # in favour of collections.abc (https://www.python.org/dev/peps/pep-0585/) if int(platform.python_version_tuple()[1]) < 9: from typing import Iterable else: from collections.abc import Iterable def _wmi_init(): '''internal function to create and return a wmi instance''' # WMI calls don't work in new threads so we have to run this check if threading.current_thread() != threading.main_thread(): pythoncom.CoInitialize() instance = wmi.WMI(namespace='wmi') return instance def get_display_info() -> List[dict]: ''' Gets information about all connected displays using WMI and win32api Returns: list: list of dictionaries Example: ```python import screen_brightness_control as s info = s.windows.get_display_info() for display in info: print(display['name']) ``` ''' try: info = __cache__.get('windows_monitors_info_raw') except Exception: info = [] try: # collect all monitor UIDs (derived from DeviceID) monitor_uids = {} last_good = 0 for i in win32api.EnumDisplayMonitors(): tmp = win32api.GetMonitorInfo(i[0]) try: # If a user has all displays plugged into display adapter 4 then # this statement would be tried for each adapter (0-4) per monitor # meaning 4 tries per monitor, 12 tries total. # By trying the "last good" adapter first, only the first display will # be tried 4 times. All the others will be tried once device = win32api.EnumDisplayDevices(tmp['Device'], last_good, 1) monitor_uids[device.DeviceID.split('#')[2]] = device except Exception: # iterate up to 4 display adapters for j in range(5): try: # last_good is tried just above here. No point trying it again if j != last_good: # i have no idea what this 3rd parameter does but the code doesn't work without it device = win32api.EnumDisplayDevices(tmp['Device'], j, 1) monitor_uids[device.DeviceID.split('#')[2]] = device last_good = j break except Exception: continue # gather list of laptop displays to check against later wmi = _wmi_init() try: laptop_displays = [] for i in wmi.WmiMonitorBrightness(): laptop_displays.append( i.InstanceName.replace('_0', '', 1).split('\\')[2] ) except Exception: pass monitors = [] extras = [] uid_keys = list(monitor_uids.keys()) for m in wmi.WmiMonitorID(): name = m.InstanceName.replace('_0', '', 1).split('\\')[2] if name in uid_keys: monitors.append(m) else: extras.append(m) # sort the monitors in the same order as win32api reports them # because the first item in win32api's list is usually the primary display monitors = sorted( monitors, key=lambda x: uid_keys.index(x.InstanceName.replace('_0', '', 1).split('\\')[2]) ) monitors += extras # get all available edid strings try: descriptors = { i.InstanceName: i.WmiGetMonitorRawEEdidV1Block(0) for i in wmi.WmiMonitorDescriptorMethods() } except Exception: pass laptop = 0 desktop = 0 for monitor in monitors: model, serial, manufacturer, man_id, edid = None, None, None, None, None instance_name = monitor.InstanceName.replace('_0', '', 1).split('\\')[2] pydevice = monitor_uids[instance_name] try: serial = bytes(monitor.SerialNumberID).decode().replace('\x00', '') manufacturer, model = bytes(monitor.UserFriendlyName).decode().replace('\x00', '').split(' ') manufacturer = manufacturer.lower().capitalize() try: man_id, manufacturer = _monitor_brand_lookup(manufacturer) except Exception: man_id = None except Exception: devid = pydevice.DeviceID.split('#') serial = devid[2] man_id = devid[1][:3] model = devid[3:] del(devid) try: man_id, manufacturer = _monitor_brand_lookup(man_id) except Exception: manufacturer = None try: try: chars = descriptors[monitor.InstanceName][0] except Exception: chars = descriptors[pydevice.InstanceName][0] finally: edid = '' for char in chars: char = str(hex(char)).replace('0x', '') if len(char) == 1: char = '0' + char edid += char del(chars) except Exception: edid = None if (serial, model) != (None, None): info.append( { 'name': f'{manufacturer} {model}', 'model': model, 'serial': serial, 'manufacturer': manufacturer, 'manufacturer_id': man_id, 'edid': edid } ) if instance_name in laptop_displays: info[-1]['index'] = laptop info[-1]['method'] = WMI laptop += 1 else: info[-1]['index'] = desktop info[-1]['method'] = VCP desktop += 1 except Exception: pass __cache__.store('windows_monitors_info_raw', info) return info class WMI: ''' A collection of screen brightness related methods using the WMI API. This class primarily works with laptop displays. ''' @classmethod def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: ''' Returns a list of dictionaries of info about all detected monitors Args: display (str or int): [*Optional*] the monitor to return info about. Pass in the serial number, name, model, edid or index Returns: list: list of dicts Example: ```python import screen_brightness_control as sbc info = sbc.windows.WMI.get_display_info() for i in info: print('================') for key, value in i.items(): print(key, ':', value) # get information about the first WMI addressable monitor primary_info = sbc.windows.WMI.get_display_info(0) # get information about a monitor with a specific name benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H') ``` ''' try: info = __cache__.get('wmi_monitor_info') except Exception: info = [i for i in get_display_info() if i['method'] == cls] __cache__.store('wmi_monitor_info', info) if display is not None: info = filter_monitors(display=display, haystack=info) return info @classmethod def get_display_names(cls) -> List[str]: ''' Returns names of all displays that can be addressed by WMI Returns: list: list of strings Example: ```python import screen_brightness_control as sbc for name in sbc.windows.WMI.get_display_names(): print(name) ``` ''' return [i['name'] for i in cls.get_display_info()] @classmethod def set_brightness( cls, value: int, display: Optional[Union[int, str]] = None, no_return: bool = False ) -> Union[List[int], None]: ''' Sets the display brightness for Windows using WMI Args: value (int): The percentage to set the brightness to display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. no_return (bool): if True, this function returns None Otherwise it returns the result of `WMI.get_brightness()` Returns: list: list of integers (0 to 100) None: if `no_return` is set to `True` Raises: LookupError: if the given display cannot be found Example: ```python import screen_brightness_control as sbc # set brightness of WMI addressable monitors to 50% sbc.windows.WMI.set_brightness(50) # set the primary display brightness to 75% sbc.windows.WMI.set_brightness(75, display = 0) # set the brightness of a named monitor to 25% sbc.windows.WMI.set_brightness(25, display = 'BenQ GL2450H') ``` ''' brightness_method = _wmi_init().WmiMonitorBrightnessMethods() if display is not None: if type(display) == int: brightness_method = [brightness_method[display]] else: indexes = [i['index'] for i in filter_monitors(display=display, method='wmi')] brightness_method = [brightness_method[i] for i in indexes] for method in brightness_method: method.WmiSetBrightness(value, 0) return cls.get_brightness(display=display) if not no_return else None @classmethod def get_brightness(cls, display: Optional[Union[int, str]] = None) -> List[int]: ''' Returns the current display brightness using WMI Args: display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. Returns: list: list of integers (0 to 100) Raises: LookupError: if the given display cannot be found Example: ```python import screen_brightness_control as sbc # get brightness of all WMI addressable monitors current_brightness = sbc.windows.WMI.get_brightness() if type(current_brightness) is int: print('There is only one detected display') else: print('There are', len(current_brightness), 'detected displays') # get the primary display brightness primary_brightness = sbc.windows.WMI.get_brightness(display = 0) # get the brightness of a named monitor benq_brightness = sbc.windows.WMI.get_brightness(display = 'BenQ GL2450H') ``` ''' brightness_method = _wmi_init().WmiMonitorBrightness() if display is not None: if type(display) == int: brightness_method = [brightness_method[display]] else: displays = cls.get_display_info(display) brightness_method = [brightness_method[i['index']] for i in displays] values = [i.CurrentBrightness for i in brightness_method] return values class VCP: '''Collection of screen brightness related methods using the DDC/CI commands''' _MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM) class _PHYSICAL_MONITOR(Structure): '''internal class, do not call''' _fields_ = [('handle', HANDLE), ('description', WCHAR * 128)] @classmethod def iter_physical_monitors(cls) -> Iterable[ctypes.wintypes.HANDLE]: ''' A generator to iterate through all physical monitors and then close them again afterwards, yielding their handles. It is not recommended to use this function unless you are familiar with `ctypes` and `windll` Returns: generator: generator that yields `ctypes.wintypes.HANDLE` objects Raises: ctypes.WinError: upon failure to enumerate through the monitors Example: ```python import screen_brightness_control as sbc for monitor in sbc.windows.VCP.iter_physical_monitors(): print(sbc.windows.VCP.get_monitor_caps(monitor)) ``` ''' def callback(hmonitor, hdc, lprect, lparam): monitors.append(HMONITOR(hmonitor)) return True monitors = [] if not windll.user32.EnumDisplayMonitors(None, None, cls._MONITORENUMPROC(callback), None): raise WinError('EnumDisplayMonitors failed') for monitor in monitors: # Get physical monitor count count = DWORD() if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)): raise WinError() if count.value > 0: # Get physical monitor handles physical_array = (cls._PHYSICAL_MONITOR * count.value)() if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array): raise WinError() for item in physical_array: yield item.handle windll.dxva2.DestroyPhysicalMonitor(item.handle) @classmethod def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: ''' Returns a dictionary of info about all detected monitors or a selection of monitors Args: display (int or str): [*Optional*] the monitor to return info about. Pass in the serial number, name, model, edid or index Returns: list: list of dicts Example: ```python import screen_brightness_control as sbc # get the information about all monitors vcp_info = sbc.windows.VCP.get_display_info() print(vcp_info) # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] # get information about a monitor with this specific model bnq_info = sbc.windows.VCP.get_display_info('GL2450H') # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... } ``` ''' try: info = __cache__.get('vcp_monitor_info') except Exception: info = [i for i in get_display_info() if i['method'] == cls] __cache__.store('vcp_monitor_info', info) if display is not None: info = filter_monitors(display=display, haystack=info) return info @staticmethod def get_monitor_caps(monitor: ctypes.wintypes.HANDLE) -> str: ''' Fetches and returns the VCP capabilities string of a monitor. This function takes anywhere from 1-2 seconds to run Args: monitor: a monitor handle as returned by `VCP.iter_physical_monitors()` Returns: str: a string of the monitor's capabilities Examples: ```python import screen_brightness_control as sbc for monitor in sbc.windows.VCP.iter_physical_monitors(): print(sbc.windows.VCP.get_monitor_caps(monitor)) # EG output: '(prot(monitor)type(LCD)model(GL2450HM)cmds(01 02 03 07 0C F3)vcp(02...)' ``` ''' caps_string_length = DWORD() if not windll.dxva2.GetCapabilitiesStringLength(monitor, ctypes.byref(caps_string_length)): return caps_string = (ctypes.c_char * caps_string_length.value)() if not windll.dxva2.CapabilitiesRequestAndCapabilitiesReply(monitor, caps_string, caps_string_length): return return caps_string.value.decode('ASCII') @classmethod def get_display_names(cls) -> List[str]: ''' Return the names of each detected monitor Returns: list: list of strings Example: ```python import screen_brightness_control as sbc names = sbc.windows.VCP.get_display_names() print(names) # EG output: ['BenQ GL2450H', 'Dell U2211H'] ``` ''' return [i['name'] for i in cls.get_display_info()] @classmethod def get_brightness(cls, display: Optional[Union[int, str]] = None) -> List[int]: ''' Retrieve the brightness of all connected displays using the `ctypes.windll` API Args: display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. Returns: list: list of ints (0 to 100) Examples: ```python import screen_brightness_control as sbc # Get the brightness for all detected displays current_brightness = sbc.windows.VCP.get_brightness() print('There are', len(current_brightness), 'detected displays') # Get the brightness for the primary display primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] # Get the brightness for a secondary display secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] # Get the brightness for a display with the model 'GL2450H' benq_brightness = sbc.windows.VCP.get_brightness(display = 'GL2450H')[0] ``` ''' # filter monitors even if display kwarg is not specified due to oddities surrounding issues #7 and #8 # https://github.com/Crozzers/screen_brightness_control/issues/7 # https://github.com/Crozzers/screen_brightness_control/issues/8 # essentially, we get 'ghost' monitors showing up here that cannot actually # be adjusted (even if no error gets raised) so we use this to filter out # such ghost monitors by only attempting to get the brightness for valid monitors # (determined by VCP.get_display_info) # yes, it does add an unnecessary function call but that's only if you're using this module low-level. # Top-level functions always end up specifying the display kwarg anyway if type(display) == int: indexes = [display] else: indexes = [i['index'] for i in filter_monitors(display=display, haystack=cls.get_display_info())] count = 0 values = [] for m in cls.iter_physical_monitors(): try: v = __cache__.get(f'vcp_brightness_{count}') except Exception: cur_out = DWORD() for _ in range(10): if windll.dxva2.GetVCPFeatureAndVCPFeatureReply(HANDLE(m), BYTE(0x10), None, byref(cur_out), None): v = cur_out.value break else: time.sleep(0.02) v = None del(cur_out) if v is not None: if count in indexes: try: __cache__.store(f'vcp_brightness_{count}', v, expires=0.1) except IndexError: pass values.append(v) if count >= max(indexes): break count += 1 return values @classmethod def set_brightness( cls, value: int, display: Optional[Union[int, str]] = None, no_return: bool = False ) -> Union[List[int], None]: ''' Sets the brightness for all connected displays using the `ctypes.windll` API Args: display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. no_return (bool): if set to `True` this function will return `None` Returns: list: list of ints (0 to 100) (the result of `VCP.get_brightness`) if `no_return` is False None: if `no_return` is True Examples: ```python import screen_brightness_control as sbc # Set the brightness for all detected displays to 50% sbc.windows.VCP.set_brightness(50) # Set the brightness for the primary display to 75% sbc.windows.VCP.set_brightness(75, display = 0) # Set the brightness for a secondary display to 25% sbc.windows.VCP.set_brightness(25, display = 1) # Set the brightness for a display with the model 'GL2450H' to 100% sbc.windows.VCP.set_brightness(100, display = 'GL2450H') ``` ''' if type(display) == int: indexes = [display] else: # see VCP.set_brightness for the explanation for why we always gather this list indexes = [i['index'] for i in filter_monitors(display=display, haystack=cls.get_display_info())] __cache__.expire(startswith='vcp_brightness_') count = 0 for m in cls.iter_physical_monitors(): if display is None or (count in indexes): for _ in range(10): if windll.dxva2.SetVCPFeature(HANDLE(m), BYTE(0x10), DWORD(value)): break else: time.sleep(0.02) count += 1 return cls.get_brightness(display=display) if not no_return else None def list_monitors_info(method: Optional[str] = None, allow_duplicates: bool = False) -> List[dict]: ''' Lists detailed information about all detected monitors Args: method (str): the method the monitor can be addressed by. Can be 'wmi' or 'vcp' allow_duplicates (bool): whether to filter out duplicate displays (displays with the same EDID) or not Returns: list: list of dicts upon success, empty list upon failure Raises: ValueError: if the method kwarg is invalid Example: ```python import screen_brightness_control as sbc monitors = sbc.windows.list_monitors_info() for info in monitors: print('=======================') # the manufacturer name plus the model print('Name:', info['name']) # the general model of the display print('Model:', info['model']) # a unique string assigned by Windows to this display print('Serial:', info['serial']) # the name of the brand of the monitor print('Manufacturer:', info['manufacturer']) # the 3 letter code corresponding to the brand name, EG: BNQ -> BenQ print('Manufacturer ID:', info['manufacturer_id']) # the index of that display FOR THE SPECIFIC METHOD THE DISPLAY USES print('Index:', info['index']) # the method this monitor can be addressed by print('Method:', info['method']) # the EDID string of the monitor print('EDID:', info['edid']) ``` ''' try: return __cache__.get(f'windows_monitors_info_{method}_{allow_duplicates}') except Exception: info = get_display_info() if method is not None: method = method.lower() if method not in ('wmi', 'vcp'): raise ValueError('method kwarg must be \'wmi\' or \'vcp\'') info_final = [] serials = [] # to make sure each display (with unique edid) is only reported once for i in info: if allow_duplicates or i['serial'] not in serials: if method is None or method == i['method'].__name__.lower(): serials.append(i['serial']) info_final.append(i) __cache__.store(f'windows_monitors_info_{method}_{allow_duplicates}', info_final) return info_final def list_monitors(method: Optional[str] = None) -> List[str]: ''' Returns a list of all addressable monitor names Args: method (str): the method the monitor can be addressed by. Can be 'wmi' or 'vcp' Returns: list: list of strings Example: ```python import screen_brightness_control as sbc monitors = sbc.windows.list_monitors() # EG output: ['BenQ GL2450H', 'Dell U2211H'] ``` ''' return [i['name'] for i in list_monitors_info(method=method)] def __set_and_get_brightness(*args, display=None, method=None, meta_method='get', **kwargs) -> Union[List[int], None]: ''' Internal function, do not call. Either sets the brightness or gets it. Exists because set_brightness and get_brightness only have a couple differences ''' errors = [] try: # filter known list of monitors according to kwargs if type(display) == int: monitors = [list_monitors_info(method=method)[display]] else: monitors = filter_monitors(display=display, method=method) except Exception as e: errors.append(['', type(e).__name__, e]) else: output = [] for m in monitors: # add the output of each brightness method to the output list try: output.append( getattr(m['method'], meta_method + '_brightness')(*args, display=m['index'], **kwargs) ) except Exception as e: output.append(None) errors.append([f"{m['name']} ({m['serial']})", type(e).__name__, e]) # use `'no_return' not in kwargs` because dict membership only checks the keys if ( output and not (all(i in (None, []) for i in output) and ('no_return' not in kwargs or not kwargs['no_return'])) ): # flatten and return any output (taking into account the no_return parameter) if 'no_return' in kwargs and kwargs['no_return']: return None output = flatten_list(output) return output # if function hasn't already returned it has failed msg = '\n' for e in errors: msg += f'\t{e[0]} -> {e[1]}: {e[2]}\n' if msg == '\n': msg += '\tno valid output was received from brightness methods' raise Exception(msg) def set_brightness( value: int, display: Optional[Union[int, str]] = None, method: Optional[str] = None, **kwargs ) -> Union[List[int], None]: ''' Sets the brightness of any connected monitors Args: value (int): Sets the brightness to this value as a percentage display (int or str): The specific display you wish to adjust. Can be index, model, name or serial of the display method (str): the method to use ('wmi' or 'vcp') kwargs (dict): passed directly to the chosen brightness method Returns: list: list of ints (0 to 100) (the result of `get_brightness`) None: if `no_return` is True Raises: LookupError: if the chosen display (with method if applicable) is not found ValueError: if the chosen method is invalid TypeError: if the value given for `display` is not int or str Exception: if the brightness could not be set by any method Example: ```python import screen_brightness_control as sbc # set the current brightness to 50% sbc.windows.set_brightness(50) # set the brightness of the primary display to 75% sbc.windows.set_brightness(75, display = 0) # set the brightness of any displays using VCP to 25% sbc.windows.set_brightness(25, method = 'vcp') # set the brightness of displays with model name 'BenQ GL2450H' to 100% sbc.windows.set_brightness(100, display = 'BenQ GL2450H') ``` ''' # this function is called because set_brightness and get_brightness only differed by 1 line of code # so I made another internal function to reduce the filesize return __set_and_get_brightness(value, display=display, method=method, meta_method='set', **kwargs) def get_brightness(display: Optional[Union[int, str]] = None, method: Optional[str] = None, **kwargs) -> List[int]: ''' Returns the brightness of any connected monitors Args: display (int or str): The specific display you wish to adjust. Can be index, model, name or serial of the display method (str): the method to use ('wmi' or 'vcp') kwargs (dict): passed directly to chosen brightness method Returns: list: list of ints (0 to 100) Raises: LookupError: if the chosen display (with method if applicable) is not found ValueError: if the chosen method is invalid TypeError: if the value given for `display` is not int or str Exception: if the brightness could not be obtained by any method Example: ```python import screen_brightness_control as sbc # get the current brightness current_brightness = sbc.windows.get_brightness() if type(current_brightness) is int: print('There is only one detected display') else: print('There are', len(current_brightness), 'detected displays') # get the brightness of the primary display primary_brightness = sbc.windows.get_brightness(display=0)[0] # get the brightness of any displays using VCP vcp_brightness = sbc.windows.get_brightness(method='vcp') # get the brightness of displays with the model name 'BenQ GL2450H' benq_brightness = sbc.windows.get_brightness(display='BenQ GL2450H')[0] ``` ''' # this function is called because set_brightness and get_brightness only differed by 1 line of code # so I made another internal function to reduce the filesize return __set_and_get_brightness(display=display, method=method, meta_method='get', **kwargs)
View Source
def get_display_info() -> List[dict]: ''' Gets information about all connected displays using WMI and win32api Returns: list: list of dictionaries Example: ```python import screen_brightness_control as s info = s.windows.get_display_info() for display in info: print(display['name']) ``` ''' try: info = __cache__.get('windows_monitors_info_raw') except Exception: info = [] try: # collect all monitor UIDs (derived from DeviceID) monitor_uids = {} last_good = 0 for i in win32api.EnumDisplayMonitors(): tmp = win32api.GetMonitorInfo(i[0]) try: # If a user has all displays plugged into display adapter 4 then # this statement would be tried for each adapter (0-4) per monitor # meaning 4 tries per monitor, 12 tries total. # By trying the "last good" adapter first, only the first display will # be tried 4 times. All the others will be tried once device = win32api.EnumDisplayDevices(tmp['Device'], last_good, 1) monitor_uids[device.DeviceID.split('#')[2]] = device except Exception: # iterate up to 4 display adapters for j in range(5): try: # last_good is tried just above here. No point trying it again if j != last_good: # i have no idea what this 3rd parameter does but the code doesn't work without it device = win32api.EnumDisplayDevices(tmp['Device'], j, 1) monitor_uids[device.DeviceID.split('#')[2]] = device last_good = j break except Exception: continue # gather list of laptop displays to check against later wmi = _wmi_init() try: laptop_displays = [] for i in wmi.WmiMonitorBrightness(): laptop_displays.append( i.InstanceName.replace('_0', '', 1).split('\\')[2] ) except Exception: pass monitors = [] extras = [] uid_keys = list(monitor_uids.keys()) for m in wmi.WmiMonitorID(): name = m.InstanceName.replace('_0', '', 1).split('\\')[2] if name in uid_keys: monitors.append(m) else: extras.append(m) # sort the monitors in the same order as win32api reports them # because the first item in win32api's list is usually the primary display monitors = sorted( monitors, key=lambda x: uid_keys.index(x.InstanceName.replace('_0', '', 1).split('\\')[2]) ) monitors += extras # get all available edid strings try: descriptors = { i.InstanceName: i.WmiGetMonitorRawEEdidV1Block(0) for i in wmi.WmiMonitorDescriptorMethods() } except Exception: pass laptop = 0 desktop = 0 for monitor in monitors: model, serial, manufacturer, man_id, edid = None, None, None, None, None instance_name = monitor.InstanceName.replace('_0', '', 1).split('\\')[2] pydevice = monitor_uids[instance_name] try: serial = bytes(monitor.SerialNumberID).decode().replace('\x00', '') manufacturer, model = bytes(monitor.UserFriendlyName).decode().replace('\x00', '').split(' ') manufacturer = manufacturer.lower().capitalize() try: man_id, manufacturer = _monitor_brand_lookup(manufacturer) except Exception: man_id = None except Exception: devid = pydevice.DeviceID.split('#') serial = devid[2] man_id = devid[1][:3] model = devid[3:] del(devid) try: man_id, manufacturer = _monitor_brand_lookup(man_id) except Exception: manufacturer = None try: try: chars = descriptors[monitor.InstanceName][0] except Exception: chars = descriptors[pydevice.InstanceName][0] finally: edid = '' for char in chars: char = str(hex(char)).replace('0x', '') if len(char) == 1: char = '0' + char edid += char del(chars) except Exception: edid = None if (serial, model) != (None, None): info.append( { 'name': f'{manufacturer} {model}', 'model': model, 'serial': serial, 'manufacturer': manufacturer, 'manufacturer_id': man_id, 'edid': edid } ) if instance_name in laptop_displays: info[-1]['index'] = laptop info[-1]['method'] = WMI laptop += 1 else: info[-1]['index'] = desktop info[-1]['method'] = VCP desktop += 1 except Exception: pass __cache__.store('windows_monitors_info_raw', info) return info
Gets information about all connected displays using WMI and win32api
Returns
- list: list of dictionaries
Example
import screen_brightness_control as s info = s.windows.get_display_info() for display in info: print(display['name'])
View Source
class WMI: ''' A collection of screen brightness related methods using the WMI API. This class primarily works with laptop displays. ''' @classmethod def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: ''' Returns a list of dictionaries of info about all detected monitors Args: display (str or int): [*Optional*] the monitor to return info about. Pass in the serial number, name, model, edid or index Returns: list: list of dicts Example: ```python import screen_brightness_control as sbc info = sbc.windows.WMI.get_display_info() for i in info: print('================') for key, value in i.items(): print(key, ':', value) # get information about the first WMI addressable monitor primary_info = sbc.windows.WMI.get_display_info(0) # get information about a monitor with a specific name benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H') ``` ''' try: info = __cache__.get('wmi_monitor_info') except Exception: info = [i for i in get_display_info() if i['method'] == cls] __cache__.store('wmi_monitor_info', info) if display is not None: info = filter_monitors(display=display, haystack=info) return info @classmethod def get_display_names(cls) -> List[str]: ''' Returns names of all displays that can be addressed by WMI Returns: list: list of strings Example: ```python import screen_brightness_control as sbc for name in sbc.windows.WMI.get_display_names(): print(name) ``` ''' return [i['name'] for i in cls.get_display_info()] @classmethod def set_brightness( cls, value: int, display: Optional[Union[int, str]] = None, no_return: bool = False ) -> Union[List[int], None]: ''' Sets the display brightness for Windows using WMI Args: value (int): The percentage to set the brightness to display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. no_return (bool): if True, this function returns None Otherwise it returns the result of `WMI.get_brightness()` Returns: list: list of integers (0 to 100) None: if `no_return` is set to `True` Raises: LookupError: if the given display cannot be found Example: ```python import screen_brightness_control as sbc # set brightness of WMI addressable monitors to 50% sbc.windows.WMI.set_brightness(50) # set the primary display brightness to 75% sbc.windows.WMI.set_brightness(75, display = 0) # set the brightness of a named monitor to 25% sbc.windows.WMI.set_brightness(25, display = 'BenQ GL2450H') ``` ''' brightness_method = _wmi_init().WmiMonitorBrightnessMethods() if display is not None: if type(display) == int: brightness_method = [brightness_method[display]] else: indexes = [i['index'] for i in filter_monitors(display=display, method='wmi')] brightness_method = [brightness_method[i] for i in indexes] for method in brightness_method: method.WmiSetBrightness(value, 0) return cls.get_brightness(display=display) if not no_return else None @classmethod def get_brightness(cls, display: Optional[Union[int, str]] = None) -> List[int]: ''' Returns the current display brightness using WMI Args: display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. Returns: list: list of integers (0 to 100) Raises: LookupError: if the given display cannot be found Example: ```python import screen_brightness_control as sbc # get brightness of all WMI addressable monitors current_brightness = sbc.windows.WMI.get_brightness() if type(current_brightness) is int: print('There is only one detected display') else: print('There are', len(current_brightness), 'detected displays') # get the primary display brightness primary_brightness = sbc.windows.WMI.get_brightness(display = 0) # get the brightness of a named monitor benq_brightness = sbc.windows.WMI.get_brightness(display = 'BenQ GL2450H') ``` ''' brightness_method = _wmi_init().WmiMonitorBrightness() if display is not None: if type(display) == int: brightness_method = [brightness_method[display]] else: displays = cls.get_display_info(display) brightness_method = [brightness_method[i['index']] for i in displays] values = [i.CurrentBrightness for i in brightness_method] return values
A collection of screen brightness related methods using the WMI API. This class primarily works with laptop displays.
View Source
@classmethod def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: ''' Returns a list of dictionaries of info about all detected monitors Args: display (str or int): [*Optional*] the monitor to return info about. Pass in the serial number, name, model, edid or index Returns: list: list of dicts Example: ```python import screen_brightness_control as sbc info = sbc.windows.WMI.get_display_info() for i in info: print('================') for key, value in i.items(): print(key, ':', value) # get information about the first WMI addressable monitor primary_info = sbc.windows.WMI.get_display_info(0) # get information about a monitor with a specific name benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H') ``` ''' try: info = __cache__.get('wmi_monitor_info') except Exception: info = [i for i in get_display_info() if i['method'] == cls] __cache__.store('wmi_monitor_info', info) if display is not None: info = filter_monitors(display=display, haystack=info) return info
Returns a list of dictionaries of info about all detected monitors
Args
- display (str or int): [Optional] the monitor to return info about. Pass in the serial number, name, model, edid or index
Returns
- list: list of dicts
Example
import screen_brightness_control as sbc info = sbc.windows.WMI.get_display_info() for i in info: print('================') for key, value in i.items(): print(key, ':', value) # get information about the first WMI addressable monitor primary_info = sbc.windows.WMI.get_display_info(0) # get information about a monitor with a specific name benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H')
View Source
@classmethod def get_display_names(cls) -> List[str]: ''' Returns names of all displays that can be addressed by WMI Returns: list: list of strings Example: ```python import screen_brightness_control as sbc for name in sbc.windows.WMI.get_display_names(): print(name) ``` ''' return [i['name'] for i in cls.get_display_info()]
Returns names of all displays that can be addressed by WMI
Returns
- list: list of strings
Example
import screen_brightness_control as sbc for name in sbc.windows.WMI.get_display_names(): print(name)
View Source
@classmethod def set_brightness( cls, value: int, display: Optional[Union[int, str]] = None, no_return: bool = False ) -> Union[List[int], None]: ''' Sets the display brightness for Windows using WMI Args: value (int): The percentage to set the brightness to display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. no_return (bool): if True, this function returns None Otherwise it returns the result of `WMI.get_brightness()` Returns: list: list of integers (0 to 100) None: if `no_return` is set to `True` Raises: LookupError: if the given display cannot be found Example: ```python import screen_brightness_control as sbc # set brightness of WMI addressable monitors to 50% sbc.windows.WMI.set_brightness(50) # set the primary display brightness to 75% sbc.windows.WMI.set_brightness(75, display = 0) # set the brightness of a named monitor to 25% sbc.windows.WMI.set_brightness(25, display = 'BenQ GL2450H') ``` ''' brightness_method = _wmi_init().WmiMonitorBrightnessMethods() if display is not None: if type(display) == int: brightness_method = [brightness_method[display]] else: indexes = [i['index'] for i in filter_monitors(display=display, method='wmi')] brightness_method = [brightness_method[i] for i in indexes] for method in brightness_method: method.WmiSetBrightness(value, 0) return cls.get_brightness(display=display) if not no_return else None
Sets the display brightness for Windows using WMI
Args
- value (int): The percentage to set the brightness to
- display (int or str): The specific display you wish to query.
Can be index, name, model, serial or edid string.
int
is faster as it isn't passed tofilter_monitors
to be matched against.str
is slower as it is passed tofilter_monitors
to match to a display. - no_return (bool): if True, this function returns None
Otherwise it returns the result of
WMI.get_brightness()
Returns
- list: list of integers (0 to 100)
- None: if
no_return
is set toTrue
Raises
- LookupError: if the given display cannot be found
Example
import screen_brightness_control as sbc # set brightness of WMI addressable monitors to 50% sbc.windows.WMI.set_brightness(50) # set the primary display brightness to 75% sbc.windows.WMI.set_brightness(75, display = 0) # set the brightness of a named monitor to 25% sbc.windows.WMI.set_brightness(25, display = 'BenQ GL2450H')
View Source
@classmethod def get_brightness(cls, display: Optional[Union[int, str]] = None) -> List[int]: ''' Returns the current display brightness using WMI Args: display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. Returns: list: list of integers (0 to 100) Raises: LookupError: if the given display cannot be found Example: ```python import screen_brightness_control as sbc # get brightness of all WMI addressable monitors current_brightness = sbc.windows.WMI.get_brightness() if type(current_brightness) is int: print('There is only one detected display') else: print('There are', len(current_brightness), 'detected displays') # get the primary display brightness primary_brightness = sbc.windows.WMI.get_brightness(display = 0) # get the brightness of a named monitor benq_brightness = sbc.windows.WMI.get_brightness(display = 'BenQ GL2450H') ``` ''' brightness_method = _wmi_init().WmiMonitorBrightness() if display is not None: if type(display) == int: brightness_method = [brightness_method[display]] else: displays = cls.get_display_info(display) brightness_method = [brightness_method[i['index']] for i in displays] values = [i.CurrentBrightness for i in brightness_method] return values
Returns the current display brightness using WMI
Args
- display (int or str): The specific display you wish to query.
Can be index, name, model, serial or edid string.
int
is faster as it isn't passed tofilter_monitors
to be matched against.str
is slower as it is passed tofilter_monitors
to match to a display.
Returns
- list: list of integers (0 to 100)
Raises
- LookupError: if the given display cannot be found
Example
import screen_brightness_control as sbc # get brightness of all WMI addressable monitors current_brightness = sbc.windows.WMI.get_brightness() if type(current_brightness) is int: print('There is only one detected display') else: print('There are', len(current_brightness), 'detected displays') # get the primary display brightness primary_brightness = sbc.windows.WMI.get_brightness(display = 0) # get the brightness of a named monitor benq_brightness = sbc.windows.WMI.get_brightness(display = 'BenQ GL2450H')
View Source
class VCP: '''Collection of screen brightness related methods using the DDC/CI commands''' _MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM) class _PHYSICAL_MONITOR(Structure): '''internal class, do not call''' _fields_ = [('handle', HANDLE), ('description', WCHAR * 128)] @classmethod def iter_physical_monitors(cls) -> Iterable[ctypes.wintypes.HANDLE]: ''' A generator to iterate through all physical monitors and then close them again afterwards, yielding their handles. It is not recommended to use this function unless you are familiar with `ctypes` and `windll` Returns: generator: generator that yields `ctypes.wintypes.HANDLE` objects Raises: ctypes.WinError: upon failure to enumerate through the monitors Example: ```python import screen_brightness_control as sbc for monitor in sbc.windows.VCP.iter_physical_monitors(): print(sbc.windows.VCP.get_monitor_caps(monitor)) ``` ''' def callback(hmonitor, hdc, lprect, lparam): monitors.append(HMONITOR(hmonitor)) return True monitors = [] if not windll.user32.EnumDisplayMonitors(None, None, cls._MONITORENUMPROC(callback), None): raise WinError('EnumDisplayMonitors failed') for monitor in monitors: # Get physical monitor count count = DWORD() if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)): raise WinError() if count.value > 0: # Get physical monitor handles physical_array = (cls._PHYSICAL_MONITOR * count.value)() if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array): raise WinError() for item in physical_array: yield item.handle windll.dxva2.DestroyPhysicalMonitor(item.handle) @classmethod def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: ''' Returns a dictionary of info about all detected monitors or a selection of monitors Args: display (int or str): [*Optional*] the monitor to return info about. Pass in the serial number, name, model, edid or index Returns: list: list of dicts Example: ```python import screen_brightness_control as sbc # get the information about all monitors vcp_info = sbc.windows.VCP.get_display_info() print(vcp_info) # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] # get information about a monitor with this specific model bnq_info = sbc.windows.VCP.get_display_info('GL2450H') # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... } ``` ''' try: info = __cache__.get('vcp_monitor_info') except Exception: info = [i for i in get_display_info() if i['method'] == cls] __cache__.store('vcp_monitor_info', info) if display is not None: info = filter_monitors(display=display, haystack=info) return info @staticmethod def get_monitor_caps(monitor: ctypes.wintypes.HANDLE) -> str: ''' Fetches and returns the VCP capabilities string of a monitor. This function takes anywhere from 1-2 seconds to run Args: monitor: a monitor handle as returned by `VCP.iter_physical_monitors()` Returns: str: a string of the monitor's capabilities Examples: ```python import screen_brightness_control as sbc for monitor in sbc.windows.VCP.iter_physical_monitors(): print(sbc.windows.VCP.get_monitor_caps(monitor)) # EG output: '(prot(monitor)type(LCD)model(GL2450HM)cmds(01 02 03 07 0C F3)vcp(02...)' ``` ''' caps_string_length = DWORD() if not windll.dxva2.GetCapabilitiesStringLength(monitor, ctypes.byref(caps_string_length)): return caps_string = (ctypes.c_char * caps_string_length.value)() if not windll.dxva2.CapabilitiesRequestAndCapabilitiesReply(monitor, caps_string, caps_string_length): return return caps_string.value.decode('ASCII') @classmethod def get_display_names(cls) -> List[str]: ''' Return the names of each detected monitor Returns: list: list of strings Example: ```python import screen_brightness_control as sbc names = sbc.windows.VCP.get_display_names() print(names) # EG output: ['BenQ GL2450H', 'Dell U2211H'] ``` ''' return [i['name'] for i in cls.get_display_info()] @classmethod def get_brightness(cls, display: Optional[Union[int, str]] = None) -> List[int]: ''' Retrieve the brightness of all connected displays using the `ctypes.windll` API Args: display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. Returns: list: list of ints (0 to 100) Examples: ```python import screen_brightness_control as sbc # Get the brightness for all detected displays current_brightness = sbc.windows.VCP.get_brightness() print('There are', len(current_brightness), 'detected displays') # Get the brightness for the primary display primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] # Get the brightness for a secondary display secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] # Get the brightness for a display with the model 'GL2450H' benq_brightness = sbc.windows.VCP.get_brightness(display = 'GL2450H')[0] ``` ''' # filter monitors even if display kwarg is not specified due to oddities surrounding issues #7 and #8 # https://github.com/Crozzers/screen_brightness_control/issues/7 # https://github.com/Crozzers/screen_brightness_control/issues/8 # essentially, we get 'ghost' monitors showing up here that cannot actually # be adjusted (even if no error gets raised) so we use this to filter out # such ghost monitors by only attempting to get the brightness for valid monitors # (determined by VCP.get_display_info) # yes, it does add an unnecessary function call but that's only if you're using this module low-level. # Top-level functions always end up specifying the display kwarg anyway if type(display) == int: indexes = [display] else: indexes = [i['index'] for i in filter_monitors(display=display, haystack=cls.get_display_info())] count = 0 values = [] for m in cls.iter_physical_monitors(): try: v = __cache__.get(f'vcp_brightness_{count}') except Exception: cur_out = DWORD() for _ in range(10): if windll.dxva2.GetVCPFeatureAndVCPFeatureReply(HANDLE(m), BYTE(0x10), None, byref(cur_out), None): v = cur_out.value break else: time.sleep(0.02) v = None del(cur_out) if v is not None: if count in indexes: try: __cache__.store(f'vcp_brightness_{count}', v, expires=0.1) except IndexError: pass values.append(v) if count >= max(indexes): break count += 1 return values @classmethod def set_brightness( cls, value: int, display: Optional[Union[int, str]] = None, no_return: bool = False ) -> Union[List[int], None]: ''' Sets the brightness for all connected displays using the `ctypes.windll` API Args: display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. no_return (bool): if set to `True` this function will return `None` Returns: list: list of ints (0 to 100) (the result of `VCP.get_brightness`) if `no_return` is False None: if `no_return` is True Examples: ```python import screen_brightness_control as sbc # Set the brightness for all detected displays to 50% sbc.windows.VCP.set_brightness(50) # Set the brightness for the primary display to 75% sbc.windows.VCP.set_brightness(75, display = 0) # Set the brightness for a secondary display to 25% sbc.windows.VCP.set_brightness(25, display = 1) # Set the brightness for a display with the model 'GL2450H' to 100% sbc.windows.VCP.set_brightness(100, display = 'GL2450H') ``` ''' if type(display) == int: indexes = [display] else: # see VCP.set_brightness for the explanation for why we always gather this list indexes = [i['index'] for i in filter_monitors(display=display, haystack=cls.get_display_info())] __cache__.expire(startswith='vcp_brightness_') count = 0 for m in cls.iter_physical_monitors(): if display is None or (count in indexes): for _ in range(10): if windll.dxva2.SetVCPFeature(HANDLE(m), BYTE(0x10), DWORD(value)): break else: time.sleep(0.02) count += 1 return cls.get_brightness(display=display) if not no_return else None
Collection of screen brightness related methods using the DDC/CI commands
View Source
@classmethod def iter_physical_monitors(cls) -> Iterable[ctypes.wintypes.HANDLE]: ''' A generator to iterate through all physical monitors and then close them again afterwards, yielding their handles. It is not recommended to use this function unless you are familiar with `ctypes` and `windll` Returns: generator: generator that yields `ctypes.wintypes.HANDLE` objects Raises: ctypes.WinError: upon failure to enumerate through the monitors Example: ```python import screen_brightness_control as sbc for monitor in sbc.windows.VCP.iter_physical_monitors(): print(sbc.windows.VCP.get_monitor_caps(monitor)) ``` ''' def callback(hmonitor, hdc, lprect, lparam): monitors.append(HMONITOR(hmonitor)) return True monitors = [] if not windll.user32.EnumDisplayMonitors(None, None, cls._MONITORENUMPROC(callback), None): raise WinError('EnumDisplayMonitors failed') for monitor in monitors: # Get physical monitor count count = DWORD() if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)): raise WinError() if count.value > 0: # Get physical monitor handles physical_array = (cls._PHYSICAL_MONITOR * count.value)() if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array): raise WinError() for item in physical_array: yield item.handle windll.dxva2.DestroyPhysicalMonitor(item.handle)
A generator to iterate through all physical monitors
and then close them again afterwards, yielding their handles.
It is not recommended to use this function unless you are familiar with ctypes
and windll
Returns
- generator: generator that yields
ctypes.wintypes.HANDLE
objects
Raises
- ctypes.WinError: upon failure to enumerate through the monitors
Example
import screen_brightness_control as sbc for monitor in sbc.windows.VCP.iter_physical_monitors(): print(sbc.windows.VCP.get_monitor_caps(monitor))
View Source
@classmethod def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: ''' Returns a dictionary of info about all detected monitors or a selection of monitors Args: display (int or str): [*Optional*] the monitor to return info about. Pass in the serial number, name, model, edid or index Returns: list: list of dicts Example: ```python import screen_brightness_control as sbc # get the information about all monitors vcp_info = sbc.windows.VCP.get_display_info() print(vcp_info) # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] # get information about a monitor with this specific model bnq_info = sbc.windows.VCP.get_display_info('GL2450H') # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... } ``` ''' try: info = __cache__.get('vcp_monitor_info') except Exception: info = [i for i in get_display_info() if i['method'] == cls] __cache__.store('vcp_monitor_info', info) if display is not None: info = filter_monitors(display=display, haystack=info) return info
Returns a dictionary of info about all detected monitors or a selection of monitors
Args
- display (int or str): [Optional] the monitor to return info about. Pass in the serial number, name, model, edid or index
Returns
- list: list of dicts
Example
import screen_brightness_control as sbc # get the information about all monitors vcp_info = sbc.windows.VCP.get_display_info() print(vcp_info) # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] # get information about a monitor with this specific model bnq_info = sbc.windows.VCP.get_display_info('GL2450H') # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... }
View Source
@staticmethod def get_monitor_caps(monitor: ctypes.wintypes.HANDLE) -> str: ''' Fetches and returns the VCP capabilities string of a monitor. This function takes anywhere from 1-2 seconds to run Args: monitor: a monitor handle as returned by `VCP.iter_physical_monitors()` Returns: str: a string of the monitor's capabilities Examples: ```python import screen_brightness_control as sbc for monitor in sbc.windows.VCP.iter_physical_monitors(): print(sbc.windows.VCP.get_monitor_caps(monitor)) # EG output: '(prot(monitor)type(LCD)model(GL2450HM)cmds(01 02 03 07 0C F3)vcp(02...)' ``` ''' caps_string_length = DWORD() if not windll.dxva2.GetCapabilitiesStringLength(monitor, ctypes.byref(caps_string_length)): return caps_string = (ctypes.c_char * caps_string_length.value)() if not windll.dxva2.CapabilitiesRequestAndCapabilitiesReply(monitor, caps_string, caps_string_length): return return caps_string.value.decode('ASCII')
Fetches and returns the VCP capabilities string of a monitor. This function takes anywhere from 1-2 seconds to run
Args
- monitor: a monitor handle as returned by
VCP.iter_physical_monitors()
Returns
- str: a string of the monitor's capabilities
Examples
import screen_brightness_control as sbc for monitor in sbc.windows.VCP.iter_physical_monitors(): print(sbc.windows.VCP.get_monitor_caps(monitor)) # EG output: '(prot(monitor)type(LCD)model(GL2450HM)cmds(01 02 03 07 0C F3)vcp(02...)'
View Source
@classmethod def get_display_names(cls) -> List[str]: ''' Return the names of each detected monitor Returns: list: list of strings Example: ```python import screen_brightness_control as sbc names = sbc.windows.VCP.get_display_names() print(names) # EG output: ['BenQ GL2450H', 'Dell U2211H'] ``` ''' return [i['name'] for i in cls.get_display_info()]
Return the names of each detected monitor
Returns
- list: list of strings
Example
import screen_brightness_control as sbc names = sbc.windows.VCP.get_display_names() print(names) # EG output: ['BenQ GL2450H', 'Dell U2211H']
View Source
@classmethod def get_brightness(cls, display: Optional[Union[int, str]] = None) -> List[int]: ''' Retrieve the brightness of all connected displays using the `ctypes.windll` API Args: display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. Returns: list: list of ints (0 to 100) Examples: ```python import screen_brightness_control as sbc # Get the brightness for all detected displays current_brightness = sbc.windows.VCP.get_brightness() print('There are', len(current_brightness), 'detected displays') # Get the brightness for the primary display primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] # Get the brightness for a secondary display secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] # Get the brightness for a display with the model 'GL2450H' benq_brightness = sbc.windows.VCP.get_brightness(display = 'GL2450H')[0] ``` ''' # filter monitors even if display kwarg is not specified due to oddities surrounding issues #7 and #8 # https://github.com/Crozzers/screen_brightness_control/issues/7 # https://github.com/Crozzers/screen_brightness_control/issues/8 # essentially, we get 'ghost' monitors showing up here that cannot actually # be adjusted (even if no error gets raised) so we use this to filter out # such ghost monitors by only attempting to get the brightness for valid monitors # (determined by VCP.get_display_info) # yes, it does add an unnecessary function call but that's only if you're using this module low-level. # Top-level functions always end up specifying the display kwarg anyway if type(display) == int: indexes = [display] else: indexes = [i['index'] for i in filter_monitors(display=display, haystack=cls.get_display_info())] count = 0 values = [] for m in cls.iter_physical_monitors(): try: v = __cache__.get(f'vcp_brightness_{count}') except Exception: cur_out = DWORD() for _ in range(10): if windll.dxva2.GetVCPFeatureAndVCPFeatureReply(HANDLE(m), BYTE(0x10), None, byref(cur_out), None): v = cur_out.value break else: time.sleep(0.02) v = None del(cur_out) if v is not None: if count in indexes: try: __cache__.store(f'vcp_brightness_{count}', v, expires=0.1) except IndexError: pass values.append(v) if count >= max(indexes): break count += 1 return values
Retrieve the brightness of all connected displays using the ctypes.windll
API
Args
- display (int or str): The specific display you wish to query.
Can be index, name, model, serial or edid string.
int
is faster as it isn't passed tofilter_monitors
to be matched against.str
is slower as it is passed tofilter_monitors
to match to a display.
Returns
- list: list of ints (0 to 100)
Examples
import screen_brightness_control as sbc # Get the brightness for all detected displays current_brightness = sbc.windows.VCP.get_brightness() print('There are', len(current_brightness), 'detected displays') # Get the brightness for the primary display primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] # Get the brightness for a secondary display secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] # Get the brightness for a display with the model 'GL2450H' benq_brightness = sbc.windows.VCP.get_brightness(display = 'GL2450H')[0]
View Source
@classmethod def set_brightness( cls, value: int, display: Optional[Union[int, str]] = None, no_return: bool = False ) -> Union[List[int], None]: ''' Sets the brightness for all connected displays using the `ctypes.windll` API Args: display (int or str): The specific display you wish to query. Can be index, name, model, serial or edid string. `int` is faster as it isn't passed to `filter_monitors` to be matched against. `str` is slower as it is passed to `filter_monitors` to match to a display. no_return (bool): if set to `True` this function will return `None` Returns: list: list of ints (0 to 100) (the result of `VCP.get_brightness`) if `no_return` is False None: if `no_return` is True Examples: ```python import screen_brightness_control as sbc # Set the brightness for all detected displays to 50% sbc.windows.VCP.set_brightness(50) # Set the brightness for the primary display to 75% sbc.windows.VCP.set_brightness(75, display = 0) # Set the brightness for a secondary display to 25% sbc.windows.VCP.set_brightness(25, display = 1) # Set the brightness for a display with the model 'GL2450H' to 100% sbc.windows.VCP.set_brightness(100, display = 'GL2450H') ``` ''' if type(display) == int: indexes = [display] else: # see VCP.set_brightness for the explanation for why we always gather this list indexes = [i['index'] for i in filter_monitors(display=display, haystack=cls.get_display_info())] __cache__.expire(startswith='vcp_brightness_') count = 0 for m in cls.iter_physical_monitors(): if display is None or (count in indexes): for _ in range(10): if windll.dxva2.SetVCPFeature(HANDLE(m), BYTE(0x10), DWORD(value)): break else: time.sleep(0.02) count += 1 return cls.get_brightness(display=display) if not no_return else None
Sets the brightness for all connected displays using the ctypes.windll
API
Args
- display (int or str): The specific display you wish to query.
Can be index, name, model, serial or edid string.
int
is faster as it isn't passed tofilter_monitors
to be matched against.str
is slower as it is passed tofilter_monitors
to match to a display. - no_return (bool): if set to
True
this function will returnNone
Returns
- list: list of ints (0 to 100) (the result of
VCP.get_brightness
) ifno_return
is False - None: if
no_return
is True
Examples
import screen_brightness_control as sbc # Set the brightness for all detected displays to 50% sbc.windows.VCP.set_brightness(50) # Set the brightness for the primary display to 75% sbc.windows.VCP.set_brightness(75, display = 0) # Set the brightness for a secondary display to 25% sbc.windows.VCP.set_brightness(25, display = 1) # Set the brightness for a display with the model 'GL2450H' to 100% sbc.windows.VCP.set_brightness(100, display = 'GL2450H')
View Source
def list_monitors_info(method: Optional[str] = None, allow_duplicates: bool = False) -> List[dict]: ''' Lists detailed information about all detected monitors Args: method (str): the method the monitor can be addressed by. Can be 'wmi' or 'vcp' allow_duplicates (bool): whether to filter out duplicate displays (displays with the same EDID) or not Returns: list: list of dicts upon success, empty list upon failure Raises: ValueError: if the method kwarg is invalid Example: ```python import screen_brightness_control as sbc monitors = sbc.windows.list_monitors_info() for info in monitors: print('=======================') # the manufacturer name plus the model print('Name:', info['name']) # the general model of the display print('Model:', info['model']) # a unique string assigned by Windows to this display print('Serial:', info['serial']) # the name of the brand of the monitor print('Manufacturer:', info['manufacturer']) # the 3 letter code corresponding to the brand name, EG: BNQ -> BenQ print('Manufacturer ID:', info['manufacturer_id']) # the index of that display FOR THE SPECIFIC METHOD THE DISPLAY USES print('Index:', info['index']) # the method this monitor can be addressed by print('Method:', info['method']) # the EDID string of the monitor print('EDID:', info['edid']) ``` ''' try: return __cache__.get(f'windows_monitors_info_{method}_{allow_duplicates}') except Exception: info = get_display_info() if method is not None: method = method.lower() if method not in ('wmi', 'vcp'): raise ValueError('method kwarg must be \'wmi\' or \'vcp\'') info_final = [] serials = [] # to make sure each display (with unique edid) is only reported once for i in info: if allow_duplicates or i['serial'] not in serials: if method is None or method == i['method'].__name__.lower(): serials.append(i['serial']) info_final.append(i) __cache__.store(f'windows_monitors_info_{method}_{allow_duplicates}', info_final) return info_final
Lists detailed information about all detected monitors
Args
- method (str): the method the monitor can be addressed by. Can be 'wmi' or 'vcp'
- allow_duplicates (bool): whether to filter out duplicate displays (displays with the same EDID) or not
Returns
- list: list of dicts upon success, empty list upon failure
Raises
- ValueError: if the method kwarg is invalid
Example
import screen_brightness_control as sbc monitors = sbc.windows.list_monitors_info() for info in monitors: print('=======================') # the manufacturer name plus the model print('Name:', info['name']) # the general model of the display print('Model:', info['model']) # a unique string assigned by Windows to this display print('Serial:', info['serial']) # the name of the brand of the monitor print('Manufacturer:', info['manufacturer']) # the 3 letter code corresponding to the brand name, EG: BNQ -> BenQ print('Manufacturer ID:', info['manufacturer_id']) # the index of that display FOR THE SPECIFIC METHOD THE DISPLAY USES print('Index:', info['index']) # the method this monitor can be addressed by print('Method:', info['method']) # the EDID string of the monitor print('EDID:', info['edid'])
View Source
def list_monitors(method: Optional[str] = None) -> List[str]: ''' Returns a list of all addressable monitor names Args: method (str): the method the monitor can be addressed by. Can be 'wmi' or 'vcp' Returns: list: list of strings Example: ```python import screen_brightness_control as sbc monitors = sbc.windows.list_monitors() # EG output: ['BenQ GL2450H', 'Dell U2211H'] ``` ''' return [i['name'] for i in list_monitors_info(method=method)]
Returns a list of all addressable monitor names
Args
- method (str): the method the monitor can be addressed by. Can be 'wmi' or 'vcp'
Returns
- list: list of strings
Example
import screen_brightness_control as sbc monitors = sbc.windows.list_monitors() # EG output: ['BenQ GL2450H', 'Dell U2211H']
View Source
def set_brightness( value: int, display: Optional[Union[int, str]] = None, method: Optional[str] = None, **kwargs ) -> Union[List[int], None]: ''' Sets the brightness of any connected monitors Args: value (int): Sets the brightness to this value as a percentage display (int or str): The specific display you wish to adjust. Can be index, model, name or serial of the display method (str): the method to use ('wmi' or 'vcp') kwargs (dict): passed directly to the chosen brightness method Returns: list: list of ints (0 to 100) (the result of `get_brightness`) None: if `no_return` is True Raises: LookupError: if the chosen display (with method if applicable) is not found ValueError: if the chosen method is invalid TypeError: if the value given for `display` is not int or str Exception: if the brightness could not be set by any method Example: ```python import screen_brightness_control as sbc # set the current brightness to 50% sbc.windows.set_brightness(50) # set the brightness of the primary display to 75% sbc.windows.set_brightness(75, display = 0) # set the brightness of any displays using VCP to 25% sbc.windows.set_brightness(25, method = 'vcp') # set the brightness of displays with model name 'BenQ GL2450H' to 100% sbc.windows.set_brightness(100, display = 'BenQ GL2450H') ``` ''' # this function is called because set_brightness and get_brightness only differed by 1 line of code # so I made another internal function to reduce the filesize return __set_and_get_brightness(value, display=display, method=method, meta_method='set', **kwargs)
Sets the brightness of any connected monitors
Args
- value (int): Sets the brightness to this value as a percentage
- display (int or str): The specific display you wish to adjust. Can be index, model, name or serial of the display
- method (str): the method to use ('wmi' or 'vcp')
- kwargs (dict): passed directly to the chosen brightness method
Returns
- list: list of ints (0 to 100) (the result of
get_brightness
) - None: if
no_return
is True
Raises
- LookupError: if the chosen display (with method if applicable) is not found
- ValueError: if the chosen method is invalid
- TypeError: if the value given for
display
is not int or str - Exception: if the brightness could not be set by any method
Example
import screen_brightness_control as sbc # set the current brightness to 50% sbc.windows.set_brightness(50) # set the brightness of the primary display to 75% sbc.windows.set_brightness(75, display = 0) # set the brightness of any displays using VCP to 25% sbc.windows.set_brightness(25, method = 'vcp') # set the brightness of displays with model name 'BenQ GL2450H' to 100% sbc.windows.set_brightness(100, display = 'BenQ GL2450H')
View Source
def get_brightness(display: Optional[Union[int, str]] = None, method: Optional[str] = None, **kwargs) -> List[int]: ''' Returns the brightness of any connected monitors Args: display (int or str): The specific display you wish to adjust. Can be index, model, name or serial of the display method (str): the method to use ('wmi' or 'vcp') kwargs (dict): passed directly to chosen brightness method Returns: list: list of ints (0 to 100) Raises: LookupError: if the chosen display (with method if applicable) is not found ValueError: if the chosen method is invalid TypeError: if the value given for `display` is not int or str Exception: if the brightness could not be obtained by any method Example: ```python import screen_brightness_control as sbc # get the current brightness current_brightness = sbc.windows.get_brightness() if type(current_brightness) is int: print('There is only one detected display') else: print('There are', len(current_brightness), 'detected displays') # get the brightness of the primary display primary_brightness = sbc.windows.get_brightness(display=0)[0] # get the brightness of any displays using VCP vcp_brightness = sbc.windows.get_brightness(method='vcp') # get the brightness of displays with the model name 'BenQ GL2450H' benq_brightness = sbc.windows.get_brightness(display='BenQ GL2450H')[0] ``` ''' # this function is called because set_brightness and get_brightness only differed by 1 line of code # so I made another internal function to reduce the filesize return __set_and_get_brightness(display=display, method=method, meta_method='get', **kwargs)
Returns the brightness of any connected monitors
Args
- display (int or str): The specific display you wish to adjust. Can be index, model, name or serial of the display
- method (str): the method to use ('wmi' or 'vcp')
- kwargs (dict): passed directly to chosen brightness method
Returns
- list: list of ints (0 to 100)
Raises
- LookupError: if the chosen display (with method if applicable) is not found
- ValueError: if the chosen method is invalid
- TypeError: if the value given for
display
is not int or str - Exception: if the brightness could not be obtained by any method
Example
import screen_brightness_control as sbc # get the current brightness current_brightness = sbc.windows.get_brightness() if type(current_brightness) is int: print('There is only one detected display') else: print('There are', len(current_brightness), 'detected displays') # get the brightness of the primary display primary_brightness = sbc.windows.get_brightness(display=0)[0] # get the brightness of any displays using VCP vcp_brightness = sbc.windows.get_brightness(method='vcp') # get the brightness of displays with the model name 'BenQ GL2450H' benq_brightness = sbc.windows.get_brightness(display='BenQ GL2450H')[0]