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
|
package main
import (
"fmt"
"strconv"
)
// The laptop's built-in panel. xrandr calls it `eDP` on this machine (older
// kernels and other laptops use `eDP1` or `eDP-1`), and both layouts are
// defined relative to it.
const internalOutput = "eDP"
// TwoMonitorArgs computes the xrandr arguments for the desk setup: the external
// monitor is primary and sits to the right of the laptop panel, and the two are
// aligned along their bottom edge.
//
// The bottom alignment is why the external monitor's height has to be at least
// the laptop's: the laptop is pushed down by the difference, and a negative
// offset would push it off the top of the screen.
func TwoMonitorArgs(outputs []Output) ([]string, error) {
connected := Connected(outputs)
internal, ok := Find(connected, internalOutput)
if !ok {
return nil, fmt.Errorf("could not find %s (laptop screen)", internalOutput)
}
if len(connected) != 2 {
return nil, fmt.Errorf(
"only know how to configure two monitors, but %d connected", len(connected))
}
// The laptop panel has to be switched on: if it has no active mode, xrandr
// printed no `*`, and repositioning it would be meaningless.
if internal.Current == nil {
return nil, fmt.Errorf("%s is connected but has no active mode", internalOutput)
}
if internal.Native == nil {
return nil, fmt.Errorf("%s has no native resolution", internalOutput)
}
var external Output
for _, o := range connected {
if o.Name != internalOutput {
external = o
}
}
if external.Native == nil {
return nil, fmt.Errorf("%s has no native resolution", external.Name)
}
internalHeight, err := strconv.Atoi(internal.Native.Height)
if err != nil {
return nil, fmt.Errorf("%s: cannot read native height %q: %w",
internalOutput, internal.Native.Height, err)
}
externalHeight, err := strconv.Atoi(external.Native.Height)
if err != nil {
return nil, fmt.Errorf("%s: cannot read native height %q: %w",
external.Name, external.Native.Height, err)
}
heightOffset := externalHeight - internalHeight
if heightOffset < 0 {
return nil, fmt.Errorf(
"%s (%dpx) is shorter than %s (%dpx); bottom-aligning would push the laptop screen off the top",
external.Name, externalHeight, internalOutput, internalHeight)
}
return []string{
"--verbose",
"--output", external.Name, "--primary",
"--mode", external.Native.Width + "x" + external.Native.Height,
"--pos", internal.Native.Width + "x0",
"--output", internalOutput,
"--mode", internal.Native.Width + "x" + internal.Native.Height,
"--pos", "0x" + strconv.Itoa(heightOffset),
}, nil
}
// LaptopOnlyArgs computes the xrandr arguments for using just the built-in
// panel, at its native resolution.
func LaptopOnlyArgs(outputs []Output) ([]string, error) {
connected := Connected(outputs)
internal, ok := Find(connected, internalOutput)
if !ok {
return nil, fmt.Errorf("could not find %s (laptop screen)", internalOutput)
}
if internal.Current == nil {
return nil, fmt.Errorf("%s is connected but has no active mode", internalOutput)
}
if internal.Native == nil {
return nil, fmt.Errorf("%s has no native resolution", internalOutput)
}
return []string{
"--verbose",
"--output", internalOutput, "--primary",
"--mode", internal.Native.Width + "x" + internal.Native.Height,
}, nil
}
|