-
Notifications
You must be signed in to change notification settings - Fork 17
/
monconf.go
118 lines (106 loc) · 3.5 KB
/
monconf.go
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
108
109
110
111
112
113
114
115
116
117
118
package wallutils
// Functions and structs for dealing with ~/.config/monitors.xml, which is used by GNOME, Cinnamon and MATE
import (
"encoding/xml"
"fmt"
"image"
"os"
"strconv"
"github.com/xyproto/env/v2"
)
// MonitorConfiguration is mainly a collection of MConfiguration + a Version field
type MonitorConfiguration struct {
XMLName xml.Name `xml:"monitors"`
Version string `xml:"version,attr"`
Configurations []MConfiguration `xml:"configuration"`
}
// MConfiguration is mainly a collection of MOutput + a Clone field
type MConfiguration struct {
XMLName xml.Name `xml:"configuration"`
Clone string `xml:"clone,omitempty"`
Outputs []MOutput `xml:"output"`
}
// MOutput represents a monitor configuration, including:
// width, height, rotation and if the monitor is the primary monitor.
type MOutput struct {
XMLName xml.Name `xml:"output"`
Name string `xml:"name,attr"`
Vendor string `xml:"vendor,omitempty"`
Product string `xml:"product,omitempty"`
Serial string `xml:"serial,omitempty"`
Width string `xml:"width,omitempty"`
Height string `xml:"height,omitempty"`
Rate string `xml:"rate,omitempty"`
X string `xml:"x,omitempty"`
Y string `xml:"y,omitempty"`
Rotation string `xml:"rotation,omitempty"`
ReflectX string `xml:"reflect_x,omitempty"`
ReflectY string `xml:"reflect_y,omitempty"`
Primary string `xml:"primary,omitempty"`
}
// ParseMonitorFile can parse monitor XML files,
// like the one that typically exists in ~/.config/monitors.xml
func ParseMonitorFile(filename string) (*MonitorConfiguration, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
var monitors MonitorConfiguration
if err = xml.Unmarshal(data, &monitors); err != nil {
return nil, fmt.Errorf("could not parse %s as XML: error: %s", filename, err)
}
return &monitors, nil
}
// NewMonitorConfiguration returns a new MonitorConfiguration struct,
// filled with the contents of ~/.config/monitors.xml.
func NewMonitorConfiguration() (*MonitorConfiguration, error) {
return ParseMonitorFile(env.ExpandUser("~/.config/monitors.xml"))
}
// Overlapping can check if configurations in monitors.xml have overlapping areas.
func (mc *MonitorConfiguration) Overlapping() bool {
mc, err := NewMonitorConfiguration()
if err != nil {
return false
}
// Run a check per <configuration> section in the XML file
for _, conf := range mc.Configurations {
rects := make([]image.Rectangle, 0)
for _, output := range conf.Outputs {
if output.X != "" && output.Y != "" && output.Width != "" && output.Height != "" {
x, err := strconv.Atoi(output.X)
if err != nil {
continue
}
y, err := strconv.Atoi(output.Y)
if err != nil {
continue
}
width, err := strconv.Atoi(output.Width)
if err != nil {
continue
}
height, err := strconv.Atoi(output.Height)
if err != nil {
continue
}
r := NewRect(uint(x), uint(y), uint(width), uint(height))
rects = append(rects, r)
}
}
if Overlaps(rects) {
return true
}
}
return false
}
// MonConfOverlap is a convenience function for checking if the
// x,y,w,h monitor configurations in ie. ~/.config/monitors.xml are
// overlapping or not. If monitors.xml can not be parsed or read,
// false is returned.
func MonConfOverlap(filename string) bool {
// a leading ~ will be expanded to the home directory
if mc, err := ParseMonitorFile(env.ExpandUser(filename)); err != nil {
return mc.Overlapping()
}
return false
}