Skip to content

Commit

Permalink
WIN32: Fixed setScale()
Browse files Browse the repository at this point in the history
  • Loading branch information
Kalmat committed Aug 23, 2023
1 parent e8e8423 commit 93009cf
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 40 deletions.
Binary file modified dist/PyMonCtl-0.0.10-py3-none-any.whl
Binary file not shown.
3 changes: 2 additions & 1 deletion src/ewmhlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
__all__ = [
"version", "displaysCount", "getDisplaysNames", "getDisplaysInfo", "getDisplayFromRoot", "getDisplayFromWindow",
"getProperty", "getPropertyValue", "changeProperty", "sendMessage",
"defaultDisplay", "defaultScreen", "defaultRoot", "defaultRootWindow", "RootWindow", "EwmhWindow"
"defaultDisplay", "defaultScreen", "defaultRoot", "defaultRootWindow", "RootWindow", "EwmhWindow",
"Props", "Structs"
]


Expand Down
8 changes: 5 additions & 3 deletions src/ewmhlib/_ewmhlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ def __init__(self, root: Optional[Union[XWindow, int]] = None):

if root and isinstance(root, XWindow):
root = root.id
self.display, self.screen, self.root = getDisplayFromRoot(cast(int, root))
self.id: int = self.root.id
self.display, self.screen, self.root = getDisplayFromRoot(cast(Optional[int], root))
self.id = self.root.id
self.wmProtocols = self._WmProtocols(self.display, self.root)

def getProperty(self, prop: Union[str, int], prop_type: int = Xlib.X.AnyPropertyType) -> Optional[Xlib.protocol.request.GetProperty]:
Expand Down Expand Up @@ -1007,10 +1007,12 @@ def __init__(self, window: Union[int, XWindow]):
self.id: int = window.id
self.display, self.screen, self.root = getDisplayFromWindow(self.id)
self.xWindow: XWindow = window
else:
elif isinstance(window, int):
self.id = window
self.display, self.screen, self.root = getDisplayFromWindow(self.id)
self.xWindow = self.display.create_resource_object('window', self.id)
else:
raise ValueError
self.rootWindow: RootWindow = defaultRootWindow if self.root.id == defaultRoot.id else RootWindow(self.root)
self.extensions = _Extensions(self.id, self.display, self.root)

Expand Down
96 changes: 64 additions & 32 deletions src/pymonctl/_pymonctl_win.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@

from pymonctl import BaseMonitor, _getRelativePosition, \
DisplayMode, ScreenValue, Box, Rect, Point, Size, Position, Orientation
from pymonctl.structs import _QDC_ONLY_ACTIVE_PATHS, _DISPLAYCONFIG_PATH_ACTIVE, _DISPLAYCONFIG_PATH_INFO, \
_DISPLAYCONFIG_MODE_INFO, _DISPLAYCONFIG_SOURCE_DPI_SCALE_GET, \
_DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE
from pymonctl.structs import (_QDC_ONLY_ACTIVE_PATHS, _DISPLAYCONFIG_PATH_INFO, _DISPLAYCONFIG_MODE_INFO, _LUID,
_DISPLAYCONFIG_SOURCE_DPI_SCALE_GET, _DISPLAYCONFIG_SOURCE_DPI_SCALE_SET, _DPI_VALUES,
_DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE, _DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE,
_DISPLAYCONFIG_SOURCE_DEVICE_NAME, _DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME
)


dpiAware = ctypes.windll.user32.GetAwarenessFromDpiAwarenessContext(ctypes.windll.user32.GetThreadDpiAwarenessContext())
Expand Down Expand Up @@ -167,6 +169,8 @@ def __init__(self, handle: Optional[int] = None):
raise ValueError
self._hasVCPSupport = _win32hasVCPSupport(self.handle)
self._hasVCPPowerSupport = _win32hasVCPPowerSupport(self.handle)
self._sourceAdapterId: Optional[_LUID] = None
self._sourceId: Optional[int] = None

@property
def size(self) -> Optional[Size]:
Expand Down Expand Up @@ -229,59 +233,87 @@ def scale(self) -> Optional[Tuple[float, float]]:
return float(pScale.value), float(pScale.value)
return None

def _getPaths(self):
def _getPaths(self) -> Tuple[Optional[_LUID], Optional[int]]:

flags = _QDC_ONLY_ACTIVE_PATHS
numPathArrayElements = ctypes.c_uint32()
numModeInfoArrayElements = ctypes.c_uint32()
ctypes.windll.user32.GetDisplayConfigBufferSizes(flags,
ctypes.byref(numPathArrayElements),
ctypes.byref(numModeInfoArrayElements))
print("PATHS", numPathArrayElements.value, "MODES", numModeInfoArrayElements.value)

flags = _DISPLAYCONFIG_PATH_ACTIVE
flags = _QDC_ONLY_ACTIVE_PATHS
paths = (_DISPLAYCONFIG_PATH_INFO * numPathArrayElements.value)()
print(ctypes.sizeof(pymonctl.structs._DISPLAYCONFIG_PATH_INFO()), ctypes.sizeof(paths))
modes = (_DISPLAYCONFIG_MODE_INFO * numModeInfoArrayElements.value)()
print(ctypes.sizeof(_DISPLAYCONFIG_MODE_INFO()), ctypes.sizeof(modes))
nullptr = ctypes.c_void_p() # or None?
nullptr = ctypes.c_void_p()
ret = ctypes.windll.user32.QueryDisplayConfig(flags,
ctypes.byref(numPathArrayElements),
ctypes.byref(paths),
ctypes.byref(numModeInfoArrayElements),
ctypes.byref(modes),
nullptr
)
print("RET", ret, "PATHS", numPathArrayElements.value, "MODES", numModeInfoArrayElements.value)
if ret == 0:
for i in range(numPathArrayElements.value):
pathInfo: _DISPLAYCONFIG_PATH_INFO = paths[i].value
print(pathInfo)
for path in paths:
sourceName = _DISPLAYCONFIG_SOURCE_DEVICE_NAME()
sourceName.header.type = _DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME
sourceName.header.size = ctypes.sizeof(sourceName)
sourceName.header.adapterId = path.sourceInfo.adapterId
sourceName.header.id = path.sourceInfo.id
ret = ctypes.windll.user32.DisplayConfigGetDeviceInfo(ctypes.byref(sourceName))
if ret == 0 and sourceName.viewGdiDeviceName == self.name:
return path.sourceInfo.adapterId, path.sourceInfo.id
else:
print("FAILED!!! (I guess you are gonna see this a ridiculously huge number of times...)")

def setScale(self, scale: Tuple[float, float]):
if scale is not None and self.handle is not None:
# https://github.com/lihas/windows-DPI-scaling-sample/blob/master/DPIHelper/DpiHelper.cpp
return None, None

# self._getPaths()
def setScale(self, scale: Optional[Tuple[float, float]]):

scaleData = _DISPLAYCONFIG_SOURCE_DPI_SCALE_GET()
scaleData.header.type = _DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE
scaleData.header.size = ctypes.sizeof(scaleData)
if self.handle is not None and scale is not None:
# https://github.com/lihas/windows-DPI-scaling-sample/blob/master/DPIHelper/DpiHelper.cpp
# https://github.com/lihas/windows-DPI-scaling-sample/blob/master/DPIHelper/DpiHelper.cpp
# HOW to GET adapterId and sourceId values???? -> QueryDisplayConfig
# https://stackoverflow.com/questions/67332814/how-to-properly-clone-and-extend-two-specific-monitors-on-windows
# Seriously all that to just get the adapterId and the sourceId???? I definitely love you, MS©
scaleData.header.adapterId = pymonctl.structs._LUID()
scaleData.header.adapterId.lowPart = 0
scaleData.header.adapterId.highPart = self.handle
scaleData.header.id = 0
scaleData.minScaleRel = 100
scaleData.curScaleRel = 125
scaleData.maxScaleRel = 250
ret = ctypes.windll.user32.DisplayConfigGetDeviceInfo(ctypes.byref(scaleData))
# print("RET", ret, "MIN", scaleData.minScaleRel, "CURR", scaleData.curScaleRel, "MAX", scaleData.maxScaleRel)
# ctypes.windll.user32.DisplayConfigSetDeviceInfo(ctypes.byref(data))

if self._sourceAdapterId is None:
self._sourceAdapterId, self._sourceId = self._getPaths()
if self._sourceAdapterId is None:
self._sourceAdapterId = 0
self._sourceId = None

if self._sourceAdapterId is not None and self._sourceId is not None:

scaleData = _DISPLAYCONFIG_SOURCE_DPI_SCALE_GET()
scaleData.header.type = _DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE
scaleData.header.size = ctypes.sizeof(scaleData)
scaleData.header.adapterId = self._sourceAdapterId
scaleData.header.id = self._sourceId
ctypes.windll.user32.DisplayConfigGetDeviceInfo(ctypes.byref(scaleData))
minScale = _DPI_VALUES[scaleData.minScaleRel]
maxScale = _DPI_VALUES[scaleData.maxScaleRel]

scale = int(scale[0])
targetScale = -1
if scale < minScale:
targetScale = 0
elif scale > maxScale:
targetScale = len(_DPI_VALUES) - 1
else:
try:
targetScale = _DPI_VALUES.index(scale)
except:
for i, value in enumerate(_DPI_VALUES):
targetScale = i
if value > scale:
break
if targetScale >= 0:
setScaleData = _DISPLAYCONFIG_SOURCE_DPI_SCALE_SET()
setScaleData.header.type = _DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE
setScaleData.header.size = ctypes.sizeof(setScaleData)
setScaleData.header.adapterId = self._sourceAdapterId
setScaleData.header.id = self._sourceId
setScaleData.scaleRel = targetScale
ctypes.windll.user32.DisplayConfigSetDeviceInfo(ctypes.byref(setScaleData))

@property
def dpi(self) -> Optional[Tuple[float, float]]:
Expand Down
37 changes: 33 additions & 4 deletions src/pymonctl/structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,16 @@ class _DISPLAYCONFIG_MODE_INFO(ctypes.Structure):
]


_QDC_ONLY_ACTIVE_PATHS = 0x00000002
_DISPLAYCONFIG_PATH_ACTIVE = 0x00000001
_QDC_ONLY_ACTIVE_PATHS = 2
_DISPLAYCONFIG_PATH_ACTIVE = 1


# ==================================== DisplayConfig[Get/Set]DeviceInfo =========================================


_DPI_VALUES = [100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500]


class _DISPLAYCONFIG_DEVICE_INFO_HEADER(ctypes.Structure):
_fields_ = [
('type', ctypes.c_uint32),
Expand All @@ -247,6 +251,9 @@ class _DISPLAYCONFIG_DEVICE_INFO_HEADER(ctypes.Structure):
]


_DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE = -3 # returns min, max, and currently applied DPI scaling values


class _DISPLAYCONFIG_SOURCE_DPI_SCALE_GET(ctypes.Structure):
_fields_ = [
('header', _DISPLAYCONFIG_DEVICE_INFO_HEADER),
Expand All @@ -256,7 +263,7 @@ class _DISPLAYCONFIG_SOURCE_DPI_SCALE_GET(ctypes.Structure):
]


_DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE = -3 # returns min, max, and currently applied DPI scaling values.
_DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE = -4 # set current dpi scaling value for a display


class _DISPLAYCONFIG_SOURCE_DPI_SCALE_SET(ctypes.Structure):
Expand All @@ -266,4 +273,26 @@ class _DISPLAYCONFIG_SOURCE_DPI_SCALE_SET(ctypes.Structure):
]


_DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE = -4 # set current dpi scaling value for a display
_DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2


class _DISPLAY_CONFIG_TARGET_DEVICE_NAME(ctypes.Structure):
_fields_ = [
('header', _DISPLAYCONFIG_DEVICE_INFO_HEADER),
('flags', ctypes.c_uint32),
('outputTechnology', ctypes.c_uint32),
('edidManufactureId', ctypes.c_uint16),
('edidProductCodeId', ctypes.c_uint16),
('connectorInstance', ctypes.c_uint32),
('monitorFriendlyDeviceName', ctypes.wintypes.WCHAR * 64),
('monitorDevicePath', ctypes.wintypes.WCHAR * 128)
]


_DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1

class _DISPLAYCONFIG_SOURCE_DEVICE_NAME(ctypes.Structure):
_fields_ = [
('header', _DISPLAYCONFIG_DEVICE_INFO_HEADER),
('viewGdiDeviceName', ctypes.wintypes.WCHAR * 32)
]

0 comments on commit 93009cf

Please sign in to comment.