3

I recently stumbled upon this software: https://clickmonitorddc.bplaced.net/ And I wanted to find a way to programmatically change my default monitor's input source (from DP to HDMI and back) (On my first of two monitors).

I found this Sending DDC/CI commands to monitor on Windows using Python? detailing how to send ddc-ci commands through python.

Now this is all well and good, but the linked pdf file to the list of DDC commands has expired and I can't seem to figure out how I'd apply this to my specific case. Messing around has only resulted in me succesfully making my monitors go blank one after the other, but that's not really what I'm trying to accomplish.

I sadly do not have much of any attempts or code to share a part from the one in the linked post above.

Any help would be greatly appreciated.

Tom
  • 571
  • 2
  • 11
  • 29

2 Answers2

9

After some testing with the code linked in my original, I managed to figure it out:

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 HDMI, wait for the user to press return and then back to DP
for handle in _iter_physical_monitors():
    set_vcp_feature(handle, 0x60, 0x11) #Change input to HDMI
    input()
    set_vcp_feature(handle, 0x60, 0x0F) #Change input to DisplayPort

Turns out the vcp code for input commands is 0x60, and from there the values can be determined quite easily, they are as followed:

0x01: D-sub/VGA, 0x03: DVI, 0x11 or 0x04 depending on the brand: HDMI, 0x0F: DisplayPort

Tom
  • 571
  • 2
  • 11
  • 29
-1

This page may have the information you are looking for. Seems to be similar to the no-longer-linked PDF referenced in the comments: https://milek7.pl/ddcbacklight/mccs.pdf

Jace
  • 19
  • Welcome to Stack Overflow. It is great that you make the effort on answering a question, and that you've found an external source that may be of author's help, but you should at least quote the important parts from the external source that may help the user resolve his problem, instead of linking to a TL;DR page. – Marc Sances Aug 20 '20 at 16:23
  • Answers should include at least a minimum example code. – Orsiris de Jong Aug 20 '20 at 16:45
  • This answer adds to the previous one and code is not needed so idk why it was flagged – Michał Darowny Nov 22 '21 at 11:01