Sending DDC/CI commands to monitor on Windows using Python?

后端 未结 1 1849
隐瞒了意图╮
隐瞒了意图╮ 2020-12-24 15:40

I would like to have my monitors controlled from Windows (simple stuff such as changing the input source), but cannot find a way of sending DDC/CI commands from Python...

相关标签:
1条回答
  • 2020-12-24 16:10

    This is easily possible using the windows monitor API. I don't think there are any Python bindings out there and pywin32 doesn't contain those functions. However, using ctypes to call them is not that hard.

    Here's an example that switches the monitor to soft-off and then back on; it should be pretty easy to adapt it to changing the input source etc. The only complicated part is getting the handles for the physical monitors after all:

    from ctypes import windll, byref, Structure, WinError, POINTER, WINFUNCTYPE
    from ctypes.wintypes import BOOL, HMONITOR, HDC, RECT, LPARAM, DWORD, BYTE, WCHAR, HANDLE
    
    
    _MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM)
    
    
    class _PHYSICAL_MONITOR(Structure):
        _fields_ = [('handle', HANDLE),
                    ('description', WCHAR * 128)]
    
    
    def _iter_physical_monitors(close_handles=True):
        """Iterates physical monitors.
    
        The handles are closed automatically whenever the iterator is advanced.
        This means that the iterator should always be fully exhausted!
    
        If you want to keep handles e.g. because you need to store all of them and
        use them later, set `close_handles` to False and close them manually."""
    
        def callback(hmonitor, hdc, lprect, lparam):
            monitors.append(HMONITOR(hmonitor))
            return True
    
        monitors = []
        if not windll.user32.EnumDisplayMonitors(None, None, _MONITORENUMPROC(callback), None):
            raise WinError('EnumDisplayMonitors failed')
    
        for monitor in monitors:
            # Get physical monitor count
            count = DWORD()
            if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)):
                raise WinError()
            # Get physical monitor handles
            physical_array = (_PHYSICAL_MONITOR * count.value)()
            if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array):
                raise WinError()
            for physical in physical_array:
                yield physical.handle
                if close_handles:
                    if not windll.dxva2.DestroyPhysicalMonitor(physical.handle):
                        raise WinError()
    
    
    def set_vcp_feature(monitor, code, value):
        """Sends a DDC command to the specified monitor.
    
        See this link for a list of commands:
        ftp://ftp.cis.nctu.edu.tw/pub/csie/Software/X11/private/VeSaSpEcS/VESA_Document_Center_Monitor_Interface/mccsV3.pdf
        """
        if not windll.dxva2.SetVCPFeature(HANDLE(monitor), BYTE(code), DWORD(value)):
            raise WinError()
    
    
    # Switch to SOFT-OFF, wait for the user to press return and then back to ON
    for handle in _iter_physical_monitors():
        set_vcp_feature(handle, 0xd6, 0x04)
        raw_input()
        set_vcp_feature(handle, 0xd6, 0x01)
    
    0 讨论(0)
提交回复
热议问题