mirror of https://github.com/ralsina/xrandroll.git
102 lines
3.1 KiB
Python
102 lines
3.1 KiB
Python
"""Read/Write system display state using xrandr."""
|
|
|
|
import subprocess
|
|
|
|
from .monitor import Monitor, _split_by_lines_matching
|
|
|
|
|
|
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
|
|
)
|
|
|
|
|
|
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
|
|
self.update_replica_of()
|
|
|
|
def generate(self):
|
|
"""Create a list of xrandr invocations to match this state."""
|
|
results = []
|
|
for output, mon in self.monitors.items():
|
|
cli = ["xrandr"]
|
|
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}")
|
|
cli.append(f"--rate {mode.frequency}")
|
|
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")
|
|
results.append(" ".join(cli))
|
|
return results
|
|
|
|
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)
|
|
|
|
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
|
|
|
|
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
|
|
|
|
|
|
def read_data():
|
|
data = subprocess.check_output(
|
|
["xrandr", "--verbose"], encoding="utf-8"
|
|
).splitlines()
|
|
return data
|
|
|
|
|
|
def parse_data(data):
|
|
# Going to pretend there can only be one screen because life is short.
|
|
return Screen(_split_by_lines_matching("^Screen ", data)[0])
|