Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
0.93, 2026/XX/XX -- ALL: Replaced bare `except:` clauses with `except Exception:`, preventing accidental suppression of system-exit signals (KeyboardInterrupt, SystemExit) during error handling.
ALL: Removed dependency on `typing_extensions`
ALL: Allow `getPrimary` to return `None` if the underlying `Monitor()` call throws a `ValueError` (no primary monitor)
0.8, 2023/10/11 -- ALL: Added saveSetup() and restoreSetup(). Fixed / Improved watchdog (especially in Linux). Fixed / improved setPosition() method
LINUX: Added ewmhlib as separate module. Fixed watchdog (freezing randomly invoking screen_resources and get_output_info), fixed workarea crash (some apps/environments do not set it), improved to work almost fine in Manjaro/KDE, avoid crashing in Wayland for "fake" :1 display (though module won't likely work)
WIN32: Fixed dev.StateFlags returning weird values for multi-monitor. Fixed GetAwarenessFromDpiAwarenessContext not supported on Windows Server
Expand Down
33 changes: 24 additions & 9 deletions src/pymonctl/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,20 @@ def getMonitorsCount() -> int:
"""
return _getMonitorsCount()


def getPrimary() -> Monitor:
def getPrimary() -> Monitor | None:
"""
Get primary monitor instance. This is equivalent to invoking ''Monitor()'', with empty input params.
Get primary monitor instance.

This is equivalent to invoking `Monitor()`, with empty input params.
With a fallback to None in case of `ValueError`.
Which usually means lack of primary (or any) monitor.

:return: Monitor instance or None
"""
return _getPrimary()
try:
return Monitor()
except ValueError:
return None
Comment thread
Kalmat marked this conversation as resolved.


def findMonitorsAtPoint(x: int, y: int) -> list[Monitor]:
Expand Down Expand Up @@ -202,6 +208,15 @@ def arrangeMonitors(arrangement: dict[str, dict[str, str | int | Position | Poin
_arrangeMonitors(arrangement)


def getMousePos() -> Point:
Comment thread
Kalmat marked this conversation as resolved.
"""
Get the current (x, y) coordinates of the mouse pointer on screen, in pixels

:return: Point struct
"""
return _getMousePos()


def saveSetup() -> list[tuple[Monitor, ScreenValue]]:
"""
Save current monitors setup information to be restored afterward.
Expand Down Expand Up @@ -1003,16 +1018,16 @@ def _getRelativePosition(monitor, relativeTo) -> tuple[int, int]:


if sys.platform == "darwin":
from ._pymonctl_macos import (_getAllMonitors, _getAllMonitorsDict, _getMonitorsCount, _getPrimary,
_findMonitor, _arrangeMonitors, _getMousePos as getMousePos, MacOSMonitor as Monitor
from ._pymonctl_macos import (_getAllMonitors, _getAllMonitorsDict, _getMonitorsCount,
_findMonitor, _arrangeMonitors, _getMousePos, MacOSMonitor as Monitor
)
elif sys.platform == "win32":
from ._pymonctl_win import (_getAllMonitors, _getAllMonitorsDict, _getMonitorsCount, _getPrimary,
_findMonitor, _arrangeMonitors, _getMousePos as getMousePos, Win32Monitor as Monitor
from ._pymonctl_win import (_getAllMonitors, _getAllMonitorsDict, _getMonitorsCount,
_findMonitor, _arrangeMonitors, _getMousePos, Win32Monitor as Monitor
)
elif sys.platform == "linux":
from ._pymonctl_linux import (_getAllMonitors, _getAllMonitorsDict, _getAllMonitorsDictThread, _getMonitorsData,
_getMonitorsCount, _getPrimary, _findMonitor, _arrangeMonitors, _getMousePos as getMousePos,
_getMonitorsCount, _findMonitor, _arrangeMonitors, _getMousePos,
LinuxMonitor as Monitor
)
else:
Expand Down
5 changes: 1 addition & 4 deletions src/pymonctl/_pymonctl_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ def _findMonitor(x: int, y: int) -> list[LinuxMonitor]:
return monitors


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


def _arrangeMonitors(arrangement: dict[str, dict[str, str | int | Position | Point | Size | None]]):

monitors = _XgetMonitorsDict()
Expand Down Expand Up @@ -224,6 +220,7 @@ def __init__(self, handle: int | None = None) -> None:
getPrimary() or findMonitor(x, y).

It can raise ValueError exception in case provided handle is not valid
or there's no primary monitor with no provided handle.
Comment thread
Kalmat marked this conversation as resolved.
"""
monitorData = _XgetMonitorData(handle)
if monitorData:
Expand Down
5 changes: 0 additions & 5 deletions src/pymonctl/_pymonctl_macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,7 @@ def _findMonitor(x: int, y: int) -> list[MacOSMonitor]:
return [MacOSMonitor(displayId) for displayId in ids]


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


def _arrangeMonitors(arrangement: dict[str, dict[str, str | int | Position | Point | Size | None]]):

monitors = _NSgetAllMonitorsDict()
primaryPresent = False
setAsPrimary = ""
Expand Down
4 changes: 0 additions & 4 deletions src/pymonctl/_pymonctl_win.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,6 @@ def _findMonitor(x: int, y: int) -> list[Win32Monitor]:
return []


def _getPrimary() -> Win32Monitor:
return Win32Monitor()


def _arrangeMonitors(arrangement: dict[str, dict[str, str | int | Position | Point | Size | None]]):
# https://stackoverflow.com/questions/35814309/winapi-changedisplaysettingsex-does-not-work
# https://stackoverflow.com/questions/195267/use-windows-api-from-c-sharp-to-set-primary-monitor
Expand Down
7 changes: 2 additions & 5 deletions tests/test_pymonctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import pymonctl as pmc


_TIMELAP = 5


Expand All @@ -25,10 +24,8 @@ def changedCB(names: list[str], info: dict[str, pmc.ScreenValue]) -> None:


print("MONITORS COUNT:", pmc.getMonitorsCount())
try:
print("PRIMARY MONITOR:", pmc.getPrimary().name)
except ValueError:
print("PRIMARY MONITOR: None")
primary = pmc.getPrimary()
print("PRIMARY MONITOR:", primary and primary.name)
print()
monDict = pmc.getAllMonitorsDict()
for mon in monDict:
Expand Down
Loading