v0.2.0 13c6a90

props.go RawHistoryFolder

1package drm
2
3import (
4	"runtime"
5	"unsafe"
6
7	"golang.org/x/sys/unix"
8)
9
10// KMS object properties, read by name; atomic commits set them.
11
12const (
13	ioctlObjGetProps = 0xC02064B9 // DRM_IOWR('d', 0xB9, struct drm_mode_obj_get_properties)
14	ioctlGetProp     = 0xC04064AA // DRM_IOWR('d', 0xAA, struct drm_mode_get_property)
15
16	objCrtc      = 0xcccccccc
17	objConnector = 0xc0c0c0c0
18)
19
20type objGetProps struct {
21	props, values uint64
22	count, objID  uint32
23	objType, _    uint32
24}
25
26type propEnum struct {
27	value uint64
28	name  [32]byte
29}
30
31type getProp struct {
32	values, enumBlobs    uint64
33	propID, flags        uint32
34	name                 [32]byte
35	countValues, countEn uint32
36}
37
38// objProps returns an object's property IDs and values by name.
39func objProps(fd int, obj, typ uint32) (map[string][2]uint64, error) {
40	q := objGetProps{objID: obj, objType: typ}
41	if err := ioctl(fd, ioctlObjGetProps, unsafe.Pointer(&q)); err != nil {
42		return nil, err
43	}
44	out := map[string][2]uint64{}
45	if q.count == 0 {
46		return out, nil
47	}
48	ids := make([]uint32, q.count)
49	vals := make([]uint64, q.count)
50	q.props, q.values = uint64(uintptr(unsafe.Pointer(&ids[0]))), uint64(uintptr(unsafe.Pointer(&vals[0])))
51	if err := ioctl(fd, ioctlObjGetProps, unsafe.Pointer(&q)); err != nil {
52		return nil, err
53	}
54	for i := range min(int(q.count), len(ids)) {
55		p := getProp{propID: ids[i]}
56		if ioctl(fd, ioctlGetProp, unsafe.Pointer(&p)) != nil {
57			continue
58		}
59		out[unix.ByteSliceToString(p.name[:])] = [2]uint64{uint64(ids[i]), vals[i]}
60	}
61	return out, nil
62}
63
64// readContentTypeProp records the connector's content type enum values.
65func readContentTypeProp(fd int, props map[string][2]uint64) (uint32, [5]uint64) {
66	id := uint32(props["content type"][0])
67	var values [5]uint64
68	if id == 0 {
69		return 0, values
70	}
71	p := getProp{propID: id}
72	if ioctl(fd, ioctlGetProp, unsafe.Pointer(&p)) != nil || p.countEn == 0 {
73		return 0, values
74	}
75	enums := make([]propEnum, p.countEn)
76	v := make([]uint64, max(p.countValues, 1))
77	p.enumBlobs = uint64(uintptr(unsafe.Pointer(&enums[0])))
78	p.values = uint64(uintptr(unsafe.Pointer(&v[0])))
79	if ioctl(fd, ioctlGetProp, unsafe.Pointer(&p)) != nil {
80		return 0, values
81	}
82	for _, e := range enums[:min(int(p.countEn), len(enums))] {
83		switch unix.ByteSliceToString(e.name[:]) {
84		case "No Data":
85			values[0] = e.value
86		case "Graphics":
87			values[1] = e.value
88		case "Photo":
89			values[2] = e.value
90		case "Cinema":
91			values[3] = e.value
92		case "Game":
93			values[4] = e.value
94		}
95	}
96	runtime.KeepAlive(enums)
97	runtime.KeepAlive(v)
98	return id, values
99}
100
101// connectorHDRProps retains connector property IDs, enum values and the
102// original max bpc for SDR modesets and restoration.
103type connectorHDRProps struct {
104	Metadata, Colorspace, MaxBPC uint32
105	BT2020Value                  uint64
106	DefaultValue                 uint64 // connector Colorspace "Default" enum
107	HasDefault                   bool
108	MaxBPCValue                  uint64 // current value for eventual restoration
109}
110
111// readConnectorHDRProps checks property metadata, not only current values.
112func readConnectorHDRProps(fd int, props map[string][2]uint64) connectorHDRProps {
113	var out connectorHDRProps
114	out.Metadata = uint32(props["HDR_OUTPUT_METADATA"][0])
115	if id := uint32(props["Colorspace"][0]); id != 0 {
116		p := getProp{propID: id}
117		if ioctl(fd, ioctlGetProp, unsafe.Pointer(&p)) == nil && p.countEn > 0 {
118			enums := make([]propEnum, p.countEn)
119			p.enumBlobs = uint64(uintptr(unsafe.Pointer(&enums[0])))
120			// The kernel also copies an enum's values when countValues is
121			// set: a nil values pointer makes the whole call fail (EFAULT).
122			values := make([]uint64, max(p.countValues, 1))
123			p.values = uint64(uintptr(unsafe.Pointer(&values[0])))
124			if ioctl(fd, ioctlGetProp, unsafe.Pointer(&p)) == nil {
125				out.Colorspace, out.BT2020Value = bt2020Enum(id, enums[:min(int(p.countEn), len(enums))])
126				for _, e := range enums[:min(int(p.countEn), len(enums))] {
127					if unix.ByteSliceToString(e.name[:]) == "Default" {
128						out.DefaultValue, out.HasDefault = e.value, true
129					}
130				}
131			}
132			runtime.KeepAlive(enums)
133			runtime.KeepAlive(values)
134		}
135	}
136	if id := uint32(props["max bpc"][0]); id != 0 {
137		p := getProp{propID: id}
138		if ioctl(fd, ioctlGetProp, unsafe.Pointer(&p)) == nil && p.countValues >= 2 {
139			values := make([]uint64, p.countValues)
140			p.values = uint64(uintptr(unsafe.Pointer(&values[0])))
141			if ioctl(fd, ioctlGetProp, unsafe.Pointer(&p)) == nil && p.countValues >= 2 && bpcAllows10(values) {
142				out.MaxBPC, out.MaxBPCValue = id, props["max bpc"][1]
143			}
144			runtime.KeepAlive(values)
145		}
146	}
147	return out
148}
149
150func bt2020Enum(id uint32, enums []propEnum) (uint32, uint64) {
151	for _, e := range enums {
152		if unix.ByteSliceToString(e.name[:]) == "BT2020_RGB" {
153			return id, e.value
154		}
155	}
156	return 0, 0
157}
158
159func bpcAllows10(values []uint64) bool {
160	return len(values) >= 2 && values[0] <= 10 && values[1] >= 10
161}
162
163// hdrCapability combines EDID and connector support, retaining the first reason.
164type hdrCapability struct {
165	Capable bool
166	Reason  string
167	HDRMetadata
168}
169
170func detectHDR(m Monitor, p connectorHDRProps) hdrCapability {
171	c := hdrCapability{HDRMetadata: m.HDR}
172	switch {
173	case !m.HDR.PQ:
174		c.Reason = "edid: no PQ"
175	case !m.HDR.StaticType1:
176		c.Reason = "edid: no static metadata type 1"
177	case !m.HDR.BT2020RGB:
178		c.Reason = "edid: no BT2020RGB"
179	case p.Metadata == 0:
180		c.Reason = "connector: no HDR_OUTPUT_METADATA"
181	case p.Colorspace == 0:
182		c.Reason = "connector: no BT2020_RGB Colorspace"
183	case p.MaxBPC == 0:
184		c.Reason = "connector: max bpc below 10"
185	case !p.HasDefault:
186		c.Reason = "connector: no Default Colorspace"
187	default:
188		c.Capable = true
189	}
190	return c
191}
192
193// vrrProperty returns the CRTC's VRR_ENABLED property when the connector
194// is VRR capable, else 0.
195func vrrProperty(k kms, conn, crtc uint32) uint32 {
196	cp, err := k.objProps(conn, objConnector)
197	if err != nil || cp["vrr_capable"][1] != 1 {
198		return 0
199	}
200	rp, err := k.objProps(crtc, objCrtc)
201	if err != nil {
202		return 0
203	}
204	return uint32(rp["VRR_ENABLED"][0])
205}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close