summaryrefslogtreecommitdiff
path: root/display-manager.py
blob: 921310789ac1a8745c2942eaeb1555c547f8cf9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env python3
import subprocess, re

def xrandr(args):
    return subprocess.check_output(["xrandr"] + args).decode("utf-8")

def zenity_list(text, options, height):
    args = ["zenity", "--list", "--hide-header", "--text", text, "--height", str(height), "--column", ""] + list(options)
    res = subprocess.run(args, stdout = subprocess.PIPE)
    if res.returncode == 0:
        output = res.stdout.decode("utf-8")
        if output[-1] == "\n": output = output[:-1]
        return output
    else:
        return None

def current_setup():
    displays = dict()
    walk_display = None

    for line in xrandr(["-q"]).split("\n"):
        if len(line) == 0: continue
        indent = len(re.match(r" *", line)[0])

        if indent == 0:
            if re.match(r"Screen [0-9]+:", line) is not None:
                continue
            else:
                walk_display = line[:line.index(" ")]
                displays[walk_display] = {
                    "current": None,
                    "preferred": None
                }
        elif indent == 2:
            continue
        elif indent == 3:
            words = re.split(r" {2,}", line.strip())
            if len(words) == 0:
                continue

            m = re.match(r"([0-9]+)x([0-9]+)$", words[0])
            resolution = (int(m[1]), int(m[2]))
            freq_current = None  # *
            freq_preferred = None  # +
            for word in words[1:]:
                m = re.match(r"([0-9.]+)([\* ]?)([\+ ]?)$", word)
                if m[2] == "*": freq_current = float(m[1])
                if m[3] == "+": freq_preferred = float(m[1])

            if freq_current is not None:
                displays[walk_display]["current"] = (resolution, freq_current)
            if freq_preferred is not None:
                displays[walk_display]["preferred"] = (resolution, freq_preferred)

    return displays

def iter_find(l, predicate):
    for x in l:
        if predicate(x): return x
    return None

def is_internal_display(name):
    return name.startswith("eDP")

def is_external_display(name):
    return name.startswith("HDMI")

def main():
    setup = current_setup()

    def props_for_name(name):
        nonlocal setup
        current = False if name is None else setup[name]["current"] is not None
        formatted = "<none>" if name is None else name + (" (enabled)" if current else "")
        return {
            "name": name,
            "formatted": formatted,
            "current": current
        }

    internal = props_for_name(iter_find(setup.keys(), is_internal_display))
    external = props_for_name(iter_find(setup.keys(), is_external_display))
    assert internal["name"] is not None
    assert internal["name"] != external["name"]

    other_displays = [setup[name]["formatted"] for name in setup.keys()
                      if name not in [internal["name"], external["name"]]]

    message = "{} displays connected:\n- internal: {}\n- external: {}".format(
                    len(setup.keys()), internal["formatted"], external["formatted"])
    if len(other_displays) > 0:
        message += "\n- unknown: {}".format(other_displays.join(", "))

    options = {
        "Only internal": ["--output", external["name"], "--off"],
        "External left": ["--output", external["name"], "--auto", "--left-of", internal["name"]],
        "External right": ["--output", external["name"], "--auto", "--right-of", internal["name"]],
        "Mirror": ["--output", external["name"], "--auto", "--same-as", internal["name"]]
    }

    response = zenity_list(message, options.keys(), 225)
    if response is not None:
        flags = options[response]
        xrandr(flags)

if __name__ == "__main__":
    main()