Skip to content

Commit

Permalink
parts/displays: puavodisplays.xrandr: parse output modes
Browse files Browse the repository at this point in the history
  • Loading branch information
tuomasjjrasanen committed Feb 20, 2024
1 parent 0fc970e commit c2c9289
Show file tree
Hide file tree
Showing 2 changed files with 721 additions and 3 deletions.
74 changes: 71 additions & 3 deletions parts/displays/puavodisplays/xrandr.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Standard library imports
import collections
import enum
import fractions
import logging
import re
import subprocess
Expand Down Expand Up @@ -74,7 +75,7 @@ def __repr__(self) -> str:
),
(
_TokenId.MODE,
r"^ (?P<resolution>\d+x\d+) (?P<rates>.*?)\s*$",
r"^ (?P<resolution_x>\d+)x(?P<resolution_y>\d+) \s*(?P<refresh_rates>.*?)\s*$",
),
(
_TokenId.PROP_ATTR_RANGE,
Expand Down Expand Up @@ -108,6 +109,16 @@ def _tokenize(line: str) -> typing.Tuple[_TokenId, typing.Dict[str, str]]:
raise UnexpectedOutputError("invalid output line", line)


def _parse_refresh_rate(value: str) -> typing.Tuple[float, bool, bool]:
refresh_rate_part_match = re.match(r"^([0-9.]+)(\*)?(\+)?$", value)
if refresh_rate_part_match is None:
raise UnexpectedOutputError("Invalid refresh rate", value)

(refresh_rate, is_current, is_preferred) = refresh_rate_part_match.groups()

return float(refresh_rate), is_preferred is not None, is_current is not None


class _XRandrPropOutputParser: # pylint: disable=too-few-public-methods
def __init__(self) -> None:
self.__transitions = {
Expand Down Expand Up @@ -141,9 +152,15 @@ def __init__(self) -> None:
self.__action_create_output,
_State.OUTPUT,
),
(_State.OUTPUT_PROP, _TokenId.MODE): (None, _State.OUTPUT_MODE),
(_State.OUTPUT_PROP, _TokenId.MODE): (
self.__action_add_mode,
_State.OUTPUT_MODE,
),
(_State.OUTPUT_PROP, _TokenId.EOF): (None, _State.DONE),
(_State.OUTPUT_MODE, _TokenId.MODE): (None, _State.OUTPUT_MODE),
(_State.OUTPUT_MODE, _TokenId.MODE): (
self.__action_add_mode,
_State.OUTPUT_MODE,
),
(_State.OUTPUT_MODE, _TokenId.CONNECTOR): (
self.__action_create_output,
_State.OUTPUT,
Expand Down Expand Up @@ -207,6 +224,57 @@ def __action_add_prop_attr_supported(
v.strip() for v in supported_values.split(",")
]

def __set_current_mode(self, mode: typing.Dict[str, typing.Any]) -> None:
if "current_mode" in self.__last_output:
raise UnexpectedOutputError("Multiple current modes")
self.__last_output["current_mode"] = mode

def __set_preferred_mode(self, mode: typing.Dict[str, typing.Any]) -> None:
if "preferred_mode" in self.__last_output:
raise UnexpectedOutputError("Multiple preferred modes")
self.__last_output["preferred_mode"] = mode

def __action_add_mode(
self,
token_id: _TokenId, # pylint: disable=unused-argument
resolution_x: str,
resolution_y: str,
refresh_rates: str,
) -> None:
refresh_rate_parts = refresh_rates.split()
if len(refresh_rate_parts) == 0:
raise UnexpectedOutputError("Invalid refresh rates", refresh_rates)

modes = self.__last_output.setdefault("modes", [])
for refresh_rate_part in refresh_rate_parts:
if refresh_rate_part == "+":
# Lonely + marks the last refresh rate as the preferred.
self.__set_preferred_mode(modes[-1])
continue

refresh_rate, is_preferred, is_current = _parse_refresh_rate(
refresh_rate_part
)

resolution_x_int = int(resolution_x, 10)
resolution_y_int = int(resolution_y, 10)

mode = {
"resolution_x": resolution_x_int,
"resolution_y": resolution_y_int,
"refresh_rate": refresh_rate,
"aspect_ratio": str(
fractions.Fraction(resolution_x_int, resolution_y_int)
).replace("/", ":"),
}

modes.append(mode)

if is_preferred:
self.__set_preferred_mode(mode)
if is_current:
self.__set_current_mode(mode)

def __push(
self, token_id: _TokenId, token_groupdict: typing.Dict[str, str]
) -> None:
Expand Down
Loading

0 comments on commit c2c9289

Please sign in to comment.