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 23, 2023
1 parent 8bcea4d commit 0c6a721
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 105 deletions.
Binary file modified dist/PyMonCtl-0.0.9-py3-none-any.whl
Binary file not shown.
18 changes: 9 additions & 9 deletions src/pymonctl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
from collections.abc import Callable
from typing import List, Optional, Union, Tuple

from .structs import *
from .structs import DisplayMode, ScreenValue, Size, Point, Box, Rect, Position, Orientation

__all__ = [
"version", "structs", "getAllMonitors", "_getAllMonitorsDict", "_getMonitorsCount",
"getPrimary", "findMonitor", "findMonitorInfo", "arrangeMonitors",
"getAllMonitors", "getPrimary", "findMonitor", "findMonitorInfo", "arrangeMonitors",
"enableUpdate", "disableUpdate", "isUpdateEnabled", "updateInterval",
"getMousePos", "Monitor"
"DisplayMode", "ScreenValue", "Size", "Point", "Box", "Rect", "Position", "Orientation",
"getMousePos", "version", "Monitor"
]

__version__ = "0.0.9"
Expand Down Expand Up @@ -567,8 +567,8 @@ def run(self):
if screens[s] != self._screens[s]:
names.append(s)
self._monitorPropsChanged(names, screens)
self._screens = screens
self._monitors = _getAllMonitors()
self._screens: dict[str, ScreenValue] = screens
self._monitors: list[Monitor] = _getAllMonitors()

self._kill.wait(self._interval)

Expand Down Expand Up @@ -735,15 +735,15 @@ def _getRelativePosition(monitor, relativeTo) -> Tuple[int, int, str]:

if sys.platform == "darwin":
from ._pymonctl_macos import (_getAllMonitors, _getAllMonitorsDict, _getMonitorsCount, _getPrimary,
_findMonitor, _arrangeMonitors, _getMousePos, Monitor
_findMonitor, _arrangeMonitors, _getMousePos, MacOSMonitor as Monitor
)
elif sys.platform == "win32":
from ._pymonctl_win import (_getAllMonitors, _getAllMonitorsDict, _getMonitorsCount, _getPrimary,
_findMonitor, _arrangeMonitors, _getMousePos, Monitor
_findMonitor, _arrangeMonitors, _getMousePos, Win32Monitor as Monitor
)
elif sys.platform == "linux":
from ._pymonctl_linux import (_getAllMonitors, _getAllMonitorsDict, _getMonitorsCount, _getPrimary,
_findMonitor, _arrangeMonitors, _getMousePos, Monitor
_findMonitor, _arrangeMonitors, _getMousePos, LinuxMonitor as Monitor
)
else:
raise NotImplementedError('PyMonCtl currently does not support this platform. If you think you can help, please contribute! https://github.com/Kalmat/PyMonCtl')
30 changes: 15 additions & 15 deletions src/pymonctl/_pymonctl_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from Xlib.ext import randr

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

Expand Down Expand Up @@ -61,12 +61,12 @@ def _XgetRoots():
_roots = _XgetRoots()


def _getAllMonitors() -> list[Monitor]:
def _getAllMonitors() -> list[LinuxMonitor]:
monitors = []
for outputData in _XgetAllOutputs():
display, screen, root, res, output, outputInfo = outputData
if outputInfo.crtc:
monitors.append(Monitor(output))
monitors.append(LinuxMonitor(output))
return monitors


Expand Down Expand Up @@ -126,16 +126,16 @@ def _getMonitorsCount() -> int:
return count


def _findMonitor(x: int, y: int) -> Optional[Monitor]:
def _findMonitor(x: int, y: int) -> Optional[LinuxMonitor]:
for monitor in _getAllMonitors():
if monitor.position is not None and monitor.size is not None:
if _pointInBox(x, y, monitor.position.x, monitor.position.y, monitor.size.width, monitor.size.height):
return monitor
return None


def _getPrimary() -> Monitor:
return Monitor()
def _getPrimary() -> LinuxMonitor:
return LinuxMonitor()


def _arrangeMonitors(arrangement: dict[str, dict[str, Union[str, int, Position, Point, Size]]]):
Expand All @@ -149,9 +149,9 @@ def _arrangeMonitors(arrangement: dict[str, dict[str, Union[str, int, Position,
relPos = arrangement[monName]["relativePos"]
relMon = arrangement[monName]["relativeTo"]
if monName not in monitors.keys() or (relMon and relMon not in monitors.keys()) or \
(not relMon and relPos != PRIMARY):
(not relMon and relPos != Position.PRIMARY):
return
elif relPos == PRIMARY:
elif relPos == Position.PRIMARY:
primaryPresent = True
if not primaryPresent:
return
Expand All @@ -170,7 +170,7 @@ def _getMousePos() -> Point:
return Point(mp.root_x, mp.root_y)


class Monitor(BaseMonitor):
class LinuxMonitor(BaseMonitor):

def __init__(self, handle: Optional[int] = None):
"""
Expand Down Expand Up @@ -279,7 +279,7 @@ def orientation(self) -> Optional[Union[int, Orientation]]:
return None

def setOrientation(self, orientation: Optional[Union[int, Orientation]]):
if orientation in (NORMAL, INVERTED, LEFT, RIGHT):
if orientation in (Orientation.NORMAL, Orientation.INVERTED, Orientation.LEFT, Orientation.RIGHT):
# outputs = _XgetAllOutputs(self.name)
# for outputData in outputs:
# display, screen, root, res, output, outputInfo = outputData
Expand All @@ -288,11 +288,11 @@ def setOrientation(self, orientation: Optional[Union[int, Orientation]]):
# randr.set_crtc_config(display, outputInfo.crtc, Xlib.X.CurrentTime, crtcInfo.x, crtcInfo.y,
# crtcInfo.mode, (orientation or 1) ** 2, crtcInfo.outputs)

if orientation == RIGHT:
if orientation == Orientation.RIGHT:
direction = "right"
elif orientation == INVERTED:
elif orientation == Orientation.INVERTED:
direction = "inverted"
elif orientation == LEFT:
elif orientation == Orientation.LEFT:
direction = "left"
else:
direction = "normal"
Expand Down Expand Up @@ -584,7 +584,7 @@ def _setPositionTwice(relativePos: Position, relativeTo: Optional[str], name: st


def _setPosition(relativePos: Position, relativeTo: Optional[str], name: str):
if relativePos == PRIMARY:
if relativePos == Position.PRIMARY:
_setPrimary(name)

else:
Expand All @@ -605,7 +605,7 @@ def _setPosition(relativePos: Position, relativeTo: Optional[str], name: str):
# cmd2 = " --noprimary" if name == primaryName else ""
cmd2 = ""
x, y, relCmd = _getRelativePosition(targetMon, relMon)
cmd3 = relCmd % relativeTo if relativePos in (LEFT_TOP, RIGHT_TOP, ABOVE_LEFT, BELOW_LEFT) else ""
cmd3 = relCmd % relativeTo if relativePos in (Position.LEFT_TOP, Position.RIGHT_TOP, Position.ABOVE_LEFT, Position.BELOW_LEFT) else ""
cmd = ("xrandr --output %s --pos %sx%s" % (name, x, y)) + cmd2 + cmd3
try:
subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL)
Expand Down
24 changes: 12 additions & 12 deletions src/pymonctl/_pymonctl_macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@
import Quartz.CoreGraphics as CG

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


def _getAllMonitors() -> list[Monitor]:
def _getAllMonitors() -> list[MacOSMonitor]:
monitors = []
screens = AppKit.NSScreen.screens()
for screen in screens:
desc = screen.deviceDescription()
displayId = desc['NSScreenNumber'] # Quartz.NSScreenNumber seems to be wrong
monitors.append(Monitor(displayId))
monitors.append(MacOSMonitor(displayId))
return monitors


Expand Down Expand Up @@ -105,19 +105,19 @@ def _getMonitorsCount() -> int:
return len(AppKit.NSScreen.screens())


def _findMonitor(x: int, y: int) -> Optional[Monitor]:
def _findMonitor(x: int, y: int) -> Optional[MacOSMonitor]:
screens = AppKit.NSScreen.screens()
for screen in screens:
frame = screen.frame()
if _pointInBox(x, y, int(frame.origin.x), int(frame.origin.y), int(frame.size.width), int(frame.size.height)):
desc = screen.deviceDescription()
displayId = desc['NSScreenNumber'] # Quartz.NSScreenNumber seems to be wrong
return Monitor(displayId)
return MacOSMonitor(displayId)
return None


def _getPrimary() -> Monitor:
return Monitor()
def _getPrimary() -> MacOSMonitor:
return MacOSMonitor()


def _arrangeMonitors(arrangement: dict[str, dict[str, Union[str, int, Position, Point, Size]]]):
Expand All @@ -131,9 +131,9 @@ def _arrangeMonitors(arrangement: dict[str, dict[str, Union[str, int, Position,
relPos = arrangement[monName]["relativePos"]
relMon = arrangement[monName]["relativeTo"]
if monName not in monitors.keys() or (relMon and relMon not in monitors.keys()) or \
(not relMon and relPos != PRIMARY):
(not relMon and relPos != Position.PRIMARY):
return
elif relPos == PRIMARY:
elif relPos == Position.PRIMARY:
primaryPresent = True
if not primaryPresent:
return
Expand Down Expand Up @@ -166,7 +166,7 @@ def _getMousePos(flipValues: bool = False) -> Point:
return Point(x, y)


class Monitor(BaseMonitor):
class MacOSMonitor(BaseMonitor):

def __init__(self, handle: Optional[int] = None):
"""
Expand Down Expand Up @@ -302,7 +302,7 @@ def dpi(self) -> Optional[Tuple[float, float]]:
@property
def orientation(self) -> Optional[Union[int, Orientation]]:
orientation = int(Quartz.CGDisplayRotation(self.handle) / 90)
if orientation in (NORMAL, INVERTED, LEFT, RIGHT):
if orientation in (Orientation.NORMAL, Orientation.INVERTED, Orientation.LEFT, Orientation.RIGHT):
return orientation
return None

Expand Down Expand Up @@ -423,7 +423,7 @@ def isPrimary(self):

def setPrimary(self):
# https://stackoverflow.com/questions/13722508/change-main-monitor-on-mac-programmatically#:~:text=To%20change%20the%20secondary%20monitor%20to%20be%20the,%28%29.%20A%20full%20sample%20can%20be%20found%20Here
self.setPosition(PRIMARY, "")
self.setPosition(Position.PRIMARY, "")

def turnOn(self):
# This works, but won't wake up the display despite if the mouse is moving and/or clicking
Expand Down
Loading

0 comments on commit 0c6a721

Please sign in to comment.