screen_brightness_control.windows
1import ctypes 2import logging 3import platform 4import threading 5import time 6from ctypes import POINTER, WINFUNCTYPE, Structure, WinError, byref, windll 7from ctypes.wintypes import (BOOL, BYTE, DWORD, HANDLE, HDC, HMONITOR, LPARAM, 8 RECT, WCHAR) 9from typing import List, Optional, Union 10 11import pythoncom 12import pywintypes 13import win32api 14import win32con 15import wmi 16 17from . import filter_monitors, get_methods 18from .exceptions import EDIDParseError, NoValidDisplayError, format_exc 19from .helpers import EDID, BrightnessMethod, __Cache, _monitor_brand_lookup 20 21# a bunch of typing classes were deprecated in Python 3.9 22# in favour of collections.abc (https://www.python.org/dev/peps/pep-0585/) 23if int(platform.python_version_tuple()[1]) < 9: 24 from typing import Generator 25else: 26 from collections.abc import Generator 27 28__cache__ = __Cache() 29logger = logging.getLogger(__name__) 30 31 32def _wmi_init(): 33 '''internal function to create and return a wmi instance''' 34 # WMI calls don't work in new threads so we have to run this check 35 if threading.current_thread() != threading.main_thread(): 36 pythoncom.CoInitialize() 37 return wmi.WMI(namespace='wmi') 38 39 40def enum_display_devices() -> Generator[win32api.PyDISPLAY_DEVICEType, None, None]: 41 ''' 42 Yields all display devices connected to the computer 43 44 Yields: 45 win32api.PyDISPLAY_DEVICEType 46 ''' 47 for monitor_enum in win32api.EnumDisplayMonitors(): 48 pyhandle = monitor_enum[0] 49 monitor_info = win32api.GetMonitorInfo(pyhandle) 50 for adaptor_index in range(5): 51 try: 52 device = win32api.EnumDisplayDevices(monitor_info['Device'], adaptor_index, 1) 53 except pywintypes.error: 54 logger.debug(f'failed to get display device {monitor_info["Device"]} on adaptor index {adaptor_index}') 55 else: 56 yield device 57 break 58 59 60def get_display_info() -> List[dict]: 61 ''' 62 Gets information about all connected displays using WMI and win32api 63 64 Returns: 65 list: list of dictionaries 66 67 Example: 68 ```python 69 import screen_brightness_control as s 70 71 info = s.windows.get_display_info() 72 for display in info: 73 print(display['name']) 74 ``` 75 ''' 76 info = __cache__.get('windows_monitors_info_raw') 77 if info is None: 78 info = [] 79 # collect all monitor UIDs (derived from DeviceID) 80 monitor_uids = {} 81 for device in enum_display_devices(): 82 monitor_uids[device.DeviceID.split('#')[2]] = device 83 84 # gather list of laptop displays to check against later 85 wmi = _wmi_init() 86 try: 87 laptop_displays = [ 88 i.InstanceName 89 for i in wmi.WmiMonitorBrightness() 90 ] 91 except Exception as e: 92 # don't do specific exception classes here because WMI does not play ball with it 93 logger.warning(f'get_display_info: failed to gather list of laptop displays - {format_exc(e)}') 94 laptop_displays = [] 95 96 extras, desktop, laptop = [], 0, 0 97 uid_keys = list(monitor_uids.keys()) 98 for monitor in wmi.WmiMonitorDescriptorMethods(): 99 model, serial, manufacturer, man_id, edid = None, None, None, None, None 100 instance_name = monitor.InstanceName.replace('_0', '', 1).split('\\')[2] 101 pydevice = monitor_uids[instance_name] 102 103 # get the EDID 104 try: 105 edid = ''.join(f'{char:02x}' for char in monitor.WmiGetMonitorRawEEdidV1Block(0)[0]) 106 # we do the EDID parsing ourselves because calling wmi.WmiMonitorID 107 # takes too long 108 parsed = EDID.parse(edid) 109 man_id, manufacturer, model, name, serial = parsed 110 if name is None: 111 raise EDIDParseError('parsed EDID returned invalid display name') 112 except EDIDParseError as e: 113 edid = None 114 logger.warning( 115 f'exception parsing edid str for {monitor.InstanceName} - {format_exc(e)}') 116 except Exception as e: 117 edid = None 118 logger.error(f'failed to get EDID string for {monitor.InstanceName} - {format_exc(e)}') 119 finally: 120 if edid is None: 121 devid = pydevice.DeviceID.split('#') 122 serial = devid[2] 123 man_id = devid[1][:3] 124 model = devid[1][3:] or 'Generic Monitor' 125 del devid 126 try: 127 man_id, manufacturer = _monitor_brand_lookup(man_id) 128 except TypeError: 129 manufacturer = None 130 131 if (serial, model) != (None, None): 132 data = { 133 'name': f'{manufacturer} {model}', 134 'model': model, 135 'serial': serial, 136 'manufacturer': manufacturer, 137 'manufacturer_id': man_id, 138 'edid': edid 139 } 140 if monitor.InstanceName in laptop_displays: 141 data['index'] = laptop 142 data['method'] = WMI 143 laptop += 1 144 else: 145 data['method'] = VCP 146 desktop += 1 147 148 if instance_name in uid_keys: 149 # insert the data into the uid_keys list because 150 # uid_keys has the monitors sorted correctly. This 151 # means we don't have to re-sort the list later 152 uid_keys[uid_keys.index(instance_name)] = data 153 else: 154 extras.append(data) 155 156 info = uid_keys + extras 157 if desktop: 158 # now make sure desktop monitors have the correct index 159 count = 0 160 for item in info: 161 if item['method'] == VCP: 162 item['index'] = count 163 count += 1 164 165 # return info only which has correct data 166 info = [i for i in info if isinstance(i, dict)] 167 168 __cache__.store('windows_monitors_info_raw', info) 169 170 return info 171 172 173class WMI(BrightnessMethod): 174 ''' 175 A collection of screen brightness related methods using the WMI API. 176 This class primarily works with laptop displays. 177 ''' 178 @classmethod 179 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 180 ''' 181 Returns a list of dictionaries of info about all detected displays 182 183 Args: 184 display (str or int): The display to return info about. 185 Pass in the serial number, name, model, edid or index 186 187 Returns: 188 list: list of dicts 189 190 Example: 191 ```python 192 import screen_brightness_control as sbc 193 194 info = sbc.windows.WMI.get_display_info() 195 for i in info: 196 print('================') 197 for key, value in i.items(): 198 print(key, ':', value) 199 200 # get information about the first WMI addressable display 201 primary_info = sbc.windows.WMI.get_display_info(0) 202 203 # get information about a display with a specific name 204 benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H') 205 ``` 206 ''' 207 info = [i for i in get_display_info() if i['method'] == cls] 208 if display is not None: 209 info = filter_monitors(display=display, haystack=info) 210 return info 211 212 @classmethod 213 def set_brightness(cls, value: int, display: Optional[int] = None): 214 ''' 215 Sets the display brightness for Windows using WMI 216 217 Args: 218 value (int): The percentage to set the brightness to 219 display (int): The specific display you wish to query. 220 221 Raises: 222 LookupError: if the given display cannot be found 223 224 Example: 225 ```python 226 import screen_brightness_control as sbc 227 228 # set brightness of WMI addressable displays to 50% 229 sbc.windows.WMI.set_brightness(50) 230 231 # set the primary display brightness to 75% 232 sbc.windows.WMI.set_brightness(75, display = 0) 233 234 # set the brightness of the secondary display to 25% 235 sbc.windows.WMI.set_brightness(25, display = 1) 236 ``` 237 ''' 238 brightness_method = _wmi_init().WmiMonitorBrightnessMethods() 239 if display is not None: 240 brightness_method = [brightness_method[display]] 241 242 for method in brightness_method: 243 method.WmiSetBrightness(value, 0) 244 245 @classmethod 246 def get_brightness(cls, display: Optional[int] = None) -> List[int]: 247 ''' 248 Returns the current display brightness using WMI 249 250 Args: 251 display (int): The specific display you wish to query. 252 253 Returns: 254 list: list of integers (0 to 100) 255 256 Raises: 257 LookupError: if the given display cannot be found 258 259 Example: 260 ```python 261 import screen_brightness_control as sbc 262 263 # get brightness of all WMI addressable displays 264 current_brightness = sbc.windows.WMI.get_brightness() 265 if type(current_brightness) is int: 266 print('There is only one detected display') 267 else: 268 print('There are', len(current_brightness), 'detected displays') 269 270 # get the primary display brightness 271 primary_brightness = sbc.windows.WMI.get_brightness(display = 0) 272 273 # get the brightness of the secondary display 274 benq_brightness = sbc.windows.WMI.get_brightness(display = 1) 275 ``` 276 ''' 277 brightness_method = _wmi_init().WmiMonitorBrightness() 278 if display is not None: 279 brightness_method = [brightness_method[display]] 280 281 values = [i.CurrentBrightness for i in brightness_method] 282 return values 283 284 285class VCP(BrightnessMethod): 286 '''Collection of screen brightness related methods using the DDC/CI commands''' 287 _MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM) 288 289 logger = logger.getChild('VCP') 290 291 class _PHYSICAL_MONITOR(Structure): 292 '''internal class, do not call''' 293 _fields_ = [('handle', HANDLE), 294 ('description', WCHAR * 128)] 295 296 @classmethod 297 def iter_physical_monitors(cls, start: int = 0) -> Generator[ctypes.wintypes.HANDLE, None, None]: 298 ''' 299 A generator to iterate through all physical monitors 300 and then close them again afterwards, yielding their handles. 301 It is not recommended to use this function unless you are familiar with `ctypes` and `windll` 302 303 Args: 304 start (int): skip the first X handles 305 306 Yields: 307 ctypes.wintypes.HANDLE 308 309 Raises: 310 ctypes.WinError: upon failure to enumerate through the monitors 311 ''' 312 def callback(hmonitor, *_): 313 monitors.append(HMONITOR(hmonitor)) 314 return True 315 316 monitors = [] 317 if not windll.user32.EnumDisplayMonitors(None, None, cls._MONITORENUMPROC(callback), None): 318 cls.logger.error('EnumDisplayMonitors failed') 319 raise WinError('EnumDisplayMonitors failed') 320 321 # user index keeps track of valid monitors 322 user_index = 0 323 # monitor index keeps track of valid and pseudo monitors 324 monitor_index = 0 325 display_devices = list(enum_display_devices()) 326 327 wmi = _wmi_init() 328 try: 329 laptop_displays = [ 330 i.InstanceName.replace('_0', '').split('\\')[2] 331 for i in wmi.WmiMonitorBrightness() 332 ] 333 except Exception as e: 334 cls.logger.warning(f'failed to gather list of laptop displays - {format_exc(e)}') 335 laptop_displays = [] 336 337 for monitor in monitors: 338 # Get physical monitor count 339 count = DWORD() 340 if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)): 341 raise WinError('call to GetNumberOfPhysicalMonitorsFromHMONITOR returned invalid result') 342 if count.value > 0: 343 # Get physical monitor handles 344 physical_array = (cls._PHYSICAL_MONITOR * count.value)() 345 if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array): 346 raise WinError('call to GetPhysicalMonitorsFromHMONITOR returned invalid result') 347 for item in physical_array: 348 # check that the monitor is not a pseudo monitor by 349 # checking it's StateFlags for the 350 # win32con DISPLAY_DEVICE_ATTACHED_TO_DESKTOP flag 351 if display_devices[monitor_index].StateFlags & win32con.DISPLAY_DEVICE_ATTACHED_TO_DESKTOP: 352 # check if monitor is actually a laptop display 353 if display_devices[monitor_index].DeviceID.split('#')[2] not in laptop_displays: 354 if start is None or user_index >= start: 355 yield item.handle 356 # increment user index as a valid monitor was found 357 user_index += 1 358 # increment monitor index 359 monitor_index += 1 360 windll.dxva2.DestroyPhysicalMonitor(item.handle) 361 362 @classmethod 363 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 364 ''' 365 Returns a dictionary of info about all detected displays 366 367 Args: 368 display (int or str): The display to return info about. 369 Pass in the serial number, name, model, edid or index 370 371 Returns: 372 list: list of dicts 373 374 Example: 375 ```python 376 import screen_brightness_control as sbc 377 378 # get the information about all displays 379 vcp_info = sbc.windows.VCP.get_display_info() 380 print(vcp_info) 381 # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] 382 383 # get information about a display with this specific model 384 bnq_info = sbc.windows.VCP.get_display_info('GL2450H') 385 # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... } 386 ``` 387 ''' 388 info = [i for i in get_display_info() if i['method'] == cls] 389 if display is not None: 390 info = filter_monitors(display=display, haystack=info) 391 return info 392 393 @classmethod 394 def get_brightness(cls, display: Optional[int] = None, max_tries: int = 50) -> List[int]: 395 ''' 396 Retrieve the brightness of all connected displays using the `ctypes.windll` API 397 398 Args: 399 display (int): The specific display you wish to query. 400 max_tries (int): the maximum allowed number of attempts to 401 read the VCP output from the display 402 403 Returns: 404 list: list of ints (0 to 100) 405 406 Examples: 407 ```python 408 import screen_brightness_control as sbc 409 410 # Get the brightness for all detected displays 411 current_brightness = sbc.windows.VCP.get_brightness() 412 print('There are', len(current_brightness), 'detected displays') 413 414 # Get the brightness for the primary display 415 primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] 416 417 # Get the brightness for a secondary display 418 secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] 419 ``` 420 ''' 421 code = BYTE(0x10) 422 values = [] 423 start = display if display is not None else 0 424 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 425 current = __cache__.get(f'vcp_brightness_{index}') 426 if current is None: 427 cur_out = DWORD() 428 handle = HANDLE(monitor) 429 for attempt in range(max_tries): 430 if windll.dxva2.GetVCPFeatureAndVCPFeatureReply(handle, code, None, byref(cur_out), None): 431 current = cur_out.value 432 break 433 current = None 434 time.sleep(0.02 if attempt < 20 else 0.1) 435 else: 436 cls.logger.error(f'failed to get VCP feature reply for display:{index} after {attempt} tries') 437 438 if current is not None: 439 __cache__.store(f'vcp_brightness_{index}', current, expires=0.1) 440 values.append(current) 441 442 if display == index: 443 # if we have just got the display we wanted then exit here 444 # no point iterating through all the other ones 445 break 446 447 return values 448 449 @classmethod 450 def set_brightness(cls, value: int, display: Optional[int] = None, max_tries: int = 50): 451 ''' 452 Sets the brightness for all connected displays using the `ctypes.windll` API 453 454 Args: 455 display (int): The specific display you wish to query. 456 max_tries (int): the maximum allowed number of attempts to 457 send the VCP input to the display 458 459 Examples: 460 ```python 461 import screen_brightness_control as sbc 462 463 # Set the brightness for all detected displays to 50% 464 sbc.windows.VCP.set_brightness(50) 465 466 # Set the brightness for the primary display to 75% 467 sbc.windows.VCP.set_brightness(75, display = 0) 468 469 # Set the brightness for a secondary display to 25% 470 sbc.windows.VCP.set_brightness(25, display = 1) 471 ``` 472 ''' 473 __cache__.expire(startswith='vcp_brightness_') 474 code = BYTE(0x10) 475 value = DWORD(value) 476 start = display if display is not None else 0 477 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 478 if display is None or display == index: 479 handle = HANDLE(monitor) 480 for attempt in range(max_tries): 481 if windll.dxva2.SetVCPFeature(handle, code, value): 482 break 483 time.sleep(0.02 if attempt < 20 else 0.1) 484 else: 485 cls.logger.error(f'failed to set display:{index}->{value} after {attempt} tries') 486 487 488def list_monitors_info( 489 method: Optional[str] = None, allow_duplicates: bool = False, unsupported: bool = False 490) -> List[dict]: 491 ''' 492 Lists detailed information about all detected displays 493 494 Args: 495 method (str): the method the display can be addressed by. See `screen_brightness_control.get_methods` 496 for more info on available methods 497 allow_duplicates (bool): whether to filter out duplicate displays (displays with the same EDID) or not 498 unsupported (bool): include detected displays that are invalid or unsupported. 499 This argument does nothing on Windows 500 501 Returns: 502 list: list of dicts upon success, empty list upon failure 503 504 Example: 505 ```python 506 import screen_brightness_control as sbc 507 508 displays = sbc.windows.list_monitors_info() 509 for info in displays: 510 print('=======================') 511 # the manufacturer name plus the model 512 print('Name:', info['name']) 513 # the general model of the display 514 print('Model:', info['model']) 515 # a unique string assigned by Windows to this display 516 print('Serial:', info['serial']) 517 # the name of the brand of the display 518 print('Manufacturer:', info['manufacturer']) 519 # the 3 letter code corresponding to the brand name, EG: BNQ -> BenQ 520 print('Manufacturer ID:', info['manufacturer_id']) 521 # the index of that display FOR THE SPECIFIC METHOD THE DISPLAY USES 522 print('Index:', info['index']) 523 # the method this display can be addressed by 524 print('Method:', info['method']) 525 # the EDID string of the display 526 print('EDID:', info['edid']) 527 ``` 528 ''' 529 # no caching here because get_display_info caches its results 530 info = get_display_info() 531 532 all_methods = get_methods(method).values() 533 534 if method is not None: 535 info = [i for i in info if i['method'] in all_methods] 536 537 if allow_duplicates: 538 return info 539 540 try: 541 # use filter_monitors to remove duplicates 542 return filter_monitors(haystack=info) 543 except NoValidDisplayError: 544 return []
41def enum_display_devices() -> Generator[win32api.PyDISPLAY_DEVICEType, None, None]: 42 ''' 43 Yields all display devices connected to the computer 44 45 Yields: 46 win32api.PyDISPLAY_DEVICEType 47 ''' 48 for monitor_enum in win32api.EnumDisplayMonitors(): 49 pyhandle = monitor_enum[0] 50 monitor_info = win32api.GetMonitorInfo(pyhandle) 51 for adaptor_index in range(5): 52 try: 53 device = win32api.EnumDisplayDevices(monitor_info['Device'], adaptor_index, 1) 54 except pywintypes.error: 55 logger.debug(f'failed to get display device {monitor_info["Device"]} on adaptor index {adaptor_index}') 56 else: 57 yield device 58 break
Yields all display devices connected to the computer
Yields:
- win32api.PyDISPLAY_DEVICEType
61def get_display_info() -> List[dict]: 62 ''' 63 Gets information about all connected displays using WMI and win32api 64 65 Returns: 66 list: list of dictionaries 67 68 Example: 69 ```python 70 import screen_brightness_control as s 71 72 info = s.windows.get_display_info() 73 for display in info: 74 print(display['name']) 75 ``` 76 ''' 77 info = __cache__.get('windows_monitors_info_raw') 78 if info is None: 79 info = [] 80 # collect all monitor UIDs (derived from DeviceID) 81 monitor_uids = {} 82 for device in enum_display_devices(): 83 monitor_uids[device.DeviceID.split('#')[2]] = device 84 85 # gather list of laptop displays to check against later 86 wmi = _wmi_init() 87 try: 88 laptop_displays = [ 89 i.InstanceName 90 for i in wmi.WmiMonitorBrightness() 91 ] 92 except Exception as e: 93 # don't do specific exception classes here because WMI does not play ball with it 94 logger.warning(f'get_display_info: failed to gather list of laptop displays - {format_exc(e)}') 95 laptop_displays = [] 96 97 extras, desktop, laptop = [], 0, 0 98 uid_keys = list(monitor_uids.keys()) 99 for monitor in wmi.WmiMonitorDescriptorMethods(): 100 model, serial, manufacturer, man_id, edid = None, None, None, None, None 101 instance_name = monitor.InstanceName.replace('_0', '', 1).split('\\')[2] 102 pydevice = monitor_uids[instance_name] 103 104 # get the EDID 105 try: 106 edid = ''.join(f'{char:02x}' for char in monitor.WmiGetMonitorRawEEdidV1Block(0)[0]) 107 # we do the EDID parsing ourselves because calling wmi.WmiMonitorID 108 # takes too long 109 parsed = EDID.parse(edid) 110 man_id, manufacturer, model, name, serial = parsed 111 if name is None: 112 raise EDIDParseError('parsed EDID returned invalid display name') 113 except EDIDParseError as e: 114 edid = None 115 logger.warning( 116 f'exception parsing edid str for {monitor.InstanceName} - {format_exc(e)}') 117 except Exception as e: 118 edid = None 119 logger.error(f'failed to get EDID string for {monitor.InstanceName} - {format_exc(e)}') 120 finally: 121 if edid is None: 122 devid = pydevice.DeviceID.split('#') 123 serial = devid[2] 124 man_id = devid[1][:3] 125 model = devid[1][3:] or 'Generic Monitor' 126 del devid 127 try: 128 man_id, manufacturer = _monitor_brand_lookup(man_id) 129 except TypeError: 130 manufacturer = None 131 132 if (serial, model) != (None, None): 133 data = { 134 'name': f'{manufacturer} {model}', 135 'model': model, 136 'serial': serial, 137 'manufacturer': manufacturer, 138 'manufacturer_id': man_id, 139 'edid': edid 140 } 141 if monitor.InstanceName in laptop_displays: 142 data['index'] = laptop 143 data['method'] = WMI 144 laptop += 1 145 else: 146 data['method'] = VCP 147 desktop += 1 148 149 if instance_name in uid_keys: 150 # insert the data into the uid_keys list because 151 # uid_keys has the monitors sorted correctly. This 152 # means we don't have to re-sort the list later 153 uid_keys[uid_keys.index(instance_name)] = data 154 else: 155 extras.append(data) 156 157 info = uid_keys + extras 158 if desktop: 159 # now make sure desktop monitors have the correct index 160 count = 0 161 for item in info: 162 if item['method'] == VCP: 163 item['index'] = count 164 count += 1 165 166 # return info only which has correct data 167 info = [i for i in info if isinstance(i, dict)] 168 169 __cache__.store('windows_monitors_info_raw', info) 170 171 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'])
174class WMI(BrightnessMethod): 175 ''' 176 A collection of screen brightness related methods using the WMI API. 177 This class primarily works with laptop displays. 178 ''' 179 @classmethod 180 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 181 ''' 182 Returns a list of dictionaries of info about all detected displays 183 184 Args: 185 display (str or int): The display to return info about. 186 Pass in the serial number, name, model, edid or index 187 188 Returns: 189 list: list of dicts 190 191 Example: 192 ```python 193 import screen_brightness_control as sbc 194 195 info = sbc.windows.WMI.get_display_info() 196 for i in info: 197 print('================') 198 for key, value in i.items(): 199 print(key, ':', value) 200 201 # get information about the first WMI addressable display 202 primary_info = sbc.windows.WMI.get_display_info(0) 203 204 # get information about a display with a specific name 205 benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H') 206 ``` 207 ''' 208 info = [i for i in get_display_info() if i['method'] == cls] 209 if display is not None: 210 info = filter_monitors(display=display, haystack=info) 211 return info 212 213 @classmethod 214 def set_brightness(cls, value: int, display: Optional[int] = None): 215 ''' 216 Sets the display brightness for Windows using WMI 217 218 Args: 219 value (int): The percentage to set the brightness to 220 display (int): The specific display you wish to query. 221 222 Raises: 223 LookupError: if the given display cannot be found 224 225 Example: 226 ```python 227 import screen_brightness_control as sbc 228 229 # set brightness of WMI addressable displays to 50% 230 sbc.windows.WMI.set_brightness(50) 231 232 # set the primary display brightness to 75% 233 sbc.windows.WMI.set_brightness(75, display = 0) 234 235 # set the brightness of the secondary display to 25% 236 sbc.windows.WMI.set_brightness(25, display = 1) 237 ``` 238 ''' 239 brightness_method = _wmi_init().WmiMonitorBrightnessMethods() 240 if display is not None: 241 brightness_method = [brightness_method[display]] 242 243 for method in brightness_method: 244 method.WmiSetBrightness(value, 0) 245 246 @classmethod 247 def get_brightness(cls, display: Optional[int] = None) -> List[int]: 248 ''' 249 Returns the current display brightness using WMI 250 251 Args: 252 display (int): The specific display you wish to query. 253 254 Returns: 255 list: list of integers (0 to 100) 256 257 Raises: 258 LookupError: if the given display cannot be found 259 260 Example: 261 ```python 262 import screen_brightness_control as sbc 263 264 # get brightness of all WMI addressable displays 265 current_brightness = sbc.windows.WMI.get_brightness() 266 if type(current_brightness) is int: 267 print('There is only one detected display') 268 else: 269 print('There are', len(current_brightness), 'detected displays') 270 271 # get the primary display brightness 272 primary_brightness = sbc.windows.WMI.get_brightness(display = 0) 273 274 # get the brightness of the secondary display 275 benq_brightness = sbc.windows.WMI.get_brightness(display = 1) 276 ``` 277 ''' 278 brightness_method = _wmi_init().WmiMonitorBrightness() 279 if display is not None: 280 brightness_method = [brightness_method[display]] 281 282 values = [i.CurrentBrightness for i in brightness_method] 283 return values
A collection of screen brightness related methods using the WMI API. This class primarily works with laptop displays.
179 @classmethod 180 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 181 ''' 182 Returns a list of dictionaries of info about all detected displays 183 184 Args: 185 display (str or int): The display to return info about. 186 Pass in the serial number, name, model, edid or index 187 188 Returns: 189 list: list of dicts 190 191 Example: 192 ```python 193 import screen_brightness_control as sbc 194 195 info = sbc.windows.WMI.get_display_info() 196 for i in info: 197 print('================') 198 for key, value in i.items(): 199 print(key, ':', value) 200 201 # get information about the first WMI addressable display 202 primary_info = sbc.windows.WMI.get_display_info(0) 203 204 # get information about a display with a specific name 205 benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H') 206 ``` 207 ''' 208 info = [i for i in get_display_info() if i['method'] == cls] 209 if display is not None: 210 info = filter_monitors(display=display, haystack=info) 211 return info
Returns a list of dictionaries of info about all detected displays
Arguments:
- display (str or int): The display 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 display primary_info = sbc.windows.WMI.get_display_info(0) # get information about a display with a specific name benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H')
213 @classmethod 214 def set_brightness(cls, value: int, display: Optional[int] = None): 215 ''' 216 Sets the display brightness for Windows using WMI 217 218 Args: 219 value (int): The percentage to set the brightness to 220 display (int): The specific display you wish to query. 221 222 Raises: 223 LookupError: if the given display cannot be found 224 225 Example: 226 ```python 227 import screen_brightness_control as sbc 228 229 # set brightness of WMI addressable displays to 50% 230 sbc.windows.WMI.set_brightness(50) 231 232 # set the primary display brightness to 75% 233 sbc.windows.WMI.set_brightness(75, display = 0) 234 235 # set the brightness of the secondary display to 25% 236 sbc.windows.WMI.set_brightness(25, display = 1) 237 ``` 238 ''' 239 brightness_method = _wmi_init().WmiMonitorBrightnessMethods() 240 if display is not None: 241 brightness_method = [brightness_method[display]] 242 243 for method in brightness_method: 244 method.WmiSetBrightness(value, 0)
Sets the display brightness for Windows using WMI
Arguments:
- value (int): The percentage to set the brightness to
- display (int): The specific display you wish to query.
Raises:
- LookupError: if the given display cannot be found
Example:
import screen_brightness_control as sbc # set brightness of WMI addressable displays 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 the secondary display to 25% sbc.windows.WMI.set_brightness(25, display = 1)
246 @classmethod 247 def get_brightness(cls, display: Optional[int] = None) -> List[int]: 248 ''' 249 Returns the current display brightness using WMI 250 251 Args: 252 display (int): The specific display you wish to query. 253 254 Returns: 255 list: list of integers (0 to 100) 256 257 Raises: 258 LookupError: if the given display cannot be found 259 260 Example: 261 ```python 262 import screen_brightness_control as sbc 263 264 # get brightness of all WMI addressable displays 265 current_brightness = sbc.windows.WMI.get_brightness() 266 if type(current_brightness) is int: 267 print('There is only one detected display') 268 else: 269 print('There are', len(current_brightness), 'detected displays') 270 271 # get the primary display brightness 272 primary_brightness = sbc.windows.WMI.get_brightness(display = 0) 273 274 # get the brightness of the secondary display 275 benq_brightness = sbc.windows.WMI.get_brightness(display = 1) 276 ``` 277 ''' 278 brightness_method = _wmi_init().WmiMonitorBrightness() 279 if display is not None: 280 brightness_method = [brightness_method[display]] 281 282 values = [i.CurrentBrightness for i in brightness_method] 283 return values
Returns the current display brightness using WMI
Arguments:
- display (int): The specific display you wish to query.
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 displays 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 the secondary display benq_brightness = sbc.windows.WMI.get_brightness(display = 1)
286class VCP(BrightnessMethod): 287 '''Collection of screen brightness related methods using the DDC/CI commands''' 288 _MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM) 289 290 logger = logger.getChild('VCP') 291 292 class _PHYSICAL_MONITOR(Structure): 293 '''internal class, do not call''' 294 _fields_ = [('handle', HANDLE), 295 ('description', WCHAR * 128)] 296 297 @classmethod 298 def iter_physical_monitors(cls, start: int = 0) -> Generator[ctypes.wintypes.HANDLE, None, None]: 299 ''' 300 A generator to iterate through all physical monitors 301 and then close them again afterwards, yielding their handles. 302 It is not recommended to use this function unless you are familiar with `ctypes` and `windll` 303 304 Args: 305 start (int): skip the first X handles 306 307 Yields: 308 ctypes.wintypes.HANDLE 309 310 Raises: 311 ctypes.WinError: upon failure to enumerate through the monitors 312 ''' 313 def callback(hmonitor, *_): 314 monitors.append(HMONITOR(hmonitor)) 315 return True 316 317 monitors = [] 318 if not windll.user32.EnumDisplayMonitors(None, None, cls._MONITORENUMPROC(callback), None): 319 cls.logger.error('EnumDisplayMonitors failed') 320 raise WinError('EnumDisplayMonitors failed') 321 322 # user index keeps track of valid monitors 323 user_index = 0 324 # monitor index keeps track of valid and pseudo monitors 325 monitor_index = 0 326 display_devices = list(enum_display_devices()) 327 328 wmi = _wmi_init() 329 try: 330 laptop_displays = [ 331 i.InstanceName.replace('_0', '').split('\\')[2] 332 for i in wmi.WmiMonitorBrightness() 333 ] 334 except Exception as e: 335 cls.logger.warning(f'failed to gather list of laptop displays - {format_exc(e)}') 336 laptop_displays = [] 337 338 for monitor in monitors: 339 # Get physical monitor count 340 count = DWORD() 341 if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)): 342 raise WinError('call to GetNumberOfPhysicalMonitorsFromHMONITOR returned invalid result') 343 if count.value > 0: 344 # Get physical monitor handles 345 physical_array = (cls._PHYSICAL_MONITOR * count.value)() 346 if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array): 347 raise WinError('call to GetPhysicalMonitorsFromHMONITOR returned invalid result') 348 for item in physical_array: 349 # check that the monitor is not a pseudo monitor by 350 # checking it's StateFlags for the 351 # win32con DISPLAY_DEVICE_ATTACHED_TO_DESKTOP flag 352 if display_devices[monitor_index].StateFlags & win32con.DISPLAY_DEVICE_ATTACHED_TO_DESKTOP: 353 # check if monitor is actually a laptop display 354 if display_devices[monitor_index].DeviceID.split('#')[2] not in laptop_displays: 355 if start is None or user_index >= start: 356 yield item.handle 357 # increment user index as a valid monitor was found 358 user_index += 1 359 # increment monitor index 360 monitor_index += 1 361 windll.dxva2.DestroyPhysicalMonitor(item.handle) 362 363 @classmethod 364 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 365 ''' 366 Returns a dictionary of info about all detected displays 367 368 Args: 369 display (int or str): The display to return info about. 370 Pass in the serial number, name, model, edid or index 371 372 Returns: 373 list: list of dicts 374 375 Example: 376 ```python 377 import screen_brightness_control as sbc 378 379 # get the information about all displays 380 vcp_info = sbc.windows.VCP.get_display_info() 381 print(vcp_info) 382 # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] 383 384 # get information about a display with this specific model 385 bnq_info = sbc.windows.VCP.get_display_info('GL2450H') 386 # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... } 387 ``` 388 ''' 389 info = [i for i in get_display_info() if i['method'] == cls] 390 if display is not None: 391 info = filter_monitors(display=display, haystack=info) 392 return info 393 394 @classmethod 395 def get_brightness(cls, display: Optional[int] = None, max_tries: int = 50) -> List[int]: 396 ''' 397 Retrieve the brightness of all connected displays using the `ctypes.windll` API 398 399 Args: 400 display (int): The specific display you wish to query. 401 max_tries (int): the maximum allowed number of attempts to 402 read the VCP output from the display 403 404 Returns: 405 list: list of ints (0 to 100) 406 407 Examples: 408 ```python 409 import screen_brightness_control as sbc 410 411 # Get the brightness for all detected displays 412 current_brightness = sbc.windows.VCP.get_brightness() 413 print('There are', len(current_brightness), 'detected displays') 414 415 # Get the brightness for the primary display 416 primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] 417 418 # Get the brightness for a secondary display 419 secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] 420 ``` 421 ''' 422 code = BYTE(0x10) 423 values = [] 424 start = display if display is not None else 0 425 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 426 current = __cache__.get(f'vcp_brightness_{index}') 427 if current is None: 428 cur_out = DWORD() 429 handle = HANDLE(monitor) 430 for attempt in range(max_tries): 431 if windll.dxva2.GetVCPFeatureAndVCPFeatureReply(handle, code, None, byref(cur_out), None): 432 current = cur_out.value 433 break 434 current = None 435 time.sleep(0.02 if attempt < 20 else 0.1) 436 else: 437 cls.logger.error(f'failed to get VCP feature reply for display:{index} after {attempt} tries') 438 439 if current is not None: 440 __cache__.store(f'vcp_brightness_{index}', current, expires=0.1) 441 values.append(current) 442 443 if display == index: 444 # if we have just got the display we wanted then exit here 445 # no point iterating through all the other ones 446 break 447 448 return values 449 450 @classmethod 451 def set_brightness(cls, value: int, display: Optional[int] = None, max_tries: int = 50): 452 ''' 453 Sets the brightness for all connected displays using the `ctypes.windll` API 454 455 Args: 456 display (int): The specific display you wish to query. 457 max_tries (int): the maximum allowed number of attempts to 458 send the VCP input to the display 459 460 Examples: 461 ```python 462 import screen_brightness_control as sbc 463 464 # Set the brightness for all detected displays to 50% 465 sbc.windows.VCP.set_brightness(50) 466 467 # Set the brightness for the primary display to 75% 468 sbc.windows.VCP.set_brightness(75, display = 0) 469 470 # Set the brightness for a secondary display to 25% 471 sbc.windows.VCP.set_brightness(25, display = 1) 472 ``` 473 ''' 474 __cache__.expire(startswith='vcp_brightness_') 475 code = BYTE(0x10) 476 value = DWORD(value) 477 start = display if display is not None else 0 478 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 479 if display is None or display == index: 480 handle = HANDLE(monitor) 481 for attempt in range(max_tries): 482 if windll.dxva2.SetVCPFeature(handle, code, value): 483 break 484 time.sleep(0.02 if attempt < 20 else 0.1) 485 else: 486 cls.logger.error(f'failed to set display:{index}->{value} after {attempt} tries')
Collection of screen brightness related methods using the DDC/CI commands
297 @classmethod 298 def iter_physical_monitors(cls, start: int = 0) -> Generator[ctypes.wintypes.HANDLE, None, None]: 299 ''' 300 A generator to iterate through all physical monitors 301 and then close them again afterwards, yielding their handles. 302 It is not recommended to use this function unless you are familiar with `ctypes` and `windll` 303 304 Args: 305 start (int): skip the first X handles 306 307 Yields: 308 ctypes.wintypes.HANDLE 309 310 Raises: 311 ctypes.WinError: upon failure to enumerate through the monitors 312 ''' 313 def callback(hmonitor, *_): 314 monitors.append(HMONITOR(hmonitor)) 315 return True 316 317 monitors = [] 318 if not windll.user32.EnumDisplayMonitors(None, None, cls._MONITORENUMPROC(callback), None): 319 cls.logger.error('EnumDisplayMonitors failed') 320 raise WinError('EnumDisplayMonitors failed') 321 322 # user index keeps track of valid monitors 323 user_index = 0 324 # monitor index keeps track of valid and pseudo monitors 325 monitor_index = 0 326 display_devices = list(enum_display_devices()) 327 328 wmi = _wmi_init() 329 try: 330 laptop_displays = [ 331 i.InstanceName.replace('_0', '').split('\\')[2] 332 for i in wmi.WmiMonitorBrightness() 333 ] 334 except Exception as e: 335 cls.logger.warning(f'failed to gather list of laptop displays - {format_exc(e)}') 336 laptop_displays = [] 337 338 for monitor in monitors: 339 # Get physical monitor count 340 count = DWORD() 341 if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)): 342 raise WinError('call to GetNumberOfPhysicalMonitorsFromHMONITOR returned invalid result') 343 if count.value > 0: 344 # Get physical monitor handles 345 physical_array = (cls._PHYSICAL_MONITOR * count.value)() 346 if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array): 347 raise WinError('call to GetPhysicalMonitorsFromHMONITOR returned invalid result') 348 for item in physical_array: 349 # check that the monitor is not a pseudo monitor by 350 # checking it's StateFlags for the 351 # win32con DISPLAY_DEVICE_ATTACHED_TO_DESKTOP flag 352 if display_devices[monitor_index].StateFlags & win32con.DISPLAY_DEVICE_ATTACHED_TO_DESKTOP: 353 # check if monitor is actually a laptop display 354 if display_devices[monitor_index].DeviceID.split('#')[2] not in laptop_displays: 355 if start is None or user_index >= start: 356 yield item.handle 357 # increment user index as a valid monitor was found 358 user_index += 1 359 # increment monitor index 360 monitor_index += 1 361 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
Arguments:
- start (int): skip the first X handles
Yields:
- ctypes.wintypes.HANDLE
Raises:
- ctypes.WinError: upon failure to enumerate through the monitors
363 @classmethod 364 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 365 ''' 366 Returns a dictionary of info about all detected displays 367 368 Args: 369 display (int or str): The display to return info about. 370 Pass in the serial number, name, model, edid or index 371 372 Returns: 373 list: list of dicts 374 375 Example: 376 ```python 377 import screen_brightness_control as sbc 378 379 # get the information about all displays 380 vcp_info = sbc.windows.VCP.get_display_info() 381 print(vcp_info) 382 # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] 383 384 # get information about a display with this specific model 385 bnq_info = sbc.windows.VCP.get_display_info('GL2450H') 386 # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... } 387 ``` 388 ''' 389 info = [i for i in get_display_info() if i['method'] == cls] 390 if display is not None: 391 info = filter_monitors(display=display, haystack=info) 392 return info
Returns a dictionary of info about all detected displays
Arguments:
- display (int or str): The display 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 displays vcp_info = sbc.windows.VCP.get_display_info() print(vcp_info) # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] # get information about a display with this specific model bnq_info = sbc.windows.VCP.get_display_info('GL2450H') # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... }
394 @classmethod 395 def get_brightness(cls, display: Optional[int] = None, max_tries: int = 50) -> List[int]: 396 ''' 397 Retrieve the brightness of all connected displays using the `ctypes.windll` API 398 399 Args: 400 display (int): The specific display you wish to query. 401 max_tries (int): the maximum allowed number of attempts to 402 read the VCP output from the display 403 404 Returns: 405 list: list of ints (0 to 100) 406 407 Examples: 408 ```python 409 import screen_brightness_control as sbc 410 411 # Get the brightness for all detected displays 412 current_brightness = sbc.windows.VCP.get_brightness() 413 print('There are', len(current_brightness), 'detected displays') 414 415 # Get the brightness for the primary display 416 primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] 417 418 # Get the brightness for a secondary display 419 secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] 420 ``` 421 ''' 422 code = BYTE(0x10) 423 values = [] 424 start = display if display is not None else 0 425 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 426 current = __cache__.get(f'vcp_brightness_{index}') 427 if current is None: 428 cur_out = DWORD() 429 handle = HANDLE(monitor) 430 for attempt in range(max_tries): 431 if windll.dxva2.GetVCPFeatureAndVCPFeatureReply(handle, code, None, byref(cur_out), None): 432 current = cur_out.value 433 break 434 current = None 435 time.sleep(0.02 if attempt < 20 else 0.1) 436 else: 437 cls.logger.error(f'failed to get VCP feature reply for display:{index} after {attempt} tries') 438 439 if current is not None: 440 __cache__.store(f'vcp_brightness_{index}', current, expires=0.1) 441 values.append(current) 442 443 if display == index: 444 # if we have just got the display we wanted then exit here 445 # no point iterating through all the other ones 446 break 447 448 return values
Retrieve the brightness of all connected displays using the ctypes.windll
API
Arguments:
- display (int): The specific display you wish to query.
- max_tries (int): the maximum allowed number of attempts to read the VCP output from the 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]
450 @classmethod 451 def set_brightness(cls, value: int, display: Optional[int] = None, max_tries: int = 50): 452 ''' 453 Sets the brightness for all connected displays using the `ctypes.windll` API 454 455 Args: 456 display (int): The specific display you wish to query. 457 max_tries (int): the maximum allowed number of attempts to 458 send the VCP input to the display 459 460 Examples: 461 ```python 462 import screen_brightness_control as sbc 463 464 # Set the brightness for all detected displays to 50% 465 sbc.windows.VCP.set_brightness(50) 466 467 # Set the brightness for the primary display to 75% 468 sbc.windows.VCP.set_brightness(75, display = 0) 469 470 # Set the brightness for a secondary display to 25% 471 sbc.windows.VCP.set_brightness(25, display = 1) 472 ``` 473 ''' 474 __cache__.expire(startswith='vcp_brightness_') 475 code = BYTE(0x10) 476 value = DWORD(value) 477 start = display if display is not None else 0 478 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 479 if display is None or display == index: 480 handle = HANDLE(monitor) 481 for attempt in range(max_tries): 482 if windll.dxva2.SetVCPFeature(handle, code, value): 483 break 484 time.sleep(0.02 if attempt < 20 else 0.1) 485 else: 486 cls.logger.error(f'failed to set display:{index}->{value} after {attempt} tries')
Sets the brightness for all connected displays using the ctypes.windll
API
Arguments:
- display (int): The specific display you wish to query.
- max_tries (int): the maximum allowed number of attempts to send the VCP input to the display
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)
489def list_monitors_info( 490 method: Optional[str] = None, allow_duplicates: bool = False, unsupported: bool = False 491) -> List[dict]: 492 ''' 493 Lists detailed information about all detected displays 494 495 Args: 496 method (str): the method the display can be addressed by. See `screen_brightness_control.get_methods` 497 for more info on available methods 498 allow_duplicates (bool): whether to filter out duplicate displays (displays with the same EDID) or not 499 unsupported (bool): include detected displays that are invalid or unsupported. 500 This argument does nothing on Windows 501 502 Returns: 503 list: list of dicts upon success, empty list upon failure 504 505 Example: 506 ```python 507 import screen_brightness_control as sbc 508 509 displays = sbc.windows.list_monitors_info() 510 for info in displays: 511 print('=======================') 512 # the manufacturer name plus the model 513 print('Name:', info['name']) 514 # the general model of the display 515 print('Model:', info['model']) 516 # a unique string assigned by Windows to this display 517 print('Serial:', info['serial']) 518 # the name of the brand of the display 519 print('Manufacturer:', info['manufacturer']) 520 # the 3 letter code corresponding to the brand name, EG: BNQ -> BenQ 521 print('Manufacturer ID:', info['manufacturer_id']) 522 # the index of that display FOR THE SPECIFIC METHOD THE DISPLAY USES 523 print('Index:', info['index']) 524 # the method this display can be addressed by 525 print('Method:', info['method']) 526 # the EDID string of the display 527 print('EDID:', info['edid']) 528 ``` 529 ''' 530 # no caching here because get_display_info caches its results 531 info = get_display_info() 532 533 all_methods = get_methods(method).values() 534 535 if method is not None: 536 info = [i for i in info if i['method'] in all_methods] 537 538 if allow_duplicates: 539 return info 540 541 try: 542 # use filter_monitors to remove duplicates 543 return filter_monitors(haystack=info) 544 except NoValidDisplayError: 545 return []
Lists detailed information about all detected displays
Arguments:
- method (str): the method the display can be addressed by. See
screen_brightness_control.get_methods
for more info on available methods - allow_duplicates (bool): whether to filter out duplicate displays (displays with the same EDID) or not
- unsupported (bool): include detected displays that are invalid or unsupported. This argument does nothing on Windows
Returns:
- list: list of dicts upon success, empty list upon failure
Example:
import screen_brightness_control as sbc displays = sbc.windows.list_monitors_info() for info in displays: 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 display 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 display can be addressed by print('Method:', info['method']) # the EDID string of the display print('EDID:', info['edid'])