| Line 33: |
Line 33: |
| | | | |
| | ===Template=== | | ===Template=== |
| − | <pre>
| |
| − | Some code
| |
| − | </pre>
| |
| | | | |
| | + | <syntaxhighlight lang="python"> |
| | + | import time |
| | + | from ctypes import windll, byref, c_char, 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: |
| | + | handle = physical.handle |
| | + | |
| | + | # Get physical monitor capabilities. This may take a while... |
| | + | length = DWORD() |
| | + | if not windll.dxva2.GetCapabilitiesStringLength(HANDLE(handle), byref(length)): |
| | + | raise WinError() |
| | + | capabilities_string = (c_char * length.value)() |
| | + | if not windll.dxva2.CapabilitiesRequestAndCapabilitiesReply(HANDLE(handle), capabilities_string, length): |
| | + | raise WinError() |
| | + | raw_capabilities = capabilities_string.value.decode('ascii') |
| | + | capabilities = _parse_capabilities_string(raw_capabilities) |
| | + | if capabilities: |
| | + | # We only care about the model info for now. |
| | + | yield [capabilities['model'], handle] |
| | + | if close_handles: |
| | + | if not windll.dxva2.DestroyPhysicalMonitor(handle): |
| | + | raise WinError() |
| | + | |
| | + | |
| | + | def _parse_capabilities_string(capabilities_string): |
| | + | level = 0 |
| | + | capabilities = {} |
| | + | open_p = {} |
| | + | close_p = {0: 0} |
| | + | id = {} |
| | + | for i, chr in enumerate(capabilities_string): |
| | + | if chr == '(': |
| | + | if i == 0: |
| | + | close_p[0] = 1 |
| | + | continue |
| | + | open_p[level] = i |
| | + | if level == 0: |
| | + | id[0] = capabilities_string[close_p[0] + 1:i] |
| | + | level += 1 |
| | + | elif chr == ')': |
| | + | level -= 1 |
| | + | close_p[level] = i |
| | + | if level == 0: |
| | + | values = capabilities_string[open_p[0] + 1:i] |
| | + | # We only care about the model info for now. |
| | + | if id[0] == 'model': |
| | + | capabilities[id[0]] = values |
| | + | return capabilities |
| | + | |
| | + | |
| | + | 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() |
| | + | |
| | + | |
| | + | for model, handle in _iter_physical_monitors(): |
| | + | if model == "XL2420Z": |
| | + | set_vcp_feature(handle, 0xDC, 12) # picture mode |
| | + | |
| | + | time.sleep(2) # wait for picture mode to load |
| | + | |
| | + | set_vcp_feature(handle, 0x10, 31) # brightness |
| | + | set_vcp_feature(handle, 0x12, 50) # contrast |
| | + | set_vcp_feature(handle, 0xF0, 0) # AMA (overdrive) |
| | + | |
| | + | </syntaxhighlight> |
| | | | |
| | ==See Also== <!-- Optional --> | | ==See Also== <!-- Optional --> |