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 []

def enum_display_devices() -> collections.abc.Generator[win32api.PyDISPLAY_DEVICEType, None, None]:
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
def get_display_info() -> List[dict]:
 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.

@classmethod
def get_display_info(cls, display: Union[str, int, NoneType] = None) -> List[dict]:
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')
@classmethod
def set_brightness(cls, value: int, display: Optional[int] = None):
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)
@classmethod
def get_brightness(cls, display: Optional[int] = None) -> List[int]:
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

@classmethod
def iter_physical_monitors( cls, start: int = 0) -> collections.abc.Generator[wintypes.HANDLE, None, None]:
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
@classmethod
def get_display_info(cls, display: Union[str, int, NoneType] = None) -> List[dict]:
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', ... }
@classmethod
def get_brightness(cls, display: Optional[int] = None, max_tries: int = 50) -> List[int]:
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]
@classmethod
def set_brightness(cls, value: int, display: Optional[int] = None, max_tries: int = 50):
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)
def list_monitors_info( method: Optional[str] = None, allow_duplicates: bool = False, unsupported: bool = False) -> List[dict]:
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'])