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
}