Skip to content

Commit

Permalink
New approach based on Monitor() class to access all properties and fu…
Browse files Browse the repository at this point in the history
…nctionalities. macOS is still experimental and not tested on multi-monitor setups.
  • Loading branch information
Kalmat committed Jul 24, 2023
1 parent 0c6a721 commit 6af97e9
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 30 deletions.
Binary file modified dist/PyMonCtl-0.0.9-py3-none-any.whl
Binary file not shown.
4 changes: 2 additions & 2 deletions src/pymonctl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,8 +567,8 @@ def run(self):
if screens[s] != self._screens[s]:
names.append(s)
self._monitorPropsChanged(names, screens)
self._screens: dict[str, ScreenValue] = screens
self._monitors: list[Monitor] = _getAllMonitors()
self._screens = screens
self._monitors = _getAllMonitors()

self._kill.wait(self._interval)

Expand Down
4 changes: 2 additions & 2 deletions src/pymonctl/_pymonctl_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import Xlib.X
from Xlib.ext import randr

from pymonctl import BaseMonitor, _pointInBox, _getRelativePosition
from .structs import DisplayMode, ScreenValue, Box, Rect, Point, Size, Position, Orientation
from pymonctl import BaseMonitor, _pointInBox, _getRelativePosition, \
DisplayMode, ScreenValue, Box, Rect, Point, Size, Position, Orientation
from ewmhlib import defaultRootWindow, getProperty, getPropertyValue
from ewmhlib.Props import *

Expand Down
4 changes: 2 additions & 2 deletions src/pymonctl/_pymonctl_macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import Quartz
import Quartz.CoreGraphics as CG

from pymonctl import BaseMonitor, _pointInBox, _getRelativePosition
from .structs import DisplayMode, ScreenValue, Box, Rect, Point, Size, Position, Orientation
from pymonctl import BaseMonitor, _pointInBox, _getRelativePosition, \
DisplayMode, ScreenValue, Box, Rect, Point, Size, Position, Orientation
# from ._display_manager_lib import Display


Expand Down
25 changes: 14 additions & 11 deletions src/pymonctl/_pymonctl_win.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import win32evtlog
import win32gui

from pymonctl import BaseMonitor, _getRelativePosition
from .structs import DisplayMode, ScreenValue, Box, Rect, Point, Size, Position, Orientation
from pymonctl import BaseMonitor, _getRelativePosition, \
DisplayMode, ScreenValue, Box, Rect, Point, Size, Position, Orientation


dpiAware = ctypes.windll.user32.GetAwarenessFromDpiAwarenessContext(ctypes.windll.user32.GetThreadDpiAwarenessContext())
Expand Down Expand Up @@ -221,19 +221,19 @@ def _getPaths(self):
paths = (pymonctl.structs._DISPLAYCONFIG_PATH_INFO * numPathArrayElements.value)()
print(ctypes.sizeof(pymonctl.structs._DISPLAYCONFIG_PATH_INFO()), ctypes.sizeof(paths))
modes = (pymonctl.structs._DISPLAYCONFIG_MODE_INFO * numModeInfoArrayElements.value)()
print(ctypes.sizeof(pymonctl.structs._DISPLAYCONFIG_MODE_INFO), ctypes.sizeof(modes))
print(ctypes.sizeof(pymonctl.structs._DISPLAYCONFIG_MODE_INFO()), ctypes.sizeof(modes))
nullptr = ctypes.c_void_p() # or None?
ret = ctypes.windll.user32.QueryDisplayConfig(flags,
ctypes.byref(numPathArrayElements),
ctypes.byref(paths),
ctypes.byref(numModeInfoArrayElements),
ctypes.byref(modes),
None
nullptr
)
print("RET", ret, "PATHS", numPathArrayElements.value, "MODES", numModeInfoArrayElements.value)
if ret == 0:
for i in range(numPathArrayElements.value):
pathInfo: pymonctl.structs._DISPLAYCONFIG_PATH_INFO = paths.value[i]
pathInfo: pymonctl.structs._DISPLAYCONFIG_PATH_INFO = paths[i].value
print(pathInfo)
else:
print("FAILED!!! (I guess you are gonna see this a ridiculously huge number of times...)")
Expand All @@ -242,21 +242,23 @@ def setScale(self, scale: Tuple[float, float]):
if scale is not None:
# https://github.com/lihas/windows-DPI-scaling-sample/blob/master/DPIHelper/DpiHelper.cpp

self._getPaths()
# self._getPaths()

scaleData = pymonctl.structs._DISPLAYCONFIG_SOURCE_DPI_SCALE_GET()
scaleData.header.type = pymonctl.structs._DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE
scaleData.header.size = ctypes.sizeof(scaleData)
# 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 = self.handle
scaleData.header.id = 1
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
print(ctypes.windll.user32.DisplayConfigGetDeviceInfo(ctypes.byref(scaleData)))
print(scaleData.minScaleRel, scaleData.curScaleRel, scaleData.maxScaleRel)
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))

@property
Expand Down Expand Up @@ -416,7 +418,8 @@ def turnOn(self):
else:
# This will not work in modern systems
win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, win32con.WM_SYSCOMMAND, win32con.SC_MONITORPOWER, -1,
win32con.SMTO_ABORTIFHUNG, 100) # A mouse move can do the trick (for ALL monitors)
win32con.SMTO_ABORTIFHUNG, 100)
# A mouse move can do the trick (for ALL monitors)
mx, my = _getMousePos()
_win32moveMouse(mx + 1, my + 1)
_win32moveMouse(mx, my)
Expand Down
12 changes: 6 additions & 6 deletions src/pymonctl/structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class _DUMMYUNIONNAME(ctypes.Union):
class _LUID(ctypes.Structure):
_fields_ = [
('lowPart', ctypes.wintypes.DWORD),
('highPart', ctypes.c_long)
('highPart', ctypes.wintypes.LONG)
]


Expand Down Expand Up @@ -184,8 +184,8 @@ class _DISPLAYCONFIG_TARGET_MODE(ctypes.Structure):

class _POINTL(ctypes.Structure):
_fields_ = [
('x', ctypes.c_long),
('y', ctypes.c_long)
('x', ctypes.wintypes.LONG),
('y', ctypes.wintypes.LONG)
]


Expand Down Expand Up @@ -240,9 +240,9 @@ class _DISPLAYCONFIG_MODE_INFO(ctypes.Structure):

class _DISPLAYCONFIG_DEVICE_INFO_HEADER(ctypes.Structure):
_fields_ = [
('type', ctypes.c_uint),
('type', ctypes.c_uint32),
('size', ctypes.c_uint32),
('adapterId', ctypes.c_uint32),
('adapterId', _LUID),
('id', ctypes.c_uint32)
]

Expand All @@ -256,7 +256,7 @@ class _DISPLAYCONFIG_SOURCE_DPI_SCALE_GET(ctypes.Structure):
]


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


class _DISPLAYCONFIG_SOURCE_DPI_SCALE_SET(ctypes.Structure):
Expand Down
15 changes: 8 additions & 7 deletions tests/test_pymonctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pymonctl as pmc
from pymonctl.structs import *


def countChanged(names, screensInfo):
for name in names:
print("MONITORS COUNT CHANGED:", name, screensInfo[name])
Expand Down Expand Up @@ -96,10 +97,10 @@ def changedCB(names, info):
print()

print("CHANGE ORIENTATION")
monitor.setOrientation(INVERTED)
monitor.setOrientation(Orientation.INVERTED)
time.sleep(5)
print("RESTORE ORIENTATION")
monitor.setOrientation(NORMAL)
monitor.setOrientation(Orientation.NORMAL)
time.sleep(3)
print()

Expand Down Expand Up @@ -162,7 +163,7 @@ def changedCB(names, info):
print()

print("CHANGE POSITION OF MONITOR 2 TO BELOW_LEFT")
mon2.setPosition(BELOW_LEFT, mon1.name)
mon2.setPosition(Position.BELOW_LEFT, mon1.name)
print("MONITOR 2 POSITION:", mon2.position)
while True:
try:
Expand All @@ -173,8 +174,8 @@ def changedCB(names, info):

print("CHANGE ARRANGEMENT: MONITOR 2 AS PRIMARY, MONITOR 1 AT LEFT_BOTTOM")
arrangement: dict[str, dict[str, Union[str, int, Position, Point, Size]]] = {
str(mon2.name): {"relativePos": PRIMARY, "relativeTo": ""},
str(mon1.name): {"relativePos": LEFT_BOTTOM, "relativeTo": mon2.name}
str(mon2.name): {"relativePos": Position.PRIMARY, "relativeTo": ""},
str(mon1.name): {"relativePos": Position.LEFT_BOTTOM, "relativeTo": mon2.name}
}
print(arrangement)
pmc.arrangeMonitors(arrangement)
Expand All @@ -189,8 +190,8 @@ def changedCB(names, info):

print("CHANGE ARRANGEMENT: MONITOR 1 AS PRIMARY, MONITOR 2 AT RIGHT_TOP")
arrangement = {
str(mon1.name): {"relativePos": PRIMARY, "relativeTo": ""},
str(mon2.name): {"relativePos": RIGHT_TOP, "relativeTo": mon1.name}
str(mon1.name): {"relativePos": Position.PRIMARY, "relativeTo": ""},
str(mon2.name): {"relativePos": Position.RIGHT_TOP, "relativeTo": mon1.name}
}
print(arrangement)
pmc.arrangeMonitors(arrangement)
Expand Down

0 comments on commit 6af97e9

Please sign in to comment.