mirror of
https://github.com/ralsina/xrandroll.git
synced 2024-11-22 02:52:23 +00:00
Sort of starting to work with the new data structures
This commit is contained in:
parent
08ce2032ab
commit
1d8769c334
@ -2,7 +2,6 @@ import os
|
|||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from copy import deepcopy
|
|
||||||
|
|
||||||
from PySide2.QtCore import QFile, QObject
|
from PySide2.QtCore import QFile, QObject
|
||||||
from PySide2.QtUiTools import QUiLoader
|
from PySide2.QtUiTools import QUiLoader
|
||||||
@ -75,20 +74,6 @@ def parse_monitor(line):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
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 Window(QObject):
|
class Window(QObject):
|
||||||
def __init__(self, ui):
|
def __init__(self, ui):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -100,7 +85,6 @@ class Window(QObject):
|
|||||||
self.ui.orientationCombo.currentIndexChanged.connect(self.orientation_changed)
|
self.ui.orientationCombo.currentIndexChanged.connect(self.orientation_changed)
|
||||||
self.xrandr_info = {}
|
self.xrandr_info = {}
|
||||||
self.get_xrandr_info()
|
self.get_xrandr_info()
|
||||||
self.orig_xrandr_info = deepcopy(self.xrandr_info)
|
|
||||||
self.fill_ui()
|
self.fill_ui()
|
||||||
self.ui.horizontalScale.valueChanged.connect(self.scale_changed)
|
self.ui.horizontalScale.valueChanged.connect(self.scale_changed)
|
||||||
self.ui.verticalScale.valueChanged.connect(self.scale_changed)
|
self.ui.verticalScale.valueChanged.connect(self.scale_changed)
|
||||||
@ -132,17 +116,21 @@ class Window(QObject):
|
|||||||
self.adjust_view()
|
self.adjust_view()
|
||||||
|
|
||||||
def primary_changed(self):
|
def primary_changed(self):
|
||||||
mon = self.ui.screenCombo.currentText()
|
mon_name = self.ui.screenCombo.currentText()
|
||||||
primary = self.ui.primary.isChecked()
|
primary = self.ui.primary.isChecked()
|
||||||
|
if primary:
|
||||||
|
self.screen.set_primary(mon_name)
|
||||||
|
else:
|
||||||
|
self.screen.set_primary("foobar") # no primary
|
||||||
|
|
||||||
# Update visuals on all monitos
|
# TODO Update visuals on all monitos
|
||||||
for name, monitor in self.xrandr_info.items():
|
# for name, monitor in self.xrandr_info.items():
|
||||||
if name == mon:
|
# if name == mon:
|
||||||
monitor["primary"] = primary
|
# monitor["primary"] = primary
|
||||||
else:
|
# else:
|
||||||
if primary: # There can only be one primary
|
# if primary: # There can only be one primary
|
||||||
monitor["primary"] = False
|
# monitor["primary"] = False
|
||||||
monitor["item"].update_visuals(monitor)
|
# monitor["item"].update_visuals(monitor)
|
||||||
|
|
||||||
def scale_mode_changed(self):
|
def scale_mode_changed(self):
|
||||||
mon = self.ui.screenCombo.currentText()
|
mon = self.ui.screenCombo.currentText()
|
||||||
@ -253,57 +241,19 @@ class Window(QObject):
|
|||||||
subprocess.check_call(shlex.split(cli))
|
subprocess.check_call(shlex.split(cli))
|
||||||
|
|
||||||
def fill_ui(self):
|
def fill_ui(self):
|
||||||
"""Load data from xrandr and setup the whole thing."""
|
"""Configure UI out of our screen data."""
|
||||||
self.scene = QGraphicsScene(self)
|
self.scene = QGraphicsScene(self)
|
||||||
self.ui.sceneView.setScene(self.scene)
|
self.ui.sceneView.setScene(self.scene)
|
||||||
self.ui.screenCombo.clear()
|
self.ui.screenCombo.clear()
|
||||||
|
|
||||||
for name, monitor in self.xrandr_info.items():
|
for name, monitor in self.screen.monitors.items():
|
||||||
self.ui.screenCombo.addItem(name)
|
self.ui.screenCombo.addItem(name)
|
||||||
mon_item = MonitorItem(data=monitor, window=self, name=name,)
|
mon_item = MonitorItem(data=monitor, window=self, name=name,)
|
||||||
self.scene.addItem(mon_item)
|
self.scene.addItem(mon_item)
|
||||||
monitor["item"] = mon_item
|
monitor.item = mon_item
|
||||||
self.ui.screenCombo.setCurrentText(self.choose_a_monitor())
|
self.ui.screenCombo.setCurrentText(self.screen.choose_a_monitor())
|
||||||
self.adjust_view()
|
self.adjust_view()
|
||||||
self.scale_changed() # Trigger scale labels update
|
# self.scale_changed() # Trigger scale labels update
|
||||||
|
|
||||||
def detect_scale_mode(self, monitor):
|
|
||||||
"""Given a monitor's data, try to guess what scaling
|
|
||||||
mode it's using.
|
|
||||||
|
|
||||||
TODO: detect "Automatic: physical dimensions"
|
|
||||||
"""
|
|
||||||
if not monitor["current_mode"]: # Disabled, whatever
|
|
||||||
return None
|
|
||||||
|
|
||||||
mod_x, mod_y = parse_mode(monitor["current_mode"])
|
|
||||||
scale_x = monitor["res_x"] / mod_x
|
|
||||||
scale_y = monitor["res_y"] / mod_y
|
|
||||||
|
|
||||||
if 1 == scale_x == scale_y:
|
|
||||||
print("Scale mode looks like 1x1")
|
|
||||||
return "Disabled (1x1)"
|
|
||||||
elif scale_x == scale_y:
|
|
||||||
print("Looks like Manual, same in both dimensions")
|
|
||||||
return "Manual, same in both dimensions"
|
|
||||||
else:
|
|
||||||
return "Manual"
|
|
||||||
|
|
||||||
def choose_a_monitor(self):
|
|
||||||
"""Choose what monitor to select by default.
|
|
||||||
|
|
||||||
* Not disabled
|
|
||||||
* Primary, if possible
|
|
||||||
"""
|
|
||||||
|
|
||||||
candidate = None
|
|
||||||
for name, mon in self.xrandr_info.items():
|
|
||||||
if not mon["enabled"]:
|
|
||||||
continue
|
|
||||||
if mon["primary"]:
|
|
||||||
return name
|
|
||||||
candidate = name
|
|
||||||
return candidate
|
|
||||||
|
|
||||||
def orientation_changed(self):
|
def orientation_changed(self):
|
||||||
mon = self.ui.screenCombo.currentText()
|
mon = self.ui.screenCombo.currentText()
|
||||||
@ -317,24 +267,20 @@ class Window(QObject):
|
|||||||
if not mode:
|
if not mode:
|
||||||
return
|
return
|
||||||
print(f"Changing {mon} to {mode}")
|
print(f"Changing {mon} to {mode}")
|
||||||
self.xrandr_info[mon]["current_mode"] = mode
|
monitor = self.screen.monitors[mon]
|
||||||
mode_x, mode_y = parse_mode(mode)
|
monitor.set_current_mode(mode)
|
||||||
|
mode_x, mode_y = (
|
||||||
|
monitor.get_current_mode().res_x,
|
||||||
|
monitor.get_current_mode().res_y,
|
||||||
|
)
|
||||||
# use resolution via scaling
|
# use resolution via scaling
|
||||||
if self.xrandr_info[mon]["orientation"] in (0, 2):
|
if monitor.orientation in ("normal", "inverted"):
|
||||||
self.xrandr_info[mon]["res_x"] = int(
|
monitor.res_x = int(mode_x * self.ui.horizontalScale.value() / 1000)
|
||||||
mode_x * self.ui.horizontalScale.value() / 1000
|
monitor.res_y = int(mode_y * self.ui.verticalScale.value() / 1000)
|
||||||
)
|
|
||||||
self.xrandr_info[mon]["res_y"] = int(
|
|
||||||
mode_y * self.ui.verticalScale.value() / 1000
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.xrandr_info[mon]["res_x"] = int(
|
monitor.res_x = int(mode_y * self.ui.horizontalScale.value() / 1000)
|
||||||
mode_y * self.ui.horizontalScale.value() / 1000
|
monitor.res_y = int(mode_x * self.ui.verticalScale.value() / 1000)
|
||||||
)
|
# TODO self.xrandr_info[mon]["item"].update_visuals(self.xrandr_info[mon])
|
||||||
self.xrandr_info[mon]["res_y"] = int(
|
|
||||||
mode_x * self.ui.verticalScale.value() / 1000
|
|
||||||
)
|
|
||||||
self.xrandr_info[mon]["item"].update_visuals(self.xrandr_info[mon])
|
|
||||||
|
|
||||||
def show_pos(self, x, y):
|
def show_pos(self, x, y):
|
||||||
self.pos_label.setText(f"{x},{y}")
|
self.pos_label.setText(f"{x},{y}")
|
||||||
@ -346,7 +292,7 @@ class Window(QObject):
|
|||||||
item = mon["item"]
|
item = mon["item"]
|
||||||
mon["pos_x"] = item.x()
|
mon["pos_x"] = item.x()
|
||||||
mon["pos_y"] = item.y()
|
mon["pos_y"] = item.y()
|
||||||
self.update_replica_of_data()
|
self.screen.update_replica_of()
|
||||||
for _, mon in self.xrandr_info.items():
|
for _, mon in self.xrandr_info.items():
|
||||||
mon["item"].update_visuals(mon)
|
mon["item"].update_visuals(mon)
|
||||||
self.adjust_view()
|
self.adjust_view()
|
||||||
@ -357,76 +303,36 @@ class Window(QObject):
|
|||||||
snaps_x = []
|
snaps_x = []
|
||||||
snaps_y = []
|
snaps_y = []
|
||||||
|
|
||||||
for monitor, data in self.xrandr_info.items():
|
for output, monitor in self.screen.monitors.items():
|
||||||
if monitor == name:
|
if output == name:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
mod_x, mod_y = parse_mode(data["current_mode"])
|
mode = monitor.get_current_mode()
|
||||||
snaps_x.append(data["pos_x"])
|
mod_x, mod_y = mode.res_x, mode.res_y
|
||||||
snaps_x.append(data["pos_x"] + mod_x)
|
snaps_x.append(monitor.pos_x)
|
||||||
snaps_y.append(data["pos_y"])
|
snaps_x.append(monitor.pos_x + mod_x)
|
||||||
snaps_y.append(data["pos_y"] + mod_y)
|
snaps_y.append(monitor.pos_x)
|
||||||
|
snaps_y.append(monitor.pos_x + mod_y)
|
||||||
return snaps_x, snaps_y
|
return snaps_x, snaps_y
|
||||||
|
|
||||||
def adjust_view(self):
|
def adjust_view(self):
|
||||||
self.ui.sceneView.resetTransform()
|
self.ui.sceneView.resetTransform()
|
||||||
self.ui.sceneView.ensureVisible(self.scene.sceneRect(), 100, 100)
|
self.ui.sceneView.ensureVisible(self.scene.sceneRect(), 100, 100)
|
||||||
scale_factor = 0.8 * min(
|
try:
|
||||||
self.ui.sceneView.width() / self.scene.sceneRect().width(),
|
scale_factor = 0.8 * min(
|
||||||
self.ui.sceneView.height() / self.scene.sceneRect().height(),
|
self.ui.sceneView.width() / self.scene.sceneRect().width(),
|
||||||
)
|
self.ui.sceneView.height() / self.scene.sceneRect().height(),
|
||||||
self.ui.sceneView.scale(scale_factor, scale_factor)
|
)
|
||||||
|
self.ui.sceneView.scale(scale_factor, scale_factor)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
# Don't worry
|
||||||
|
pass
|
||||||
|
|
||||||
def get_xrandr_info(self):
|
def get_xrandr_info(self):
|
||||||
self.screen = xrandr.parse_data(xrandr.read_data())
|
_xrandr_data = xrandr.read_data()
|
||||||
|
self.screen = xrandr.parse_data(_xrandr_data)
|
||||||
self.screen.update_replica_of()
|
self.screen.update_replica_of()
|
||||||
|
self.reset_screen = xrandr.parse_data(_xrandr_data)
|
||||||
data = subprocess.check_output(["xrandr"]).decode("utf-8").splitlines()
|
|
||||||
name = None
|
|
||||||
for line in data:
|
|
||||||
if (
|
|
||||||
line and line[0] not in "S \t" and "disconnected" not in line
|
|
||||||
): # Output line
|
|
||||||
(
|
|
||||||
name,
|
|
||||||
primary,
|
|
||||||
res_x,
|
|
||||||
res_y,
|
|
||||||
w_in_mm,
|
|
||||||
h_in_mm,
|
|
||||||
pos_x,
|
|
||||||
pos_y,
|
|
||||||
enabled,
|
|
||||||
orientation,
|
|
||||||
) = parse_monitor(line)
|
|
||||||
self.xrandr_info[name] = dict(
|
|
||||||
primary=primary,
|
|
||||||
res_x=res_x,
|
|
||||||
res_y=res_y,
|
|
||||||
w_in_mm=w_in_mm,
|
|
||||||
h_in_mm=h_in_mm,
|
|
||||||
pos_x=pos_x,
|
|
||||||
pos_y=pos_y,
|
|
||||||
modes=[],
|
|
||||||
current_mode=None,
|
|
||||||
enabled=enabled,
|
|
||||||
replica_of=[],
|
|
||||||
orientation=orientation,
|
|
||||||
)
|
|
||||||
elif line[0] == " ": # A mode
|
|
||||||
mode_name = line.strip().split()[0]
|
|
||||||
self.xrandr_info[name]["modes"].append(mode_name)
|
|
||||||
if "*" in line:
|
|
||||||
print(f"Current mode for {name}: {mode_name}")
|
|
||||||
self.xrandr_info[name]["current_mode"] = mode_name
|
|
||||||
self.update_replica_of_data()
|
|
||||||
|
|
||||||
def update_replica_of_data(self):
|
|
||||||
for a in self.xrandr_info:
|
|
||||||
self.xrandr_info[a]["replica_of"] = []
|
|
||||||
for b in self.xrandr_info:
|
|
||||||
if a != b and is_replica_of(self.xrandr_info[a], self.xrandr_info[b]):
|
|
||||||
self.xrandr_info[a]["replica_of"].append(b)
|
|
||||||
|
|
||||||
def monitor_selected(self, name):
|
def monitor_selected(self, name):
|
||||||
if not name:
|
if not name:
|
||||||
@ -435,27 +341,24 @@ class Window(QObject):
|
|||||||
self.ui.modes.blockSignals(True)
|
self.ui.modes.blockSignals(True)
|
||||||
# Show modes
|
# Show modes
|
||||||
self.ui.modes.clear()
|
self.ui.modes.clear()
|
||||||
for mode in self.xrandr_info[name]["modes"]:
|
monitor = self.screen.monitors[name]
|
||||||
|
for mode in monitor.modes:
|
||||||
self.ui.modes.addItem(mode)
|
self.ui.modes.addItem(mode)
|
||||||
if (
|
|
||||||
self.xrandr_info[name]["current_mode"] is None
|
mode = monitor.get_current_mode()
|
||||||
): # Happens with turned off monitors
|
self.ui.modes.setCurrentText(mode.name)
|
||||||
self.xrandr_info[name]["enabled"] = False
|
if monitor.orientation in (0, 2):
|
||||||
h_scale = v_scale = 1
|
h_scale = monitor.res_x / mode.res_x
|
||||||
|
v_scale = monitor.res_y / mode.res_y
|
||||||
else:
|
else:
|
||||||
self.ui.modes.setCurrentText(self.xrandr_info[name]["current_mode"])
|
h_scale = monitor.res_y / mode.res_x
|
||||||
mod_x, mod_y = parse_mode(self.xrandr_info[name]["current_mode"])
|
v_scale = monitor.res_x / mode.res_y
|
||||||
if self.xrandr_info[name]["orientation"] in (0, 2):
|
|
||||||
h_scale = self.xrandr_info[name]["res_x"] / mod_x
|
|
||||||
v_scale = self.xrandr_info[name]["res_y"] / mod_y
|
|
||||||
else:
|
|
||||||
h_scale = self.xrandr_info[name]["res_y"] / mod_x
|
|
||||||
v_scale = self.xrandr_info[name]["res_x"] / mod_y
|
|
||||||
self.ui.horizontalScale.setValue(h_scale * 1000)
|
self.ui.horizontalScale.setValue(h_scale * 1000)
|
||||||
self.ui.verticalScale.setValue(v_scale * 1000)
|
self.ui.verticalScale.setValue(v_scale * 1000)
|
||||||
self.ui.primary.setChecked(self.xrandr_info[name]["primary"])
|
self.ui.primary.setChecked(monitor.primary)
|
||||||
self.ui.enabled.setChecked(self.xrandr_info[name]["enabled"])
|
self.ui.enabled.setChecked(monitor.enabled)
|
||||||
self.ui.orientationCombo.setCurrentIndex(self.xrandr_info[name]["orientation"])
|
self.ui.orientationCombo.setCurrentText(monitor.orientation)
|
||||||
|
|
||||||
self.ui.replicaOf.clear()
|
self.ui.replicaOf.clear()
|
||||||
self.ui.replicaOf.addItem("None")
|
self.ui.replicaOf.addItem("None")
|
||||||
@ -466,7 +369,7 @@ class Window(QObject):
|
|||||||
self.ui.replicaOf.setCurrentText(mon)
|
self.ui.replicaOf.setCurrentText(mon)
|
||||||
self.ui.modes.blockSignals(False)
|
self.ui.modes.blockSignals(False)
|
||||||
|
|
||||||
guessed_scale_mode = self.detect_scale_mode(self.xrandr_info[name])
|
guessed_scale_mode = monitor.guess_scale_mode()
|
||||||
self.ui.scaleModeCombo.setCurrentText(guessed_scale_mode)
|
self.ui.scaleModeCombo.setCurrentText(guessed_scale_mode)
|
||||||
self.scale_mode_changed()
|
self.scale_mode_changed()
|
||||||
|
|
||||||
@ -486,7 +389,6 @@ def main():
|
|||||||
|
|
||||||
loader = QUiLoader()
|
loader = QUiLoader()
|
||||||
Window(loader.load(ui_file))
|
Window(loader.load(ui_file))
|
||||||
|
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ class Monitor:
|
|||||||
enabled = False
|
enabled = False
|
||||||
primary = False
|
primary = False
|
||||||
orientation = "normal"
|
orientation = "normal"
|
||||||
|
item = None
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
"""Initialize a monitor object out of data from xrandr --verbose.
|
"""Initialize a monitor object out of data from xrandr --verbose.
|
||||||
|
@ -17,19 +17,19 @@ class MonitorItem(QGraphicsRectItem, QObject):
|
|||||||
self.bottom_edge.setBrush(QBrush("red", Qt.SolidPattern))
|
self.bottom_edge.setBrush(QBrush("red", Qt.SolidPattern))
|
||||||
self.update_visuals(data)
|
self.update_visuals(data)
|
||||||
|
|
||||||
def update_visuals(self, data):
|
def update_visuals(self, monitor):
|
||||||
self.setRect(0, 0, data["res_x"], data["res_y"])
|
self.setRect(0, 0, monitor.res_x, monitor.res_y)
|
||||||
self.setPos(data["pos_x"], data["pos_y"])
|
self.setPos(monitor.pos_x, monitor.pos_y)
|
||||||
if data["orientation"] == 0:
|
if monitor.orientation == "normal":
|
||||||
self.bottom_edge.setRect(0, data["res_y"] - 50, data["res_x"], 50)
|
self.bottom_edge.setRect(0, monitor.res_y - 50, monitor.res_x, 50)
|
||||||
elif data["orientation"] == 1:
|
elif monitor.orientation == 1:
|
||||||
self.bottom_edge.setRect(data["res_x"] - 50, 0, 50, data["res_y"])
|
self.bottom_edge.setRect(monitor.res_x - 50, 0, 50, monitor.res_y)
|
||||||
elif data["orientation"] == 2:
|
elif monitor.orientation == 2:
|
||||||
self.bottom_edge.setRect(0, 0, data["res_x"], 50)
|
self.bottom_edge.setRect(0, 0, monitor.res_x, 50)
|
||||||
elif data["orientation"] == 3:
|
elif monitor.orientation == 3:
|
||||||
self.bottom_edge.setRect(0, 0, 50, data["res_y"])
|
self.bottom_edge.setRect(0, 0, 50, monitor.res_y)
|
||||||
if data["replica_of"]:
|
if monitor.replica_of:
|
||||||
label_text = f"{self.name} [{','.join(data['replica_of'])}]"
|
label_text = f"{self.name} [{','.join(monitor.replica_of)}]"
|
||||||
else:
|
else:
|
||||||
label_text = self.name
|
label_text = self.name
|
||||||
self.label.setPlainText(label_text)
|
self.label.setPlainText(label_text)
|
||||||
@ -38,8 +38,8 @@ class MonitorItem(QGraphicsRectItem, QObject):
|
|||||||
self.rect().height() / self.label.boundingRect().height(),
|
self.rect().height() / self.label.boundingRect().height(),
|
||||||
)
|
)
|
||||||
self.label.setScale(label_scale)
|
self.label.setScale(label_scale)
|
||||||
if data["enabled"]:
|
if monitor.enabled:
|
||||||
if data["primary"]:
|
if monitor.primary:
|
||||||
color = QColor("#eee8d5")
|
color = QColor("#eee8d5")
|
||||||
color.setAlpha(200)
|
color.setAlpha(200)
|
||||||
self.setBrush(QBrush(color, Qt.SolidPattern))
|
self.setBrush(QBrush(color, Qt.SolidPattern))
|
||||||
@ -50,16 +50,13 @@ class MonitorItem(QGraphicsRectItem, QObject):
|
|||||||
self.setBrush(QBrush(color, Qt.SolidPattern))
|
self.setBrush(QBrush(color, Qt.SolidPattern))
|
||||||
self.setZValue(self.z)
|
self.setZValue(self.z)
|
||||||
self.z -= 1
|
self.z -= 1
|
||||||
|
self.show()
|
||||||
else:
|
else:
|
||||||
color = QColor("#f1f1f1")
|
color = QColor("#f1f1f1")
|
||||||
color.setAlpha(200)
|
color.setAlpha(200)
|
||||||
self.setBrush(QBrush(color, Qt.SolidPattern))
|
self.setBrush(QBrush(color, Qt.SolidPattern))
|
||||||
self.setZValue(-1000)
|
self.setZValue(-1000)
|
||||||
|
|
||||||
if not data["current_mode"]: # Disconnected or disabled
|
|
||||||
self.hide()
|
self.hide()
|
||||||
else:
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
self.window.pos_label.show()
|
self.window.pos_label.show()
|
||||||
|
Loading…
Reference in New Issue
Block a user