xrandroll/xrandroll/xrandr.py

102 lines
3.1 KiB
Python
Raw Permalink Normal View History

2020-02-05 13:47:20 -03:00
"""Read/Write system display state using xrandr."""
import subprocess
2020-02-05 17:39:52 -03:00
from .monitor import Monitor, _split_by_lines_matching
2020-02-05 16:11:17 -03:00
2020-02-05 13:47:20 -03:00
2020-02-05 18:21:32 -03:00
def is_replica_of(a, b):
"""Return True if monitor a is a replica of b.
Replica means same resolution and position.
"""
return (
a.pos_x == b.pos_x
and a.pos_y == b.pos_y
and a.res_x == b.res_x
and a.res_y == b.res_y
and b.enabled
)
2020-02-05 17:58:04 -03:00
class Screen:
"""A Screen is a collection of monitors."""
def __init__(self, data):
self.monitors = {}
for monitor_data in _split_by_lines_matching(r"^[^ \t].*", data[1:]):
m = Monitor(monitor_data)
self.monitors[m.output] = m
2020-02-05 18:21:32 -03:00
self.update_replica_of()
2020-02-05 21:03:42 -03:00
def generate(self):
2020-02-06 18:39:09 -03:00
"""Create a list of xrandr invocations to match this state."""
results = []
2020-02-05 21:03:42 -03:00
for output, mon in self.monitors.items():
2020-02-06 18:39:09 -03:00
cli = ["xrandr"]
2020-02-05 21:03:42 -03:00
cli.append(f"--output {output}")
if not mon.enabled:
cli.append("--off")
else:
mode = mon.get_current_mode()
cli.append(f"--pos {int(mon.pos_x)}x{int(mon.pos_y)}")
cli.append(f"--mode {mode.res_x}x{mode.res_y}")
2022-06-08 12:57:59 -03:00
cli.append(f"--rate {mode.frequency}")
2020-02-05 21:03:42 -03:00
mod_x, mod_y = mode.res_x, mode.res_y
if mon.orientation in ("left", "right"):
mod_x, mod_y = mod_y, mod_x
cli.append(f"--scale {mon.res_x/mod_x}x{mon.res_y/mod_y}")
cli.append(f"--rotate {mon.orientation}")
if mon.primary:
cli.append("--primary")
2020-02-06 18:39:09 -03:00
results.append(" ".join(cli))
return results
2020-02-05 21:03:42 -03:00
2020-02-05 18:21:32 -03:00
def update_replica_of(self):
"""Decide which monitors are replicas of each other and
mark them as such."""
for a in self.monitors:
self.monitors[a].replica_of = []
for b in self.monitors:
if a != b and is_replica_of(self.monitors[a], self.monitors[b]):
self.monitors[a].replica_of.append(b)
2020-02-05 17:58:04 -03:00
2020-02-05 18:36:22 -03:00
def choose_a_monitor(self):
"""Choose what monitor to select by default.
* Not disabled
* Primary, if possible
"""
candidate = None
for name, mon in self.monitors.items():
if not mon.enabled:
continue
if mon.primary:
return name
candidate = name
return candidate
2020-02-05 19:16:51 -03:00
def get_primary(self):
"""Return the primary monitor, if any."""
for mon in self.monitors.values():
if mon.primary:
return mon
return None
def set_primary(self, name):
for mon in self.monitors.values():
mon.primary = name == mon.output
2020-02-05 17:58:04 -03:00
2020-02-05 13:47:20 -03:00
def read_data():
2020-02-05 16:11:17 -03:00
data = subprocess.check_output(
["xrandr", "--verbose"], encoding="utf-8"
).splitlines()
2020-02-05 17:12:04 -03:00
return data
def parse_data(data):
2020-02-05 16:11:17 -03:00
# Going to pretend there can only be one screen because life is short.
2020-02-05 17:58:04 -03:00
return Screen(_split_by_lines_matching("^Screen ", data)[0])