Skip to content

Commit

Permalink
Refactor and improve RealDisplaySizeMM.py
Browse files Browse the repository at this point in the history
  - Add type hints to imports
  - Improve docstrings
  - Split get_wayland_display into functions
  - Handle None return from enumerate_displays
  - Add comments for code clarity
  • Loading branch information
oldgithubman committed Oct 14, 2024
1 parent bef8ada commit cd1f935
Showing 1 changed file with 118 additions and 75 deletions.
193 changes: 118 additions & 75 deletions DisplayCAL/RealDisplaySizeMM.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,79 @@
import re
import sys

from DisplayCAL.util_dbus import DBusObject, DBusException, BUSTYPE_SESSION
from DisplayCAL.util_dbus import BUSTYPE_SESSION, DBusException, DBusObject
from DisplayCAL.util_x import get_display as _get_x_display

if sys.platform == "darwin":
# Mac OS X has universal binaries in three flavors:
# - i386 & PPC
# - i386 & x86_64
# - i386 & ARM
# - i386 & PPC # noqa: SC100
# - i386 & x86_64 # noqa: SC100
# - i386 & ARM # noqa: SC100
if platform.architecture()[0].startswith("64"):
# TODO: Intel vs ARM (Apple Silicon) distinction
from DisplayCAL.lib64.RealDisplaySizeMM import *
from DisplayCAL.lib64.RealDisplaySizeMM import (
enumerate_displays,
GetXRandROutputXID,
RealDisplaySizeMM,
)
else:
# elif sys.platform == "win32":
# elif sys.platform == "win32": # noqa: SC100
# Windows have separate files
if sys.version_info[:2] == (3, 8):
from DisplayCAL.lib64.python38.RealDisplaySizeMM import *
from DisplayCAL.lib64.python38.RealDisplaySizeMM import ( # type: ignore
enumerate_displays,
GetXRandROutputXID,
RealDisplaySizeMM,
)
elif sys.version_info[:2] == (3, 9):
from DisplayCAL.lib64.python39.RealDisplaySizeMM import *
from DisplayCAL.lib64.python39.RealDisplaySizeMM import ( # type: ignore
enumerate_displays,
GetXRandROutputXID,
RealDisplaySizeMM,
)
elif sys.version_info[:2] == (3, 10):
from DisplayCAL.lib64.python310.RealDisplaySizeMM import *
from DisplayCAL.lib64.python310.RealDisplaySizeMM import ( # type: ignore
enumerate_displays,
GetXRandROutputXID,
RealDisplaySizeMM,
)
elif sys.version_info[:2] == (3, 11):
from DisplayCAL.lib64.python311.RealDisplaySizeMM import *
from DisplayCAL.lib64.python311.RealDisplaySizeMM import ( # type: ignore
enumerate_displays,
GetXRandROutputXID,
RealDisplaySizeMM,
)
elif sys.version_info[:2] == (3, 12):
from DisplayCAL.lib64.python312.RealDisplaySizeMM import *
from DisplayCAL.lib64.python312.RealDisplaySizeMM import ( # type: ignore
enumerate_displays,
GetXRandROutputXID,
RealDisplaySizeMM,
)
# else:
# pass

# TODO: For Linux use the ``xrandr`` command output which supplies everything.
# TODO: For Linux use the ``xrandr`` command output which supplies everything. # noqa: SC100
#
# ``xrandr --verbose`` gives all the info we need, including EDID which needs to
# be decoded:
# ``xrandr --verbose`` gives all the info we need, # noqa: SC100
# including EDID which needs to be decoded: # noqa: SC100
#
# ```python
# import codecs
# edid = codecs.decode(xrandr_edid_data, "hex")
# import codecs # noqa: SC100
# edid = codecs.decode(xrandr_edid_data, "hex") # noqa: SC100
# ```
#


_displays = None

_GetXRandROutputXID = GetXRandROutputXID
_RealDisplaySizeMM = RealDisplaySizeMM
_enumerate_displays = enumerate_displays
_GetXRandROutputXID = GetXRandROutputXID # type: ignore
_RealDisplaySizeMM = RealDisplaySizeMM # type: ignore
_enumerate_displays = enumerate_displays # type: ignore


def GetXRandROutputXID(display_no=0):
"""Return the XRandR output X11 ID of a given display.
"""
Return the XRandR output X11 ID of a given display.
Args:
display_no (int): Display number.
Expand All @@ -67,7 +92,8 @@ def GetXRandROutputXID(display_no=0):


def RealDisplaySizeMM(display_no=0):
"""Return the size (in mm) of a given display.
"""
Return the size (in mm) of a given display.
Args:
display_no (int): Display number.
Expand All @@ -84,8 +110,12 @@ def enumerate_displays():
"""Enumerate and return a list of displays."""
global _displays
_displays = _enumerate_displays()

if _displays is None:
_displays = []

for display in _displays:
desc = display["description"]
desc = display.get("description")
if desc:
match = re.findall(
rb"(.+?),? at (-?\d+), (-?\d+), width (\d+), height (\d+)", desc
Expand Down Expand Up @@ -113,42 +143,49 @@ def enumerate_displays():
def get_display(display_no=0):
if _displays is None:
enumerate_displays()
# Translate from Argyll display index to enumerated display index
# using the coordinates and dimensions

# Ensure _displays is not None after calling enumerate_displays
if _displays is None:
return None

# Translate from Argyll display index to enumerated display index using the # noqa: SC100
# coordinates and dimensions
from DisplayCAL.config import getcfg, is_virtual_display

if is_virtual_display(display_no):
return
return None
try:
argyll_display = getcfg("displays")[display_no]
except IndexError:
return
return None
else:
if argyll_display.endswith(" [PRIMARY]"):
argyll_display = " ".join(argyll_display.split(" ")[:-1])
for display in _displays:
if desc := display["description"]:
if desc := display.get("description"):
geometry = b"".join(desc.split(b"@ ")[-1:])
if argyll_display.endswith((b"@ " + geometry).decode("utf-8")):
return display
return None


def get_wayland_display(x, y, w, h):
"""Find matching Wayland display.
"""
Find matching Wayland display.
Given x, y, width and height of display geometry, find matching Wayland display.
"""
# Note that we apparently CANNNOT use width and height
# because the reported values from Argyll code and Mutter can be slightly
# different, e.g. 3660x1941 from Mutter vs 3656x1941 from Argyll when
# HiDPI is enabled. The xrandr output is also interesting in that case:
# $ xrandr
# Note that we apparently CANNOT use width and height because the reported
# values from Argyll code and Mutter can be slightly different, # noqa: SC100
# e.g. 3660x1941 from Mutter vs 3656x1941 from Argyll when HiDPI is enabled. # noqa: SC100
# The xrandr output is also interesting in that case: # noqa: SC100
# $ xrandr # noqa: SC100
# Screen 0: minimum 320 x 200, current 3660 x 1941, maximum 8192 x 8192
# XWAYLAND0 connected 3656x1941+0+0 (normal left inverted right x axis y axis) 0mm x 0mm
# 3656x1941 59.96*+
# Note the apparent mismatch between first and 2nd/3rd line.
# XWAYLAND0 connected 3656x1941+0+0 (normal left inverted right x axis y axis) 0mm x 0mm # noqa: SC100,B950
# 3656x1941 59.96*+ # noqa: SC100
# Note the apparent mismatch between first and 2nd/3rd line. # noqa: SC100
# Look for active display at x, y instead.
# Currently, only support for GNOME3/Mutter
# Currently, only support for GNOME 3 / Mutter
try:
iface = DBusObject(
BUSTYPE_SESSION,
Expand All @@ -157,44 +194,50 @@ def get_wayland_display(x, y, w, h):
)
res = iface.get_resources()
except DBusException:
pass
else:
# See
# https://github.com/GNOME/mutter/blob/master/src/org.gnome.Mutter.DisplayConfig.xml
output_storage = None
try:
found = False
crtcs = res[1]
# Look for matching CRTC
for crtc in crtcs:
if crtc[2:4] == (x, y) and crtc[6] != -1:
# Found our CRTC
crtc_id = crtc[0]
# Look for matching output
outputs = res[2]
for output in outputs:
if output[2] == crtc_id:
# Found our output
found = True
output_storage = output
break
if found:
break
if found and output_storage is not None:
properties = output_storage[7]
wayland_display = {"xrandr_name": output_storage[4]}
raw_edid = properties.get("edid", ())
edid = b"".join(v.to_bytes(1, "big") for v in raw_edid)

if edid:
wayland_display["edid"] = edid
w_mm = properties.get("width-mm")
h_mm = properties.get("height-mm")
if w_mm and h_mm:
wayland_display["size_mm"] = (w_mm, h_mm)
return wayland_display
except (IndexError, KeyError):
pass
return None

# See
# https://github.com/GNOME/mutter/blob/master/src/org.gnome.Mutter.DisplayConfig.xml
output_storage = find_matching_output(res, x, y)
if output_storage is not None:
return create_wayland_display_dict(output_storage)

return None


def find_matching_output(res, x, y):
"""Find the matching output in the resources."""
crtcs = res[1]
# Look for matching CRTC # noqa: SC100
for crtc in crtcs:
if crtc[2:4] == (x, y) and crtc[6] != -1:
# Found our CRTC # noqa: SC100
crtc_id = crtc[0]
# Look for matching output
outputs = res[2]
for output in outputs:
if output[2] == crtc_id:
# Found our output
return output
return None


def create_wayland_display_dict(output_storage):
"""Create a dictionary with Wayland display information."""
properties = output_storage[7]
wayland_display = {"xrandr_name": output_storage[4]}

raw_edid = properties.get("edid", ())
edid = b"".join(v.to_bytes(1, "big") for v in raw_edid)
if edid:
wayland_display["edid"] = edid

w_mm = properties.get("width-mm")
h_mm = properties.get("height-mm")
if w_mm and h_mm:
wayland_display["size_mm"] = (w_mm, h_mm)

return wayland_display


def get_x_display(display_no=0):
Expand Down

0 comments on commit cd1f935

Please sign in to comment.