detached d08e097

#1 feat(xtgen): generator for xcb-proto bindings

state
merged · d08e097 · fast-forward
branch
feat/xtgen → main
opened
bnema ·
updated
CommitSubjectAuthorDateSignatureStatus
d08e097feat(xtgen): decode replies as zero-copy views with list iteratorsbriceGPG Verified
017e663feat(xtgen): encode typed mask-ordered request value listsbriceGPG Verified
582be88feat(xtgen): encode request lists and derive exclusive lengthsbriceGPG Verified
8e6fe5cfeat(xtgen): encode fixed requests with captured wire goldensbriceGPG Verified
85a51ddfeat(xtgen): resolve wire types and emit typed layoutsbriceGPG Verified
2c9c8eafeat(xtgen): parse pinned XML into a strict source-located ASTbriceGPG Verified

Checks 4

102 files · +16959 -1
4/4
A bigreq/replies_fuzz_test.go +21 -0
A bigreq/replies_generated.go +21 -0
A bigreq/requests_generated.go +21 -0
A bigreq/sizes_test.go +11 -0
A bigreq/types_generated.go +2 -0
A buf.go +92 -0
A buf_test.go +69 -0
A cmd/xtgen/emit.go +252 -0
A cmd/xtgen/expr.go +43 -0
A cmd/xtgen/ir.go +39 -0
A cmd/xtgen/layout.go +103 -0
M cmd/xtgen/main.go +47 -1
A cmd/xtgen/naming.go +43 -0
A cmd/xtgen/parse.go +292 -0
A cmd/xtgen/parse_test.go +74 -0
A cmd/xtgen/replies.go +554 -0
A cmd/xtgen/replies_test.go +70 -0
A cmd/xtgen/requests.go +359 -0
A cmd/xtgen/resolve.go +356 -0
A cmd/xtgen/resolve_test.go +165 -0
A cmd/xtgen/templates/ext.tmpl +10 -0
A cmd/xtgen/templates/replies.tmpl +5 -0
A cmd/xtgen/templates/requests.tmpl +5 -0
A cmd/xtgen/templates/sizes.tmpl +7 -0
A cmd/xtgen/templates/types.tmpl +5 -0
A cmd/xtgen/values.go +136 -0
@@ -0,0 +1,136 @@
1+package main
2+
3+import (
4+ "fmt"
5+ "sort"
6+ "strings"
7+)
8+
9+type ValueField struct {
10+ Field Field
11+ Bit string
12+ Value int64
13+}
14+type ValueList struct {
15+ Mask Field
16+ Fields []ValueField
17+}
18+
19+func resolveValues(m *Module, req *Node, name string) (*ValueList, error) {
20+ var sw *Node
21+ for _, n := range req.Children {
22+ if n.Kind == "switch" {
23+ if sw != nil {
24+ return nil, n.fail("multiple value switches are unsupported")
25+ }
26+ sw = n
27+ }
28+ }
29+ if sw == nil {
30+ return nil, nil
31+ }
32+ if len(sw.Children) < 2 || sw.Children[0].Kind != "fieldref" {
33+ return nil, sw.fail("value switch requires a mask fieldref and bitcases")
34+ }
35+ var mask *Node
36+ seenSwitch := false
37+ for _, n := range req.Children {
38+ if n == sw {
39+ seenSwitch = true
40+ continue
41+ }
42+ if seenSwitch && n.Kind != "doc" && n.Kind != "reply" {
43+ return nil, n.fail("value switch must be trailing")
44+ }
45+ if n.Kind == "list" {
46+ return nil, n.fail("lists combined with value switches are unsupported")
47+ }
48+ if !seenSwitch && n.Kind == "field" && n.Attr["name"] == sw.Children[0].Text {
49+ mask = n
50+ }
51+ }
52+ if mask == nil || mask.Attr["mask"] == "" {
53+ return nil, sw.fail("value switch requires a preceding typed mask")
54+ }
55+ mt, err := lookup(m, mask.Attr["type"])
56+ if err != nil {
57+ return nil, mask.fail("%v", err)
58+ }
59+ e, owner := findEnum(m, mask.Attr["mask"])
60+ if e == nil {
61+ return nil, mask.fail("unknown mask %s", mask.Attr["mask"])
62+ }
63+ if mt.Size > 4 || !isScalar(mt) {
64+ return nil, mask.fail("value mask must fit in 32 bits")
65+ }
66+ v := &ValueList{Mask: Field{Node: mask, GoType: qualified(m, owner, e.Name)}}
67+ used := map[int64]bool{}
68+ names := map[string]bool{"Mask": true}
69+ for _, bc := range sw.Children[1:] {
70+ var members []*Node
71+ for _, c := range bc.Children {
72+ if c.Kind != "doc" {
73+ members = append(members, c)
74+ }
75+ }
76+ if bc.Kind != "bitcase" || len(members) != 2 || members[0].Kind != "enumref" || members[1].Kind != "field" {
77+ return nil, bc.fail("value switch requires one enumref and one 4-byte field per bitcase")
78+ }
79+ bit, n := members[0], members[1]
80+ be, bo := findEnum(m, bit.Attr["ref"])
81+ if be != e || bo != owner {
82+ return nil, bit.fail("bitcase enum must match value mask")
83+ }
84+ var value int64
85+ found := false
86+ for _, item := range e.Items {
87+ if item.Name == e.Name+goName(bit.Text) {
88+ value = item.Value
89+ found = true
90+ }
91+ }
92+ if !found || value <= 0 || value&(value-1) != 0 || uint64(value) >= uint64(1)<<(8*mt.Size) || used[value] {
93+ return nil, bit.fail("bitcase requires a unique mask bit")
94+ }
95+ used[value] = true
96+ typ, err := lookup(m, n.Attr["type"])
97+ if err != nil {
98+ return nil, n.fail("%v", err)
99+ }
100+ if typ.Size != 4 || !isScalar(typ) {
101+ return nil, n.fail("value-list bitcase requires a 4-byte scalar field")
102+ }
103+ f := Field{Name: goName(n.Attr["name"]), GoType: qualified(m, typ.Module, typ.GoType), Type: typ, Node: n}
104+ for _, attr := range []string{"enum", "mask"} {
105+ if ref := n.Attr[attr]; ref != "" {
106+ fe, fo := findEnum(m, ref)
107+ if fe == nil {
108+ return nil, n.fail("unknown enum %s", ref)
109+ }
110+ f.GoType = qualified(m, fo, fe.Name)
111+ }
112+ }
113+ if names[f.Name] {
114+ return nil, n.fail("value field name collision %s", f.Name)
115+ }
116+ names[f.Name] = true
117+ v.Fields = append(v.Fields, ValueField{Field: f, Bit: qualified(m, owner, e.Name+goName(bit.Text)), Value: value})
118+ }
119+ sort.Slice(v.Fields, func(i, j int) bool { return v.Fields[i].Value < v.Fields[j].Value })
120+ if err := m.claim(name+"Values", sw); err != nil {
121+ return nil, err
122+ }
123+ return v, nil
124+}
125+func valuesDecl(name string, v *ValueList) string {
126+ var b strings.Builder
127+ fmt.Fprintf(&b, "type %sValues struct { Mask %s;", name, v.Mask.GoType)
128+ for _, f := range v.Fields {
129+ fmt.Fprintf(&b, "%s %s;", f.Field.Name, f.Field.GoType)
130+ }
131+ b.WriteString("};\n")
132+ for _, f := range v.Fields {
133+ fmt.Fprintf(&b, "func(v *%sValues) Set%s(x %s) *%sValues { v.Mask |= %s; v.%s=x; return v };\n", name, f.Field.Name, f.Field.GoType, name, f.Bit, f.Field.Name)
134+ }
135+ return b.String()
136+}
@@ -0,0 +1,136 @@
1+package main
2+
3+import (
4+ "fmt"
5+ "sort"
6+ "strings"
7+)
8+
9+type ValueField struct {
10+ Field Field
11+ Bit string
12+ Value int64
13+}
14+type ValueList struct {
15+ Mask Field
16+ Fields []ValueField
17+}
18+
19+func resolveValues(m *Module, req *Node, name string) (*ValueList, error) {
20+ var sw *Node
21+ for _, n := range req.Children {
22+ if n.Kind == "switch" {
23+ if sw != nil {
24+ return nil, n.fail("multiple value switches are unsupported")
25+ }
26+ sw = n
27+ }
28+ }
29+ if sw == nil {
30+ return nil, nil
31+ }
32+ if len(sw.Children) < 2 || sw.Children[0].Kind != "fieldref" {
33+ return nil, sw.fail("value switch requires a mask fieldref and bitcases")
34+ }
35+ var mask *Node
36+ seenSwitch := false
37+ for _, n := range req.Children {
38+ if n == sw {
39+ seenSwitch = true
40+ continue
41+ }
42+ if seenSwitch && n.Kind != "doc" && n.Kind != "reply" {
43+ return nil, n.fail("value switch must be trailing")
44+ }
45+ if n.Kind == "list" {
46+ return nil, n.fail("lists combined with value switches are unsupported")
47+ }
48+ if !seenSwitch && n.Kind == "field" && n.Attr["name"] == sw.Children[0].Text {
49+ mask = n
50+ }
51+ }
52+ if mask == nil || mask.Attr["mask"] == "" {
53+ return nil, sw.fail("value switch requires a preceding typed mask")
54+ }
55+ mt, err := lookup(m, mask.Attr["type"])
56+ if err != nil {
57+ return nil, mask.fail("%v", err)
58+ }
59+ e, owner := findEnum(m, mask.Attr["mask"])
60+ if e == nil {
61+ return nil, mask.fail("unknown mask %s", mask.Attr["mask"])
62+ }
63+ if mt.Size > 4 || !isScalar(mt) {
64+ return nil, mask.fail("value mask must fit in 32 bits")
65+ }
66+ v := &ValueList{Mask: Field{Node: mask, GoType: qualified(m, owner, e.Name)}}
67+ used := map[int64]bool{}
68+ names := map[string]bool{"Mask": true}
69+ for _, bc := range sw.Children[1:] {
70+ var members []*Node
71+ for _, c := range bc.Children {
72+ if c.Kind != "doc" {
73+ members = append(members, c)
74+ }
75+ }
76+ if bc.Kind != "bitcase" || len(members) != 2 || members[0].Kind != "enumref" || members[1].Kind != "field" {
77+ return nil, bc.fail("value switch requires one enumref and one 4-byte field per bitcase")
78+ }
79+ bit, n := members[0], members[1]
80+ be, bo := findEnum(m, bit.Attr["ref"])
81+ if be != e || bo != owner {
82+ return nil, bit.fail("bitcase enum must match value mask")
83+ }
84+ var value int64
85+ found := false
86+ for _, item := range e.Items {
87+ if item.Name == e.Name+goName(bit.Text) {
88+ value = item.Value
89+ found = true
90+ }
91+ }
92+ if !found || value <= 0 || value&(value-1) != 0 || uint64(value) >= uint64(1)<<(8*mt.Size) || used[value] {
93+ return nil, bit.fail("bitcase requires a unique mask bit")
94+ }
95+ used[value] = true
96+ typ, err := lookup(m, n.Attr["type"])
97+ if err != nil {
98+ return nil, n.fail("%v", err)
99+ }
100+ if typ.Size != 4 || !isScalar(typ) {
101+ return nil, n.fail("value-list bitcase requires a 4-byte scalar field")
102+ }
103+ f := Field{Name: goName(n.Attr["name"]), GoType: qualified(m, typ.Module, typ.GoType), Type: typ, Node: n}
104+ for _, attr := range []string{"enum", "mask"} {
105+ if ref := n.Attr[attr]; ref != "" {
106+ fe, fo := findEnum(m, ref)
107+ if fe == nil {
108+ return nil, n.fail("unknown enum %s", ref)
109+ }
110+ f.GoType = qualified(m, fo, fe.Name)
111+ }
112+ }
113+ if names[f.Name] {
114+ return nil, n.fail("value field name collision %s", f.Name)
115+ }
116+ names[f.Name] = true
117+ v.Fields = append(v.Fields, ValueField{Field: f, Bit: qualified(m, owner, e.Name+goName(bit.Text)), Value: value})
118+ }
119+ sort.Slice(v.Fields, func(i, j int) bool { return v.Fields[i].Value < v.Fields[j].Value })
120+ if err := m.claim(name+"Values", sw); err != nil {
121+ return nil, err
122+ }
123+ return v, nil
124+}
125+func valuesDecl(name string, v *ValueList) string {
126+ var b strings.Builder
127+ fmt.Fprintf(&b, "type %sValues struct { Mask %s;", name, v.Mask.GoType)
128+ for _, f := range v.Fields {
129+ fmt.Fprintf(&b, "%s %s;", f.Field.Name, f.Field.GoType)
130+ }
131+ b.WriteString("};\n")
132+ for _, f := range v.Fields {
133+ fmt.Fprintf(&b, "func(v *%sValues) Set%s(x %s) *%sValues { v.Mask |= %s; v.%s=x; return v };\n", name, f.Field.Name, f.Field.GoType, name, f.Bit, f.Field.Name)
134+ }
135+ return b.String()
136+}
A cmd/xtgen/values_test.go +93 -0
@@ -0,0 +1,93 @@
1+package main
2+
3+import (
4+ "strings"
5+ "testing"
6+)
7+
8+func TestResolvePinnedValues(t *testing.T) {
9+ found := map[string]bool{}
10+ for _, m := range testModules(t) {
11+ rs, err := resolveRequests(m)
12+ if err != nil {
13+ t.Fatal(err)
14+ }
15+ for _, r := range rs {
16+ if r.Values != nil {
17+ found[m.Pkg+"/"+r.Name] = true
18+ if len(r.Values.Fields) == 0 {
19+ t.Fatal("empty values")
20+ }
21+ for i, f := range r.Values.Fields {
22+ if i > 0 && r.Values.Fields[i-1].Value >= f.Value {
23+ t.Fatal("not in bit order")
24+ }
25+ }
26+ for _, p := range r.Params {
27+ if p.Param == "valueMask" {
28+ t.Fatal("mask leaked into params")
29+ }
30+ }
31+ }
32+ }
33+ }
34+ for _, name := range []string{"xproto/CreateWindow", "xproto/ChangeWindowAttributes", "xproto/ConfigureWindow", "xproto/CreateGC", "xproto/ChangeGC", "xproto/ChangeKeyboardControl", "render/CreatePicture", "render/ChangePicture"} {
35+ if !found[name] {
36+ t.Error("missing", name)
37+ }
38+ }
39+ if len(found) != 8 {
40+ t.Fatal(found)
41+ }
42+}
43+
44+func TestResolveValueSwitches(t *testing.T) {
45+ prefix := `<xcb header="test"><enum name="Mask"><item name="Low"><bit>0</bit></item><item name="High"><bit>3</bit></item></enum><request name="Configure" opcode="1"><pad bytes="1"/><field name="value_mask" type="CARD32" mask="Mask"/>`
46+ high := `<bitcase><enumref ref="Mask">High</enumref><field name="high" type="BOOL32"/></bitcase>`
47+ low := `<bitcase><enumref ref="Mask">Low</enumref><field name="low" type="INT32"/></bitcase>`
48+ good := `<switch name="value_list"><fieldref>value_mask</fieldref>` + high + low + `</switch>`
49+ for _, tc := range []struct {
50+ name, body string
51+ valid bool
52+ }{
53+ {"sorted", good, true},
54+ {"expression", strings.Replace(good, "<fieldref>value_mask</fieldref>", "<popcount><fieldref>value_mask</fieldref></popcount>", 1), false},
55+ {"duplicate", strings.Replace(good, low, high, 1), false},
56+ {"unknown bit", strings.Replace(good, ">Low<", ">Missing<", 1), false},
57+ {"non scalar", strings.Replace(good, `type="INT32"`, `type="Pair"`, 1), false},
58+ {"trailing field", good + `<field name="extra" type="CARD32"/>`, false},
59+ } {
60+ t.Run(tc.name, func(t *testing.T) {
61+ xml := strings.Replace(prefix, `<request name=`, `<struct name="Pair"><field name="a" type="CARD32"/></struct><request name=`, 1) + tc.body + `</request></xcb>`
62+ n, err := parseXML("values.xml", strings.NewReader(xml))
63+ if err != nil {
64+ t.Fatal(err)
65+ }
66+ ms, err := resolveModules([]*Node{n})
67+ if err != nil {
68+ t.Fatal(err)
69+ }
70+ rs, err := resolveRequests(ms[0])
71+ if !tc.valid {
72+ if err == nil || !strings.Contains(err.Error(), "values.xml:1:") {
73+ t.Fatalf("got %v", err)
74+ }
75+ return
76+ }
77+ if err != nil {
78+ t.Fatal(err)
79+ }
80+ v := rs[0].Values
81+ if v.Fields[0].Field.Name != "Low" || v.Fields[1].Field.Name != "High" {
82+ t.Fatal("XML order used instead of bit order")
83+ }
84+ decl, err := requestDecl(ms[0], rs[0])
85+ if err != nil {
86+ t.Fatal(err)
87+ }
88+ if !strings.Contains(decl, "uint32(v.Low)") || !strings.Contains(decl, "if v.High") {
89+ t.Fatal(decl)
90+ }
91+ })
92+ }
93+}
@@ -0,0 +1,93 @@
1+package main
2+
3+import (
4+ "strings"
5+ "testing"
6+)
7+
8+func TestResolvePinnedValues(t *testing.T) {
9+ found := map[string]bool{}
10+ for _, m := range testModules(t) {
11+ rs, err := resolveRequests(m)
12+ if err != nil {
13+ t.Fatal(err)
14+ }
15+ for _, r := range rs {
16+ if r.Values != nil {
17+ found[m.Pkg+"/"+r.Name] = true
18+ if len(r.Values.Fields) == 0 {
19+ t.Fatal("empty values")
20+ }
21+ for i, f := range r.Values.Fields {
22+ if i > 0 && r.Values.Fields[i-1].Value >= f.Value {
23+ t.Fatal("not in bit order")
24+ }
25+ }
26+ for _, p := range r.Params {
27+ if p.Param == "valueMask" {
28+ t.Fatal("mask leaked into params")
29+ }
30+ }
31+ }
32+ }
33+ }
34+ for _, name := range []string{"xproto/CreateWindow", "xproto/ChangeWindowAttributes", "xproto/ConfigureWindow", "xproto/CreateGC", "xproto/ChangeGC", "xproto/ChangeKeyboardControl", "render/CreatePicture", "render/ChangePicture"} {
35+ if !found[name] {
36+ t.Error("missing", name)
37+ }
38+ }
39+ if len(found) != 8 {
40+ t.Fatal(found)
41+ }
42+}
43+
44+func TestResolveValueSwitches(t *testing.T) {
45+ prefix := `<xcb header="test"><enum name="Mask"><item name="Low"><bit>0</bit></item><item name="High"><bit>3</bit></item></enum><request name="Configure" opcode="1"><pad bytes="1"/><field name="value_mask" type="CARD32" mask="Mask"/>`
46+ high := `<bitcase><enumref ref="Mask">High</enumref><field name="high" type="BOOL32"/></bitcase>`
47+ low := `<bitcase><enumref ref="Mask">Low</enumref><field name="low" type="INT32"/></bitcase>`
48+ good := `<switch name="value_list"><fieldref>value_mask</fieldref>` + high + low + `</switch>`
49+ for _, tc := range []struct {
50+ name, body string
51+ valid bool
52+ }{
53+ {"sorted", good, true},
54+ {"expression", strings.Replace(good, "<fieldref>value_mask</fieldref>", "<popcount><fieldref>value_mask</fieldref></popcount>", 1), false},
55+ {"duplicate", strings.Replace(good, low, high, 1), false},
56+ {"unknown bit", strings.Replace(good, ">Low<", ">Missing<", 1), false},
57+ {"non scalar", strings.Replace(good, `type="INT32"`, `type="Pair"`, 1), false},
58+ {"trailing field", good + `<field name="extra" type="CARD32"/>`, false},
59+ } {
60+ t.Run(tc.name, func(t *testing.T) {
61+ xml := strings.Replace(prefix, `<request name=`, `<struct name="Pair"><field name="a" type="CARD32"/></struct><request name=`, 1) + tc.body + `</request></xcb>`
62+ n, err := parseXML("values.xml", strings.NewReader(xml))
63+ if err != nil {
64+ t.Fatal(err)
65+ }
66+ ms, err := resolveModules([]*Node{n})
67+ if err != nil {
68+ t.Fatal(err)
69+ }
70+ rs, err := resolveRequests(ms[0])
71+ if !tc.valid {
72+ if err == nil || !strings.Contains(err.Error(), "values.xml:1:") {
73+ t.Fatalf("got %v", err)
74+ }
75+ return
76+ }
77+ if err != nil {
78+ t.Fatal(err)
79+ }
80+ v := rs[0].Values
81+ if v.Fields[0].Field.Name != "Low" || v.Fields[1].Field.Name != "High" {
82+ t.Fatal("XML order used instead of bit order")
83+ }
84+ decl, err := requestDecl(ms[0], rs[0])
85+ if err != nil {
86+ t.Fatal(err)
87+ }
88+ if !strings.Contains(decl, "uint32(v.Low)") || !strings.Contains(decl, "if v.High") {
89+ t.Fatal(decl)
90+ }
91+ })
92+ }
93+}
A composite/replies_fuzz_test.go +39 -0
@@ -0,0 +1,39 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "testing"
5+
6+func FuzzParseQueryVersionReply(f *testing.F) {
7+ f.Add(make([]byte, 32))
8+ f.Fuzz(func(t *testing.T, raw []byte) {
9+ r, ok := ParseQueryVersionReply(raw)
10+ if !ok {
11+ if len(raw) >= 32 {
12+ t.Fatal("rejected a complete fixed-size reply")
13+ }
14+ return
15+ }
16+ if len(r.Raw()) != len(raw) {
17+ t.Fatal("Raw must return the parsed bytes")
18+ }
19+ _ = r.MajorVersion()
20+ _ = r.MinorVersion()
21+ })
22+}
23+
24+func FuzzParseGetOverlayWindowReply(f *testing.F) {
25+ f.Add(make([]byte, 32))
26+ f.Fuzz(func(t *testing.T, raw []byte) {
27+ r, ok := ParseGetOverlayWindowReply(raw)
28+ if !ok {
29+ if len(raw) >= 32 {
30+ t.Fatal("rejected a complete fixed-size reply")
31+ }
32+ return
33+ }
34+ if len(r.Raw()) != len(raw) {
35+ t.Fatal("Raw must return the parsed bytes")
36+ }
37+ _ = r.OverlayWin()
38+ })
39+}
@@ -0,0 +1,39 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "testing"
5+
6+func FuzzParseQueryVersionReply(f *testing.F) {
7+ f.Add(make([]byte, 32))
8+ f.Fuzz(func(t *testing.T, raw []byte) {
9+ r, ok := ParseQueryVersionReply(raw)
10+ if !ok {
11+ if len(raw) >= 32 {
12+ t.Fatal("rejected a complete fixed-size reply")
13+ }
14+ return
15+ }
16+ if len(r.Raw()) != len(raw) {
17+ t.Fatal("Raw must return the parsed bytes")
18+ }
19+ _ = r.MajorVersion()
20+ _ = r.MinorVersion()
21+ })
22+}
23+
24+func FuzzParseGetOverlayWindowReply(f *testing.F) {
25+ f.Add(make([]byte, 32))
26+ f.Fuzz(func(t *testing.T, raw []byte) {
27+ r, ok := ParseGetOverlayWindowReply(raw)
28+ if !ok {
29+ if len(raw) >= 32 {
30+ t.Fatal("rejected a complete fixed-size reply")
31+ }
32+ return
33+ }
34+ if len(r.Raw()) != len(raw) {
35+ t.Fatal("Raw must return the parsed bytes")
36+ }
37+ _ = r.OverlayWin()
38+ })
39+}
A composite/replies_generated.go +42 -0
@@ -0,0 +1,42 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
8+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
9+type QueryVersionReply struct{ raw []byte }
10+
11+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
12+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
13+ if len(raw) < 32 {
14+ return QueryVersionReply{}, false
15+ }
16+ r := QueryVersionReply{raw: raw}
17+ return r, true
18+}
19+func (r QueryVersionReply) Raw() []byte { return r.raw }
20+func (r QueryVersionReply) MajorVersion() uint32 {
21+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
22+}
23+func (r QueryVersionReply) MinorVersion() uint32 {
24+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
25+}
26+
27+// GetOverlayWindowReply is a zero-copy view over a GetOverlayWindow reply; offsets count from reply byte 0.
28+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
29+type GetOverlayWindowReply struct{ raw []byte }
30+
31+// ParseGetOverlayWindowReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
32+func ParseGetOverlayWindowReply(raw []byte) (GetOverlayWindowReply, bool) {
33+ if len(raw) < 32 {
34+ return GetOverlayWindowReply{}, false
35+ }
36+ r := GetOverlayWindowReply{raw: raw}
37+ return r, true
38+}
39+func (r GetOverlayWindowReply) Raw() []byte { return r.raw }
40+func (r GetOverlayWindowReply) OverlayWin() xproto.Window {
41+ return xproto.Window(binary.LittleEndian.Uint32(r.raw[8:]))
42+}
@@ -0,0 +1,42 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
8+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
9+type QueryVersionReply struct{ raw []byte }
10+
11+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
12+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
13+ if len(raw) < 32 {
14+ return QueryVersionReply{}, false
15+ }
16+ r := QueryVersionReply{raw: raw}
17+ return r, true
18+}
19+func (r QueryVersionReply) Raw() []byte { return r.raw }
20+func (r QueryVersionReply) MajorVersion() uint32 {
21+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
22+}
23+func (r QueryVersionReply) MinorVersion() uint32 {
24+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
25+}
26+
27+// GetOverlayWindowReply is a zero-copy view over a GetOverlayWindow reply; offsets count from reply byte 0.
28+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
29+type GetOverlayWindowReply struct{ raw []byte }
30+
31+// ParseGetOverlayWindowReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
32+func ParseGetOverlayWindowReply(raw []byte) (GetOverlayWindowReply, bool) {
33+ if len(raw) < 32 {
34+ return GetOverlayWindowReply{}, false
35+ }
36+ r := GetOverlayWindowReply{raw: raw}
37+ return r, true
38+}
39+func (r GetOverlayWindowReply) Raw() []byte { return r.raw }
40+func (r GetOverlayWindowReply) OverlayWin() xproto.Window {
41+ return xproto.Window(binary.LittleEndian.Uint32(r.raw[8:]))
42+}
A composite/requests_generated.go +176 -0
@@ -0,0 +1,176 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "bnema.dev/xturbo/xfixes"
5+import "bnema.dev/xturbo/xproto"
6+import "encoding/binary"
7+import "bnema.dev/xturbo"
8+
9+func QueryVersion(b *xturbo.Buf, clientMajorVersion uint32, clientMinorVersion uint32) xturbo.Seq {
10+ if b.Ext == nil {
11+ b.Fail(xturbo.ErrExtMissing)
12+ return 0
13+ }
14+ major := b.Ext.Major(xturbo.ExtComposite)
15+ if major == 0 {
16+ b.Fail(xturbo.ErrExtMissing)
17+ return 0
18+ }
19+ p, seq := b.Request(major, 0, 8)
20+ if p == nil {
21+ return 0
22+ }
23+ binary.LittleEndian.PutUint32(p[0:], uint32(clientMajorVersion))
24+ binary.LittleEndian.PutUint32(p[4:], uint32(clientMinorVersion))
25+ return seq
26+}
27+
28+func RedirectWindow(b *xturbo.Buf, window xproto.Window, update Redirect) xturbo.Seq {
29+ if b.Ext == nil {
30+ b.Fail(xturbo.ErrExtMissing)
31+ return 0
32+ }
33+ major := b.Ext.Major(xturbo.ExtComposite)
34+ if major == 0 {
35+ b.Fail(xturbo.ErrExtMissing)
36+ return 0
37+ }
38+ p, seq := b.Request(major, 1, 8)
39+ if p == nil {
40+ return 0
41+ }
42+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
43+ p[4:][0] = byte(update)
44+ return seq
45+}
46+
47+func RedirectSubwindows(b *xturbo.Buf, window xproto.Window, update Redirect) xturbo.Seq {
48+ if b.Ext == nil {
49+ b.Fail(xturbo.ErrExtMissing)
50+ return 0
51+ }
52+ major := b.Ext.Major(xturbo.ExtComposite)
53+ if major == 0 {
54+ b.Fail(xturbo.ErrExtMissing)
55+ return 0
56+ }
57+ p, seq := b.Request(major, 2, 8)
58+ if p == nil {
59+ return 0
60+ }
61+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
62+ p[4:][0] = byte(update)
63+ return seq
64+}
65+
66+func UnredirectWindow(b *xturbo.Buf, window xproto.Window, update Redirect) xturbo.Seq {
67+ if b.Ext == nil {
68+ b.Fail(xturbo.ErrExtMissing)
69+ return 0
70+ }
71+ major := b.Ext.Major(xturbo.ExtComposite)
72+ if major == 0 {
73+ b.Fail(xturbo.ErrExtMissing)
74+ return 0
75+ }
76+ p, seq := b.Request(major, 3, 8)
77+ if p == nil {
78+ return 0
79+ }
80+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
81+ p[4:][0] = byte(update)
82+ return seq
83+}
84+
85+func UnredirectSubwindows(b *xturbo.Buf, window xproto.Window, update Redirect) xturbo.Seq {
86+ if b.Ext == nil {
87+ b.Fail(xturbo.ErrExtMissing)
88+ return 0
89+ }
90+ major := b.Ext.Major(xturbo.ExtComposite)
91+ if major == 0 {
92+ b.Fail(xturbo.ErrExtMissing)
93+ return 0
94+ }
95+ p, seq := b.Request(major, 4, 8)
96+ if p == nil {
97+ return 0
98+ }
99+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
100+ p[4:][0] = byte(update)
101+ return seq
102+}
103+
104+func CreateRegionFromBorderClip(b *xturbo.Buf, region xfixes.Region, window xproto.Window) xturbo.Seq {
105+ if b.Ext == nil {
106+ b.Fail(xturbo.ErrExtMissing)
107+ return 0
108+ }
109+ major := b.Ext.Major(xturbo.ExtComposite)
110+ if major == 0 {
111+ b.Fail(xturbo.ErrExtMissing)
112+ return 0
113+ }
114+ p, seq := b.Request(major, 5, 8)
115+ if p == nil {
116+ return 0
117+ }
118+ binary.LittleEndian.PutUint32(p[0:], uint32(region))
119+ binary.LittleEndian.PutUint32(p[4:], uint32(window))
120+ return seq
121+}
122+
123+func NameWindowPixmap(b *xturbo.Buf, window xproto.Window, pixmap xproto.Pixmap) xturbo.Seq {
124+ if b.Ext == nil {
125+ b.Fail(xturbo.ErrExtMissing)
126+ return 0
127+ }
128+ major := b.Ext.Major(xturbo.ExtComposite)
129+ if major == 0 {
130+ b.Fail(xturbo.ErrExtMissing)
131+ return 0
132+ }
133+ p, seq := b.Request(major, 6, 8)
134+ if p == nil {
135+ return 0
136+ }
137+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
138+ binary.LittleEndian.PutUint32(p[4:], uint32(pixmap))
139+ return seq
140+}
141+
142+func GetOverlayWindow(b *xturbo.Buf, window xproto.Window) xturbo.Seq {
143+ if b.Ext == nil {
144+ b.Fail(xturbo.ErrExtMissing)
145+ return 0
146+ }
147+ major := b.Ext.Major(xturbo.ExtComposite)
148+ if major == 0 {
149+ b.Fail(xturbo.ErrExtMissing)
150+ return 0
151+ }
152+ p, seq := b.Request(major, 7, 4)
153+ if p == nil {
154+ return 0
155+ }
156+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
157+ return seq
158+}
159+
160+func ReleaseOverlayWindow(b *xturbo.Buf, window xproto.Window) xturbo.Seq {
161+ if b.Ext == nil {
162+ b.Fail(xturbo.ErrExtMissing)
163+ return 0
164+ }
165+ major := b.Ext.Major(xturbo.ExtComposite)
166+ if major == 0 {
167+ b.Fail(xturbo.ErrExtMissing)
168+ return 0
169+ }
170+ p, seq := b.Request(major, 8, 4)
171+ if p == nil {
172+ return 0
173+ }
174+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
175+ return seq
176+}
@@ -0,0 +1,176 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "bnema.dev/xturbo/xfixes"
5+import "bnema.dev/xturbo/xproto"
6+import "encoding/binary"
7+import "bnema.dev/xturbo"
8+
9+func QueryVersion(b *xturbo.Buf, clientMajorVersion uint32, clientMinorVersion uint32) xturbo.Seq {
10+ if b.Ext == nil {
11+ b.Fail(xturbo.ErrExtMissing)
12+ return 0
13+ }
14+ major := b.Ext.Major(xturbo.ExtComposite)
15+ if major == 0 {
16+ b.Fail(xturbo.ErrExtMissing)
17+ return 0
18+ }
19+ p, seq := b.Request(major, 0, 8)
20+ if p == nil {
21+ return 0
22+ }
23+ binary.LittleEndian.PutUint32(p[0:], uint32(clientMajorVersion))
24+ binary.LittleEndian.PutUint32(p[4:], uint32(clientMinorVersion))
25+ return seq
26+}
27+
28+func RedirectWindow(b *xturbo.Buf, window xproto.Window, update Redirect) xturbo.Seq {
29+ if b.Ext == nil {
30+ b.Fail(xturbo.ErrExtMissing)
31+ return 0
32+ }
33+ major := b.Ext.Major(xturbo.ExtComposite)
34+ if major == 0 {
35+ b.Fail(xturbo.ErrExtMissing)
36+ return 0
37+ }
38+ p, seq := b.Request(major, 1, 8)
39+ if p == nil {
40+ return 0
41+ }
42+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
43+ p[4:][0] = byte(update)
44+ return seq
45+}
46+
47+func RedirectSubwindows(b *xturbo.Buf, window xproto.Window, update Redirect) xturbo.Seq {
48+ if b.Ext == nil {
49+ b.Fail(xturbo.ErrExtMissing)
50+ return 0
51+ }
52+ major := b.Ext.Major(xturbo.ExtComposite)
53+ if major == 0 {
54+ b.Fail(xturbo.ErrExtMissing)
55+ return 0
56+ }
57+ p, seq := b.Request(major, 2, 8)
58+ if p == nil {
59+ return 0
60+ }
61+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
62+ p[4:][0] = byte(update)
63+ return seq
64+}
65+
66+func UnredirectWindow(b *xturbo.Buf, window xproto.Window, update Redirect) xturbo.Seq {
67+ if b.Ext == nil {
68+ b.Fail(xturbo.ErrExtMissing)
69+ return 0
70+ }
71+ major := b.Ext.Major(xturbo.ExtComposite)
72+ if major == 0 {
73+ b.Fail(xturbo.ErrExtMissing)
74+ return 0
75+ }
76+ p, seq := b.Request(major, 3, 8)
77+ if p == nil {
78+ return 0
79+ }
80+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
81+ p[4:][0] = byte(update)
82+ return seq
83+}
84+
85+func UnredirectSubwindows(b *xturbo.Buf, window xproto.Window, update Redirect) xturbo.Seq {
86+ if b.Ext == nil {
87+ b.Fail(xturbo.ErrExtMissing)
88+ return 0
89+ }
90+ major := b.Ext.Major(xturbo.ExtComposite)
91+ if major == 0 {
92+ b.Fail(xturbo.ErrExtMissing)
93+ return 0
94+ }
95+ p, seq := b.Request(major, 4, 8)
96+ if p == nil {
97+ return 0
98+ }
99+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
100+ p[4:][0] = byte(update)
101+ return seq
102+}
103+
104+func CreateRegionFromBorderClip(b *xturbo.Buf, region xfixes.Region, window xproto.Window) xturbo.Seq {
105+ if b.Ext == nil {
106+ b.Fail(xturbo.ErrExtMissing)
107+ return 0
108+ }
109+ major := b.Ext.Major(xturbo.ExtComposite)
110+ if major == 0 {
111+ b.Fail(xturbo.ErrExtMissing)
112+ return 0
113+ }
114+ p, seq := b.Request(major, 5, 8)
115+ if p == nil {
116+ return 0
117+ }
118+ binary.LittleEndian.PutUint32(p[0:], uint32(region))
119+ binary.LittleEndian.PutUint32(p[4:], uint32(window))
120+ return seq
121+}
122+
123+func NameWindowPixmap(b *xturbo.Buf, window xproto.Window, pixmap xproto.Pixmap) xturbo.Seq {
124+ if b.Ext == nil {
125+ b.Fail(xturbo.ErrExtMissing)
126+ return 0
127+ }
128+ major := b.Ext.Major(xturbo.ExtComposite)
129+ if major == 0 {
130+ b.Fail(xturbo.ErrExtMissing)
131+ return 0
132+ }
133+ p, seq := b.Request(major, 6, 8)
134+ if p == nil {
135+ return 0
136+ }
137+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
138+ binary.LittleEndian.PutUint32(p[4:], uint32(pixmap))
139+ return seq
140+}
141+
142+func GetOverlayWindow(b *xturbo.Buf, window xproto.Window) xturbo.Seq {
143+ if b.Ext == nil {
144+ b.Fail(xturbo.ErrExtMissing)
145+ return 0
146+ }
147+ major := b.Ext.Major(xturbo.ExtComposite)
148+ if major == 0 {
149+ b.Fail(xturbo.ErrExtMissing)
150+ return 0
151+ }
152+ p, seq := b.Request(major, 7, 4)
153+ if p == nil {
154+ return 0
155+ }
156+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
157+ return seq
158+}
159+
160+func ReleaseOverlayWindow(b *xturbo.Buf, window xproto.Window) xturbo.Seq {
161+ if b.Ext == nil {
162+ b.Fail(xturbo.ErrExtMissing)
163+ return 0
164+ }
165+ major := b.Ext.Major(xturbo.ExtComposite)
166+ if major == 0 {
167+ b.Fail(xturbo.ErrExtMissing)
168+ return 0
169+ }
170+ p, seq := b.Request(major, 8, 4)
171+ if p == nil {
172+ return 0
173+ }
174+ binary.LittleEndian.PutUint32(p[0:], uint32(window))
175+ return seq
176+}
A composite/sizes_test.go +14 -0
@@ -0,0 +1,14 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "testing"
5+
6+func TestWireSizes(t *testing.T) {
7+ if got := 32; got != 32 {
8+ t.Errorf("reply QueryVersion fixed size = %d", got)
9+ }
10+ if got := 32; got != 32 {
11+ t.Errorf("reply GetOverlayWindow fixed size = %d", got)
12+ }
13+
14+}
@@ -0,0 +1,14 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "testing"
5+
6+func TestWireSizes(t *testing.T) {
7+ if got := 32; got != 32 {
8+ t.Errorf("reply QueryVersion fixed size = %d", got)
9+ }
10+ if got := 32; got != 32 {
11+ t.Errorf("reply GetOverlayWindow fixed size = %d", got)
12+ }
13+
14+}
A composite/types_generated.go +7 -0
@@ -0,0 +1,7 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+type Redirect uint8
5+
6+const RedirectAutomatic Redirect = 0
7+const RedirectManual Redirect = 1
@@ -0,0 +1,7 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+type Redirect uint8
5+
6+const RedirectAutomatic Redirect = 0
7+const RedirectManual Redirect = 1
A ext.go +27 -0
@@ -0,0 +1,27 @@
1+package xturbo
2+
3+// ExtInfo is a negotiated extension's wire opcode and event/error bases.
4+type ExtInfo struct {
5+ Present bool
6+ Major, FirstEvent, FirstError uint8
7+}
8+
9+// ExtTable is populated during setup. Fixed arrays avoid lookup allocations.
10+// Reverse mapping is completed with the read-side runtime in Phase 3.
11+type ExtTable struct{ info [extCount]ExtInfo }
12+
13+func (t *ExtTable) Set(id ExtID, major, firstEvent, firstError uint8) {
14+ if id < extCount {
15+ t.info[id] = ExtInfo{true, major, firstEvent, firstError}
16+ }
17+}
18+func (t *ExtTable) Info(id ExtID) ExtInfo {
19+ if id >= extCount {
20+ return ExtInfo{}
21+ }
22+ if id == ExtCore {
23+ return ExtInfo{Present: true}
24+ }
25+ return t.info[id]
26+}
27+func (t *ExtTable) Major(id ExtID) uint8 { return t.Info(id).Major }
@@ -0,0 +1,27 @@
1+package xturbo
2+
3+// ExtInfo is a negotiated extension's wire opcode and event/error bases.
4+type ExtInfo struct {
5+ Present bool
6+ Major, FirstEvent, FirstError uint8
7+}
8+
9+// ExtTable is populated during setup. Fixed arrays avoid lookup allocations.
10+// Reverse mapping is completed with the read-side runtime in Phase 3.
11+type ExtTable struct{ info [extCount]ExtInfo }
12+
13+func (t *ExtTable) Set(id ExtID, major, firstEvent, firstError uint8) {
14+ if id < extCount {
15+ t.info[id] = ExtInfo{true, major, firstEvent, firstError}
16+ }
17+}
18+func (t *ExtTable) Info(id ExtID) ExtInfo {
19+ if id >= extCount {
20+ return ExtInfo{}
21+ }
22+ if id == ExtCore {
23+ return ExtInfo{Present: true}
24+ }
25+ return t.info[id]
26+}
27+func (t *ExtTable) Major(id ExtID) uint8 { return t.Info(id).Major }
A ext_generated.go +19 -0
@@ -0,0 +1,19 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xturbo
3+
4+type ExtID uint8
5+
6+const (
7+ ExtCore ExtID = iota
8+ ExtBigRequests
9+ ExtGenericEvent
10+ ExtXCMisc
11+ ExtComposite
12+ ExtXFixes
13+ ExtRes
14+ ExtRender
15+ ExtShape
16+ extCount
17+)
18+
19+var ExtXName = [extCount]string{"", "BIG-REQUESTS", "Generic Event Extension", "XC-MISC", "Composite", "XFIXES", "X-Resource", "RENDER", "SHAPE"}
@@ -0,0 +1,19 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xturbo
3+
4+type ExtID uint8
5+
6+const (
7+ ExtCore ExtID = iota
8+ ExtBigRequests
9+ ExtGenericEvent
10+ ExtXCMisc
11+ ExtComposite
12+ ExtXFixes
13+ ExtRes
14+ ExtRender
15+ ExtShape
16+ extCount
17+)
18+
19+var ExtXName = [extCount]string{"", "BIG-REQUESTS", "Generic Event Extension", "XC-MISC", "Composite", "XFIXES", "X-Resource", "RENDER", "SHAPE"}
A ge/replies_fuzz_test.go +22 -0
@@ -0,0 +1,22 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "testing"
5+
6+func FuzzParseQueryVersionReply(f *testing.F) {
7+ f.Add(make([]byte, 32))
8+ f.Fuzz(func(t *testing.T, raw []byte) {
9+ r, ok := ParseQueryVersionReply(raw)
10+ if !ok {
11+ if len(raw) >= 32 {
12+ t.Fatal("rejected a complete fixed-size reply")
13+ }
14+ return
15+ }
16+ if len(r.Raw()) != len(raw) {
17+ t.Fatal("Raw must return the parsed bytes")
18+ }
19+ _ = r.MajorVersion()
20+ _ = r.MinorVersion()
21+ })
22+}
@@ -0,0 +1,22 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "testing"
5+
6+func FuzzParseQueryVersionReply(f *testing.F) {
7+ f.Add(make([]byte, 32))
8+ f.Fuzz(func(t *testing.T, raw []byte) {
9+ r, ok := ParseQueryVersionReply(raw)
10+ if !ok {
11+ if len(raw) >= 32 {
12+ t.Fatal("rejected a complete fixed-size reply")
13+ }
14+ return
15+ }
16+ if len(r.Raw()) != len(raw) {
17+ t.Fatal("Raw must return the parsed bytes")
18+ }
19+ _ = r.MajorVersion()
20+ _ = r.MinorVersion()
21+ })
22+}
A ge/replies_generated.go +24 -0
@@ -0,0 +1,24 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "encoding/binary"
5+
6+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
7+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
8+type QueryVersionReply struct{ raw []byte }
9+
10+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
11+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
12+ if len(raw) < 32 {
13+ return QueryVersionReply{}, false
14+ }
15+ r := QueryVersionReply{raw: raw}
16+ return r, true
17+}
18+func (r QueryVersionReply) Raw() []byte { return r.raw }
19+func (r QueryVersionReply) MajorVersion() uint16 {
20+ return uint16(binary.LittleEndian.Uint16(r.raw[8:]))
21+}
22+func (r QueryVersionReply) MinorVersion() uint16 {
23+ return uint16(binary.LittleEndian.Uint16(r.raw[10:]))
24+}
@@ -0,0 +1,24 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "encoding/binary"
5+
6+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
7+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
8+type QueryVersionReply struct{ raw []byte }
9+
10+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
11+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
12+ if len(raw) < 32 {
13+ return QueryVersionReply{}, false
14+ }
15+ r := QueryVersionReply{raw: raw}
16+ return r, true
17+}
18+func (r QueryVersionReply) Raw() []byte { return r.raw }
19+func (r QueryVersionReply) MajorVersion() uint16 {
20+ return uint16(binary.LittleEndian.Uint16(r.raw[8:]))
21+}
22+func (r QueryVersionReply) MinorVersion() uint16 {
23+ return uint16(binary.LittleEndian.Uint16(r.raw[10:]))
24+}
A ge/requests_generated.go +24 -0
@@ -0,0 +1,24 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "encoding/binary"
5+import "bnema.dev/xturbo"
6+
7+func QueryVersion(b *xturbo.Buf, clientMajorVersion uint16, clientMinorVersion uint16) xturbo.Seq {
8+ if b.Ext == nil {
9+ b.Fail(xturbo.ErrExtMissing)
10+ return 0
11+ }
12+ major := b.Ext.Major(xturbo.ExtGenericEvent)
13+ if major == 0 {
14+ b.Fail(xturbo.ErrExtMissing)
15+ return 0
16+ }
17+ p, seq := b.Request(major, 0, 4)
18+ if p == nil {
19+ return 0
20+ }
21+ binary.LittleEndian.PutUint16(p[0:], uint16(clientMajorVersion))
22+ binary.LittleEndian.PutUint16(p[2:], uint16(clientMinorVersion))
23+ return seq
24+}
@@ -0,0 +1,24 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "encoding/binary"
5+import "bnema.dev/xturbo"
6+
7+func QueryVersion(b *xturbo.Buf, clientMajorVersion uint16, clientMinorVersion uint16) xturbo.Seq {
8+ if b.Ext == nil {
9+ b.Fail(xturbo.ErrExtMissing)
10+ return 0
11+ }
12+ major := b.Ext.Major(xturbo.ExtGenericEvent)
13+ if major == 0 {
14+ b.Fail(xturbo.ErrExtMissing)
15+ return 0
16+ }
17+ p, seq := b.Request(major, 0, 4)
18+ if p == nil {
19+ return 0
20+ }
21+ binary.LittleEndian.PutUint16(p[0:], uint16(clientMajorVersion))
22+ binary.LittleEndian.PutUint16(p[2:], uint16(clientMinorVersion))
23+ return seq
24+}
A ge/sizes_test.go +11 -0
@@ -0,0 +1,11 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "testing"
5+
6+func TestWireSizes(t *testing.T) {
7+ if got := 32; got != 32 {
8+ t.Errorf("reply QueryVersion fixed size = %d", got)
9+ }
10+
11+}
@@ -0,0 +1,11 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "testing"
5+
6+func TestWireSizes(t *testing.T) {
7+ if got := 32; got != 32 {
8+ t.Errorf("reply QueryVersion fixed size = %d", got)
9+ }
10+
11+}
A ge/types_generated.go +2 -0
@@ -0,0 +1,2 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
@@ -0,0 +1,2 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
A generate.go +3 -0
@@ -0,0 +1,3 @@
1+package xturbo
2+
3+//go:generate go run ./cmd/xtgen -all
@@ -0,0 +1,3 @@
1+package xturbo
2+
3+//go:generate go run ./cmd/xtgen -all
A internal/xtest/README.md +40 -0
@@ -0,0 +1,40 @@
1+# Wire goldens
2+
3+Refresh the request fixtures from the repository root:
4+
5+```
6+PATH="$PATH:/usr/sbin" go test -tags xserver ./internal/xtest -update
7+```
8+
9+The local `/usr/sbin/xtrace` is glibc's program execution tracer, not the X11
10+protocol proxy. The permitted fallback uses a raw Unix socketpair tee connected
11+to a fresh Xvfb server. A small independent reference encoder (not the generated
12+bindings) sends the requests; the tee records their actual bytes. GetGeometry's
13+successful response also establishes that the preceding MapWindow was accepted.
14+The captured target is the root window; XFIXES's major opcode is negotiated.
15+Tests read target/opcode from the fixture, not a hard-coded server assignment.
16+No runtime Conn is needed at this phase.
17+
18+The same run also writes reply goldens (`<Name>.reply.hex`): the server's
19+response to each reference request, read back through the tee. Events and
20+runtime integration belong to their subsequent tasks.
21+
22+T2.4 adds InternAtom, ChangeProperty and XRes QueryClientIds. A GetProperty
23+read-back validates the property contents. XRes's major opcode is negotiated;
24+QueryVersion 1.2 precedes the ClientIdSpec-list query. The capture is independent
25+of generated code, including counts, framing, and padding.
26+
27+T2.5 adds ConfigureWindow with empty, sparse and all-seven-bit masks,
28+CreateWindow, ChangeWindowAttributes and Render CreatePicture. The reference
29+encoder creates sibling windows, exercises signed coordinates and both BOOL32
30+values, and fences void requests with a successful reply (any preceding error
31+fails capture). RENDER's opcode and the root visual's picture format are queried
32+from Xvfb. Tests reuse only server-assigned resource/format IDs from fixtures;
33+mask bits, values and ordering remain independent expected inputs.
34+
35+T2.6 keeps the replies to QueryExtension, xfixes QueryVersion, GetGeometry,
36+InternAtom, GetProperty, res QueryClientIds and render QueryPictFormats, and
37+adds GetWindowAttributes (child window), QueryTree (root), an unset WM_NAME
38+GetProperty (zero-length value) and ListExtensions (variable STR list). These
39+reply-only requests use opcodes outside the request-golden classifier.
40+Generated `FuzzParse*Reply` targets seed from every matching reply golden.
@@ -0,0 +1,40 @@
1+# Wire goldens
2+
3+Refresh the request fixtures from the repository root:
4+
5+```
6+PATH="$PATH:/usr/sbin" go test -tags xserver ./internal/xtest -update
7+```
8+
9+The local `/usr/sbin/xtrace` is glibc's program execution tracer, not the X11
10+protocol proxy. The permitted fallback uses a raw Unix socketpair tee connected
11+to a fresh Xvfb server. A small independent reference encoder (not the generated
12+bindings) sends the requests; the tee records their actual bytes. GetGeometry's
13+successful response also establishes that the preceding MapWindow was accepted.
14+The captured target is the root window; XFIXES's major opcode is negotiated.
15+Tests read target/opcode from the fixture, not a hard-coded server assignment.
16+No runtime Conn is needed at this phase.
17+
18+The same run also writes reply goldens (`<Name>.reply.hex`): the server's
19+response to each reference request, read back through the tee. Events and
20+runtime integration belong to their subsequent tasks.
21+
22+T2.4 adds InternAtom, ChangeProperty and XRes QueryClientIds. A GetProperty
23+read-back validates the property contents. XRes's major opcode is negotiated;
24+QueryVersion 1.2 precedes the ClientIdSpec-list query. The capture is independent
25+of generated code, including counts, framing, and padding.
26+
27+T2.5 adds ConfigureWindow with empty, sparse and all-seven-bit masks,
28+CreateWindow, ChangeWindowAttributes and Render CreatePicture. The reference
29+encoder creates sibling windows, exercises signed coordinates and both BOOL32
30+values, and fences void requests with a successful reply (any preceding error
31+fails capture). RENDER's opcode and the root visual's picture format are queried
32+from Xvfb. Tests reuse only server-assigned resource/format IDs from fixtures;
33+mask bits, values and ordering remain independent expected inputs.
34+
35+T2.6 keeps the replies to QueryExtension, xfixes QueryVersion, GetGeometry,
36+InternAtom, GetProperty, res QueryClientIds and render QueryPictFormats, and
37+adds GetWindowAttributes (child window), QueryTree (root), an unset WM_NAME
38+GetProperty (zero-length value) and ListExtensions (variable STR list). These
39+reply-only requests use opcodes outside the request-golden classifier.
40+Generated `FuzzParse*Reply` targets seed from every matching reply golden.
A internal/xtest/capture_test.go +405 -0
@@ -0,0 +1,405 @@
1+//go:build xserver
2+
3+package xtest
4+
5+import (
6+ "bytes"
7+ "encoding/binary"
8+ "encoding/hex"
9+ "flag"
10+ "fmt"
11+ "io"
12+ "net"
13+ "os"
14+ "os/exec"
15+ "path/filepath"
16+ "strconv"
17+ "strings"
18+ "testing"
19+ "time"
20+
21+ "golang.org/x/sys/unix"
22+)
23+
24+var update = flag.Bool("update", false, "refresh request goldens using Xvfb and a raw socketpair tee")
25+
26+func TestCaptureRequests(t *testing.T) {
27+ if !*update {
28+ t.Skip("pass -update to capture request goldens")
29+ }
30+ // displayfd avoids display-number races and tells us when Xvfb is ready.
31+ r, w, err := os.Pipe()
32+ if err != nil {
33+ t.Fatal(err)
34+ }
35+ defer r.Close()
36+ cmd := exec.Command("Xvfb", "-displayfd", "3", "-screen", "0", "800x600x24", "-nolisten", "tcp", "-ac")
37+ cmd.ExtraFiles = []*os.File{w}
38+ var log bytes.Buffer
39+ cmd.Stderr = &log
40+ if err = cmd.Start(); err != nil {
41+ w.Close()
42+ t.Fatal(err)
43+ }
44+ w.Close()
45+ defer func() { _ = cmd.Process.Kill(); _ = cmd.Wait() }()
46+ _ = r.SetReadDeadline(time.Now().Add(5 * time.Second))
47+ var display [32]byte
48+ n, err := r.Read(display[:])
49+ if err != nil {
50+ t.Fatalf("Xvfb: %v: %s", err, log.String())
51+ }
52+ num, err := strconv.Atoi(strings.TrimSpace(string(display[:n])))
53+ if err != nil {
54+ t.Fatal(err)
55+ }
56+ server, err := net.DialTimeout("unix", fmt.Sprintf("/tmp/.X11-unix/X%d", num), 5*time.Second)
57+ if err != nil {
58+ t.Fatal(err)
59+ }
60+ defer server.Close()
61+ fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
62+ if err != nil {
63+ t.Fatal(err)
64+ }
65+ a := os.NewFile(uintptr(fds[0]), "capture-client")
66+ b := os.NewFile(uintptr(fds[1]), "capture-tee")
67+ client, err := net.FileConn(a)
68+ a.Close()
69+ if err != nil {
70+ b.Close()
71+ t.Fatal(err)
72+ }
73+ defer client.Close()
74+ tee, err := net.FileConn(b)
75+ b.Close()
76+ if err != nil {
77+ t.Fatal(err)
78+ }
79+ defer tee.Close()
80+ _ = client.SetDeadline(time.Now().Add(5 * time.Second))
81+ _ = server.SetDeadline(time.Now().Add(5 * time.Second))
82+ var recorded bytes.Buffer
83+ copied := make(chan error, 1)
84+ go func() { _, err := io.Copy(server, io.TeeReader(tee, &recorded)); copied <- err }()
85+ go func() { _, _ = io.Copy(tee, server) }()
86+ write := func(p []byte) {
87+ t.Helper()
88+ if _, err := client.Write(p); err != nil {
89+ t.Fatal(err)
90+ }
91+ }
92+ read := func(p []byte) {
93+ t.Helper()
94+ if _, err := io.ReadFull(client, p); err != nil {
95+ t.Fatal(err)
96+ }
97+ }
98+ // Independent reference encoder: no generated bindings participate in capture.
99+ write([]byte{'l', 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0})
100+ var setupHead [8]byte
101+ read(setupHead[:])
102+ if setupHead[0] != 1 {
103+ t.Fatalf("setup failed: %x", setupHead)
104+ }
105+ setup := make([]byte, int(binary.LittleEndian.Uint16(setupHead[6:]))*4)
106+ read(setup)
107+ vendorLen := int(binary.LittleEndian.Uint16(setup[16:]))
108+ formats := int(setup[21])
109+ screenOff := 32 + (vendorLen+3)&^3 + 8*formats
110+ root := binary.LittleEndian.Uint32(setup[screenOff:])
111+ // Query XFIXES dynamically rather than assuming a server-specific major opcode.
112+ query := []byte{98, 0, 4, 0, 6, 0, 0, 0, 'X', 'F', 'I', 'X', 'E', 'S', 0, 0}
113+ write(query)
114+ // Server bytes reach the client through the same tee; keep named replies as
115+ // reply goldens (reply byte 0 through 32+4*length).
116+ replies := map[string][]byte{}
117+ keep := func(name string, p []byte) []byte {
118+ t.Helper()
119+ if p[0] != 1 {
120+ t.Fatalf("%s: expected a reply, got %x", name, p)
121+ }
122+ replies[name] = p
123+ return p
124+ }
125+ packet := func() []byte {
126+ t.Helper()
127+ head := make([]byte, 32)
128+ read(head)
129+ if head[0] == 1 {
130+ tail := make([]byte, int(binary.LittleEndian.Uint32(head[4:]))*4)
131+ read(tail)
132+ head = append(head, tail...)
133+ }
134+ return head
135+ }
136+ ext := keep("xproto/QueryExtension", packet())
137+ if ext[0] != 1 || ext[8] != 1 {
138+ t.Fatalf("XFIXES unavailable: %x", ext)
139+ }
140+ major := ext[9]
141+ version := make([]byte, 12)
142+ version[0] = major
143+ version[2] = 3
144+ binary.LittleEndian.PutUint32(version[4:], 6)
145+ write(version)
146+ reply := keep("xfixes/QueryVersion", packet())
147+ if reply[0] != 1 {
148+ t.Fatalf("QueryVersion failed: %x", reply)
149+ }
150+ mapReq := make([]byte, 8)
151+ mapReq[0] = 8
152+ mapReq[2] = 2
153+ binary.LittleEndian.PutUint32(mapReq[4:], root)
154+ write(mapReq)
155+ geometry := append([]byte(nil), mapReq...)
156+ geometry[0] = 14
157+ write(geometry)
158+ geom := keep("xproto/GetGeometry", packet())
159+ if geom[0] != 1 || binary.LittleEndian.Uint16(geom[2:]) != 4 {
160+ t.Fatalf("MapWindow/GetGeometry rejected: %x", geom)
161+ }
162+
163+ // InternAtom, ChangeProperty and XRes QueryClientIds exercise derived lengths,
164+ // byte expressions and fixed-struct lists with independently assembled bytes.
165+ intern := []byte{16, 0, 4, 0, 7, 0, 0, 0, 'X', 'T', 'U', 'R', 'B', 'O', '_', 0}
166+ write(intern)
167+ atomReply := keep("xproto/InternAtom", packet())
168+ if atomReply[0] != 1 {
169+ t.Fatalf("InternAtom: %x", atomReply)
170+ }
171+ atom := binary.LittleEndian.Uint32(atomReply[8:])
172+ property := make([]byte, 28)
173+ property[0] = 18
174+ property[2] = 7
175+ binary.LittleEndian.PutUint32(property[4:], root)
176+ binary.LittleEndian.PutUint32(property[8:], atom)
177+ binary.LittleEndian.PutUint32(property[12:], 31)
178+ property[16] = 8
179+ binary.LittleEndian.PutUint32(property[20:], 3)
180+ copy(property[24:], "abc")
181+ write(property)
182+ // A successful GetProperty reply proves the preceding write was accepted.
183+ get := make([]byte, 24)
184+ get[0] = 20
185+ get[2] = 6
186+ binary.LittleEndian.PutUint32(get[4:], root)
187+ binary.LittleEndian.PutUint32(get[8:], atom)
188+ binary.LittleEndian.PutUint32(get[20:], 1)
189+ write(get)
190+ got := keep("xproto/GetProperty", packet())
191+ if got[0] != 1 || string(got[32:35]) != "abc" {
192+ t.Fatalf("ChangeProperty: %x", got)
193+ }
194+ resName := "X-Resource"
195+ queryRes := make([]byte, 20)
196+ queryRes[0] = 98
197+ queryRes[2] = 5
198+ queryRes[4] = byte(len(resName))
199+ copy(queryRes[8:], resName)
200+ write(queryRes)
201+ resExt := packet()
202+ if resExt[0] != 1 || resExt[8] != 1 {
203+ t.Fatalf("XRes unavailable: %x", resExt)
204+ }
205+ resMajor := resExt[9]
206+ resVersion := []byte{resMajor, 0, 2, 0, 1, 2, 0, 0}
207+ write(resVersion)
208+ if p := packet(); p[0] != 1 {
209+ t.Fatalf("XRes version: %x", p)
210+ }
211+ ids := make([]byte, 16)
212+ ids[0] = resMajor
213+ ids[1] = 4
214+ ids[2] = 4
215+ binary.LittleEndian.PutUint32(ids[4:], 1)
216+ binary.LittleEndian.PutUint32(ids[12:], 1)
217+ write(ids)
218+ keep("res/QueryClientIds", packet())
219+ // Value-list reference packets are assembled independently, including mask-bit
220+ // ordering. Two sibling windows allow all seven ConfigureWindow bits to be
221+ // exercised legally. A final reply fences all void requests and exposes errors.
222+ base := binary.LittleEndian.Uint32(setup[4:])
223+ child, sibling, picture := base|1, base|2, base|3
224+ create := func(id uint32, mask uint32, values ...uint32) {
225+ p := make([]byte, 32+4*len(values))
226+ p[0] = 1
227+ binary.LittleEndian.PutUint16(p[2:], uint16(len(p)/4))
228+ binary.LittleEndian.PutUint32(p[4:], id)
229+ binary.LittleEndian.PutUint32(p[8:], root)
230+ binary.LittleEndian.PutUint16(p[16:], 64)
231+ binary.LittleEndian.PutUint16(p[18:], 48)
232+ binary.LittleEndian.PutUint16(p[22:], 1) // InputOutput, inherited depth/visual
233+ binary.LittleEndian.PutUint32(p[28:], mask)
234+ for i, v := range values {
235+ binary.LittleEndian.PutUint32(p[32+4*i:], v)
236+ }
237+ write(p)
238+ }
239+ create(sibling, 0)
240+ create(child, 2|512|1024, 0x123456, 1, 0)
241+ change := make([]byte, 20)
242+ change[0], change[2] = 2, 5
243+ binary.LittleEndian.PutUint32(change[4:], child)
244+ binary.LittleEndian.PutUint32(change[8:], 512|1024)
245+ binary.LittleEndian.PutUint32(change[16:], 1) // false, true BOOL32 words
246+ write(change)
247+ configure := func(mask uint16, values ...uint32) {
248+ p := make([]byte, 12+4*len(values))
249+ p[0] = 12
250+ binary.LittleEndian.PutUint16(p[2:], uint16(len(p)/4))
251+ binary.LittleEndian.PutUint32(p[4:], child)
252+ binary.LittleEndian.PutUint16(p[8:], mask)
253+ for i, v := range values {
254+ binary.LittleEndian.PutUint32(p[12+4*i:], v)
255+ }
256+ write(p)
257+ }
258+ configure(0)
259+ configure(1|4|64, 0xfffffff9, 80, 0)
260+ configure(127, 0xfffffffd, 9, 96, 72, 2, sibling, 0)
261+ queryRender := []byte{98, 0, 4, 0, 6, 0, 0, 0, 'R', 'E', 'N', 'D', 'E', 'R', 0, 0}
262+ write(queryRender)
263+ renderExt := packet()
264+ if renderExt[0] != 1 || renderExt[8] != 1 {
265+ t.Fatalf("value requests/RENDER query: %x", renderExt)
266+ }
267+ renderMajor := renderExt[9]
268+ write([]byte{renderMajor, 1, 1, 0}) // QueryPictFormats
269+ formatsReply := keep("render/QueryPictFormats", packet())
270+ if formatsReply[0] != 1 {
271+ t.Fatalf("QueryPictFormats: %x", formatsReply)
272+ }
273+ // Match the root visual rather than guessing a server-assigned format ID.
274+ rootVisual := binary.LittleEndian.Uint32(setup[screenOff+32:])
275+ off := 32 + 28*int(binary.LittleEndian.Uint32(formatsReply[8:]))
276+ var format uint32
277+ for range binary.LittleEndian.Uint32(formatsReply[12:]) {
278+ depths := binary.LittleEndian.Uint32(formatsReply[off:])
279+ off += 8
280+ for range depths {
281+ visuals := binary.LittleEndian.Uint16(formatsReply[off+2:])
282+ off += 8
283+ for range visuals {
284+ if binary.LittleEndian.Uint32(formatsReply[off:]) == rootVisual {
285+ format = binary.LittleEndian.Uint32(formatsReply[off+4:])
286+ }
287+ off += 8
288+ }
289+ }
290+ }
291+ if format == 0 {
292+ t.Fatal("root visual has no Render format")
293+ }
294+ pic := make([]byte, 32)
295+ pic[0], pic[1], pic[2] = renderMajor, 4, 8
296+ binary.LittleEndian.PutUint32(pic[4:], picture)
297+ binary.LittleEndian.PutUint32(pic[8:], child)
298+ binary.LittleEndian.PutUint32(pic[12:], format)
299+ binary.LittleEndian.PutUint32(pic[16:], 1|16|32)
300+ binary.LittleEndian.PutUint32(pic[20:], 1) // RepeatNormal
301+ binary.LittleEndian.PutUint32(pic[24:], 0xfffffffb)
302+ binary.LittleEndian.PutUint32(pic[28:], 7)
303+ write(pic)
304+ write([]byte{43, 0, 1, 0}) // GetInputFocus fences CreatePicture
305+ if p := packet(); p[0] != 1 {
306+ t.Fatalf("CreatePicture rejected: %x", p)
307+ }
308+ // Reply-only captures. Opcodes 3, 15, 20 and 99 are not request goldens, so
309+ // the request-stream classifier below is unaffected.
310+ attrs := make([]byte, 8)
311+ attrs[0], attrs[2] = 3, 2
312+ binary.LittleEndian.PutUint32(attrs[4:], child)
313+ write(attrs)
314+ keep("xproto/GetWindowAttributes", packet())
315+ tree := make([]byte, 8)
316+ tree[0], tree[2] = 15, 2
317+ binary.LittleEndian.PutUint32(tree[4:], root)
318+ write(tree)
319+ keep("xproto/QueryTree", packet())
320+ // WM_NAME is unset on a fresh Xvfb root: a zero-length GetProperty reply.
321+ empty := make([]byte, 24)
322+ empty[0], empty[2] = 20, 6
323+ binary.LittleEndian.PutUint32(empty[4:], root)
324+ binary.LittleEndian.PutUint32(empty[8:], 39)
325+ binary.LittleEndian.PutUint32(empty[20:], 1)
326+ write(empty)
327+ keep("xproto/GetPropertyEmpty", packet())
328+ write([]byte{99, 0, 1, 0}) // ListExtensions: variable STR iterator
329+ keep("xproto/ListExtensions", packet())
330+ // Closing the client drains the request direction; wait before inspecting tee.
331+ client.Close()
332+ select {
333+ case err := <-copied:
334+ if err != nil {
335+ t.Fatal(err)
336+ }
337+ case <-time.After(5 * time.Second):
338+ t.Fatal("tee did not drain")
339+ }
340+ stream := recorded.Bytes()
341+ requests := stream[12:]
342+ captured := map[string][]byte{}
343+ configIndex := 0
344+ for len(requests) > 0 {
345+ size := int(binary.LittleEndian.Uint16(requests[2:])) * 4
346+ if size < 4 || size > len(requests) {
347+ t.Fatal("malformed capture")
348+ }
349+ p := requests[:size]
350+ switch p[0] {
351+ case 1:
352+ if binary.LittleEndian.Uint32(p[4:]) == child {
353+ captured["xproto/CreateWindow"] = p
354+ }
355+ case 2:
356+ captured["xproto/ChangeWindowAttributes"] = p
357+ case 12:
358+ names := []string{"ConfigureWindow", "ConfigureWindowSparse", "ConfigureWindowAll"}
359+ captured["xproto/"+names[configIndex]] = p
360+ configIndex++
361+ case 8:
362+ captured["xproto/MapWindow"] = p
363+ case 14:
364+ captured["xproto/GetGeometry"] = p
365+ case 16:
366+ captured["xproto/InternAtom"] = p
367+ case 18:
368+ captured["xproto/ChangeProperty"] = p
369+ default:
370+ if p[0] == renderMajor && p[1] == 4 {
371+ captured["render/CreatePicture"] = p
372+ }
373+ if p[0] == resMajor && p[1] == 4 {
374+ captured["res/QueryClientIds"] = p
375+ }
376+ if p[0] == major {
377+ captured["xfixes/QueryVersion"] = p
378+ }
379+ }
380+ requests = requests[size:]
381+ }
382+ if len(captured) != 12 || len(replies) != 11 {
383+ t.Fatal("missing captured requests or replies")
384+ }
385+ for name, p := range replies {
386+ path := filepath.Join("../../testdata/golden", name+".reply.hex")
387+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
388+ t.Fatal(err)
389+ }
390+ text := "# source: Xvfb reply via raw socketpair tee to an independent reference request\n# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace\n" + hex.EncodeToString(p) + "\n"
391+ if err := os.WriteFile(path, []byte(text), 0644); err != nil {
392+ t.Fatal(err)
393+ }
394+ }
395+ for name, p := range captured {
396+ path := filepath.Join("../../testdata/golden", name+".req.hex")
397+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
398+ t.Fatal(err)
399+ }
400+ text := "# source: Xvfb via raw socketpair tee; independent reference encoder; server accepted\n# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace\n" + hex.EncodeToString(p) + "\n"
401+ if err := os.WriteFile(path, []byte(text), 0644); err != nil {
402+ t.Fatal(err)
403+ }
404+ }
405+}
@@ -0,0 +1,405 @@
1+//go:build xserver
2+
3+package xtest
4+
5+import (
6+ "bytes"
7+ "encoding/binary"
8+ "encoding/hex"
9+ "flag"
10+ "fmt"
11+ "io"
12+ "net"
13+ "os"
14+ "os/exec"
15+ "path/filepath"
16+ "strconv"
17+ "strings"
18+ "testing"
19+ "time"
20+
21+ "golang.org/x/sys/unix"
22+)
23+
24+var update = flag.Bool("update", false, "refresh request goldens using Xvfb and a raw socketpair tee")
25+
26+func TestCaptureRequests(t *testing.T) {
27+ if !*update {
28+ t.Skip("pass -update to capture request goldens")
29+ }
30+ // displayfd avoids display-number races and tells us when Xvfb is ready.
31+ r, w, err := os.Pipe()
32+ if err != nil {
33+ t.Fatal(err)
34+ }
35+ defer r.Close()
36+ cmd := exec.Command("Xvfb", "-displayfd", "3", "-screen", "0", "800x600x24", "-nolisten", "tcp", "-ac")
37+ cmd.ExtraFiles = []*os.File{w}
38+ var log bytes.Buffer
39+ cmd.Stderr = &log
40+ if err = cmd.Start(); err != nil {
41+ w.Close()
42+ t.Fatal(err)
43+ }
44+ w.Close()
45+ defer func() { _ = cmd.Process.Kill(); _ = cmd.Wait() }()
46+ _ = r.SetReadDeadline(time.Now().Add(5 * time.Second))
47+ var display [32]byte
48+ n, err := r.Read(display[:])
49+ if err != nil {
50+ t.Fatalf("Xvfb: %v: %s", err, log.String())
51+ }
52+ num, err := strconv.Atoi(strings.TrimSpace(string(display[:n])))
53+ if err != nil {
54+ t.Fatal(err)
55+ }
56+ server, err := net.DialTimeout("unix", fmt.Sprintf("/tmp/.X11-unix/X%d", num), 5*time.Second)
57+ if err != nil {
58+ t.Fatal(err)
59+ }
60+ defer server.Close()
61+ fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
62+ if err != nil {
63+ t.Fatal(err)
64+ }
65+ a := os.NewFile(uintptr(fds[0]), "capture-client")
66+ b := os.NewFile(uintptr(fds[1]), "capture-tee")
67+ client, err := net.FileConn(a)
68+ a.Close()
69+ if err != nil {
70+ b.Close()
71+ t.Fatal(err)
72+ }
73+ defer client.Close()
74+ tee, err := net.FileConn(b)
75+ b.Close()
76+ if err != nil {
77+ t.Fatal(err)
78+ }
79+ defer tee.Close()
80+ _ = client.SetDeadline(time.Now().Add(5 * time.Second))
81+ _ = server.SetDeadline(time.Now().Add(5 * time.Second))
82+ var recorded bytes.Buffer
83+ copied := make(chan error, 1)
84+ go func() { _, err := io.Copy(server, io.TeeReader(tee, &recorded)); copied <- err }()
85+ go func() { _, _ = io.Copy(tee, server) }()
86+ write := func(p []byte) {
87+ t.Helper()
88+ if _, err := client.Write(p); err != nil {
89+ t.Fatal(err)
90+ }
91+ }
92+ read := func(p []byte) {
93+ t.Helper()
94+ if _, err := io.ReadFull(client, p); err != nil {
95+ t.Fatal(err)
96+ }
97+ }
98+ // Independent reference encoder: no generated bindings participate in capture.
99+ write([]byte{'l', 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0})
100+ var setupHead [8]byte
101+ read(setupHead[:])
102+ if setupHead[0] != 1 {
103+ t.Fatalf("setup failed: %x", setupHead)
104+ }
105+ setup := make([]byte, int(binary.LittleEndian.Uint16(setupHead[6:]))*4)
106+ read(setup)
107+ vendorLen := int(binary.LittleEndian.Uint16(setup[16:]))
108+ formats := int(setup[21])
109+ screenOff := 32 + (vendorLen+3)&^3 + 8*formats
110+ root := binary.LittleEndian.Uint32(setup[screenOff:])
111+ // Query XFIXES dynamically rather than assuming a server-specific major opcode.
112+ query := []byte{98, 0, 4, 0, 6, 0, 0, 0, 'X', 'F', 'I', 'X', 'E', 'S', 0, 0}
113+ write(query)
114+ // Server bytes reach the client through the same tee; keep named replies as
115+ // reply goldens (reply byte 0 through 32+4*length).
116+ replies := map[string][]byte{}
117+ keep := func(name string, p []byte) []byte {
118+ t.Helper()
119+ if p[0] != 1 {
120+ t.Fatalf("%s: expected a reply, got %x", name, p)
121+ }
122+ replies[name] = p
123+ return p
124+ }
125+ packet := func() []byte {
126+ t.Helper()
127+ head := make([]byte, 32)
128+ read(head)
129+ if head[0] == 1 {
130+ tail := make([]byte, int(binary.LittleEndian.Uint32(head[4:]))*4)
131+ read(tail)
132+ head = append(head, tail...)
133+ }
134+ return head
135+ }
136+ ext := keep("xproto/QueryExtension", packet())
137+ if ext[0] != 1 || ext[8] != 1 {
138+ t.Fatalf("XFIXES unavailable: %x", ext)
139+ }
140+ major := ext[9]
141+ version := make([]byte, 12)
142+ version[0] = major
143+ version[2] = 3
144+ binary.LittleEndian.PutUint32(version[4:], 6)
145+ write(version)
146+ reply := keep("xfixes/QueryVersion", packet())
147+ if reply[0] != 1 {
148+ t.Fatalf("QueryVersion failed: %x", reply)
149+ }
150+ mapReq := make([]byte, 8)
151+ mapReq[0] = 8
152+ mapReq[2] = 2
153+ binary.LittleEndian.PutUint32(mapReq[4:], root)
154+ write(mapReq)
155+ geometry := append([]byte(nil), mapReq...)
156+ geometry[0] = 14
157+ write(geometry)
158+ geom := keep("xproto/GetGeometry", packet())
159+ if geom[0] != 1 || binary.LittleEndian.Uint16(geom[2:]) != 4 {
160+ t.Fatalf("MapWindow/GetGeometry rejected: %x", geom)
161+ }
162+
163+ // InternAtom, ChangeProperty and XRes QueryClientIds exercise derived lengths,
164+ // byte expressions and fixed-struct lists with independently assembled bytes.
165+ intern := []byte{16, 0, 4, 0, 7, 0, 0, 0, 'X', 'T', 'U', 'R', 'B', 'O', '_', 0}
166+ write(intern)
167+ atomReply := keep("xproto/InternAtom", packet())
168+ if atomReply[0] != 1 {
169+ t.Fatalf("InternAtom: %x", atomReply)
170+ }
171+ atom := binary.LittleEndian.Uint32(atomReply[8:])
172+ property := make([]byte, 28)
173+ property[0] = 18
174+ property[2] = 7
175+ binary.LittleEndian.PutUint32(property[4:], root)
176+ binary.LittleEndian.PutUint32(property[8:], atom)
177+ binary.LittleEndian.PutUint32(property[12:], 31)
178+ property[16] = 8
179+ binary.LittleEndian.PutUint32(property[20:], 3)
180+ copy(property[24:], "abc")
181+ write(property)
182+ // A successful GetProperty reply proves the preceding write was accepted.
183+ get := make([]byte, 24)
184+ get[0] = 20
185+ get[2] = 6
186+ binary.LittleEndian.PutUint32(get[4:], root)
187+ binary.LittleEndian.PutUint32(get[8:], atom)
188+ binary.LittleEndian.PutUint32(get[20:], 1)
189+ write(get)
190+ got := keep("xproto/GetProperty", packet())
191+ if got[0] != 1 || string(got[32:35]) != "abc" {
192+ t.Fatalf("ChangeProperty: %x", got)
193+ }
194+ resName := "X-Resource"
195+ queryRes := make([]byte, 20)
196+ queryRes[0] = 98
197+ queryRes[2] = 5
198+ queryRes[4] = byte(len(resName))
199+ copy(queryRes[8:], resName)
200+ write(queryRes)
201+ resExt := packet()
202+ if resExt[0] != 1 || resExt[8] != 1 {
203+ t.Fatalf("XRes unavailable: %x", resExt)
204+ }
205+ resMajor := resExt[9]
206+ resVersion := []byte{resMajor, 0, 2, 0, 1, 2, 0, 0}
207+ write(resVersion)
208+ if p := packet(); p[0] != 1 {
209+ t.Fatalf("XRes version: %x", p)
210+ }
211+ ids := make([]byte, 16)
212+ ids[0] = resMajor
213+ ids[1] = 4
214+ ids[2] = 4
215+ binary.LittleEndian.PutUint32(ids[4:], 1)
216+ binary.LittleEndian.PutUint32(ids[12:], 1)
217+ write(ids)
218+ keep("res/QueryClientIds", packet())
219+ // Value-list reference packets are assembled independently, including mask-bit
220+ // ordering. Two sibling windows allow all seven ConfigureWindow bits to be
221+ // exercised legally. A final reply fences all void requests and exposes errors.
222+ base := binary.LittleEndian.Uint32(setup[4:])
223+ child, sibling, picture := base|1, base|2, base|3
224+ create := func(id uint32, mask uint32, values ...uint32) {
225+ p := make([]byte, 32+4*len(values))
226+ p[0] = 1
227+ binary.LittleEndian.PutUint16(p[2:], uint16(len(p)/4))
228+ binary.LittleEndian.PutUint32(p[4:], id)
229+ binary.LittleEndian.PutUint32(p[8:], root)
230+ binary.LittleEndian.PutUint16(p[16:], 64)
231+ binary.LittleEndian.PutUint16(p[18:], 48)
232+ binary.LittleEndian.PutUint16(p[22:], 1) // InputOutput, inherited depth/visual
233+ binary.LittleEndian.PutUint32(p[28:], mask)
234+ for i, v := range values {
235+ binary.LittleEndian.PutUint32(p[32+4*i:], v)
236+ }
237+ write(p)
238+ }
239+ create(sibling, 0)
240+ create(child, 2|512|1024, 0x123456, 1, 0)
241+ change := make([]byte, 20)
242+ change[0], change[2] = 2, 5
243+ binary.LittleEndian.PutUint32(change[4:], child)
244+ binary.LittleEndian.PutUint32(change[8:], 512|1024)
245+ binary.LittleEndian.PutUint32(change[16:], 1) // false, true BOOL32 words
246+ write(change)
247+ configure := func(mask uint16, values ...uint32) {
248+ p := make([]byte, 12+4*len(values))
249+ p[0] = 12
250+ binary.LittleEndian.PutUint16(p[2:], uint16(len(p)/4))
251+ binary.LittleEndian.PutUint32(p[4:], child)
252+ binary.LittleEndian.PutUint16(p[8:], mask)
253+ for i, v := range values {
254+ binary.LittleEndian.PutUint32(p[12+4*i:], v)
255+ }
256+ write(p)
257+ }
258+ configure(0)
259+ configure(1|4|64, 0xfffffff9, 80, 0)
260+ configure(127, 0xfffffffd, 9, 96, 72, 2, sibling, 0)
261+ queryRender := []byte{98, 0, 4, 0, 6, 0, 0, 0, 'R', 'E', 'N', 'D', 'E', 'R', 0, 0}
262+ write(queryRender)
263+ renderExt := packet()
264+ if renderExt[0] != 1 || renderExt[8] != 1 {
265+ t.Fatalf("value requests/RENDER query: %x", renderExt)
266+ }
267+ renderMajor := renderExt[9]
268+ write([]byte{renderMajor, 1, 1, 0}) // QueryPictFormats
269+ formatsReply := keep("render/QueryPictFormats", packet())
270+ if formatsReply[0] != 1 {
271+ t.Fatalf("QueryPictFormats: %x", formatsReply)
272+ }
273+ // Match the root visual rather than guessing a server-assigned format ID.
274+ rootVisual := binary.LittleEndian.Uint32(setup[screenOff+32:])
275+ off := 32 + 28*int(binary.LittleEndian.Uint32(formatsReply[8:]))
276+ var format uint32
277+ for range binary.LittleEndian.Uint32(formatsReply[12:]) {
278+ depths := binary.LittleEndian.Uint32(formatsReply[off:])
279+ off += 8
280+ for range depths {
281+ visuals := binary.LittleEndian.Uint16(formatsReply[off+2:])
282+ off += 8
283+ for range visuals {
284+ if binary.LittleEndian.Uint32(formatsReply[off:]) == rootVisual {
285+ format = binary.LittleEndian.Uint32(formatsReply[off+4:])
286+ }
287+ off += 8
288+ }
289+ }
290+ }
291+ if format == 0 {
292+ t.Fatal("root visual has no Render format")
293+ }
294+ pic := make([]byte, 32)
295+ pic[0], pic[1], pic[2] = renderMajor, 4, 8
296+ binary.LittleEndian.PutUint32(pic[4:], picture)
297+ binary.LittleEndian.PutUint32(pic[8:], child)
298+ binary.LittleEndian.PutUint32(pic[12:], format)
299+ binary.LittleEndian.PutUint32(pic[16:], 1|16|32)
300+ binary.LittleEndian.PutUint32(pic[20:], 1) // RepeatNormal
301+ binary.LittleEndian.PutUint32(pic[24:], 0xfffffffb)
302+ binary.LittleEndian.PutUint32(pic[28:], 7)
303+ write(pic)
304+ write([]byte{43, 0, 1, 0}) // GetInputFocus fences CreatePicture
305+ if p := packet(); p[0] != 1 {
306+ t.Fatalf("CreatePicture rejected: %x", p)
307+ }
308+ // Reply-only captures. Opcodes 3, 15, 20 and 99 are not request goldens, so
309+ // the request-stream classifier below is unaffected.
310+ attrs := make([]byte, 8)
311+ attrs[0], attrs[2] = 3, 2
312+ binary.LittleEndian.PutUint32(attrs[4:], child)
313+ write(attrs)
314+ keep("xproto/GetWindowAttributes", packet())
315+ tree := make([]byte, 8)
316+ tree[0], tree[2] = 15, 2
317+ binary.LittleEndian.PutUint32(tree[4:], root)
318+ write(tree)
319+ keep("xproto/QueryTree", packet())
320+ // WM_NAME is unset on a fresh Xvfb root: a zero-length GetProperty reply.
321+ empty := make([]byte, 24)
322+ empty[0], empty[2] = 20, 6
323+ binary.LittleEndian.PutUint32(empty[4:], root)
324+ binary.LittleEndian.PutUint32(empty[8:], 39)
325+ binary.LittleEndian.PutUint32(empty[20:], 1)
326+ write(empty)
327+ keep("xproto/GetPropertyEmpty", packet())
328+ write([]byte{99, 0, 1, 0}) // ListExtensions: variable STR iterator
329+ keep("xproto/ListExtensions", packet())
330+ // Closing the client drains the request direction; wait before inspecting tee.
331+ client.Close()
332+ select {
333+ case err := <-copied:
334+ if err != nil {
335+ t.Fatal(err)
336+ }
337+ case <-time.After(5 * time.Second):
338+ t.Fatal("tee did not drain")
339+ }
340+ stream := recorded.Bytes()
341+ requests := stream[12:]
342+ captured := map[string][]byte{}
343+ configIndex := 0
344+ for len(requests) > 0 {
345+ size := int(binary.LittleEndian.Uint16(requests[2:])) * 4
346+ if size < 4 || size > len(requests) {
347+ t.Fatal("malformed capture")
348+ }
349+ p := requests[:size]
350+ switch p[0] {
351+ case 1:
352+ if binary.LittleEndian.Uint32(p[4:]) == child {
353+ captured["xproto/CreateWindow"] = p
354+ }
355+ case 2:
356+ captured["xproto/ChangeWindowAttributes"] = p
357+ case 12:
358+ names := []string{"ConfigureWindow", "ConfigureWindowSparse", "ConfigureWindowAll"}
359+ captured["xproto/"+names[configIndex]] = p
360+ configIndex++
361+ case 8:
362+ captured["xproto/MapWindow"] = p
363+ case 14:
364+ captured["xproto/GetGeometry"] = p
365+ case 16:
366+ captured["xproto/InternAtom"] = p
367+ case 18:
368+ captured["xproto/ChangeProperty"] = p
369+ default:
370+ if p[0] == renderMajor && p[1] == 4 {
371+ captured["render/CreatePicture"] = p
372+ }
373+ if p[0] == resMajor && p[1] == 4 {
374+ captured["res/QueryClientIds"] = p
375+ }
376+ if p[0] == major {
377+ captured["xfixes/QueryVersion"] = p
378+ }
379+ }
380+ requests = requests[size:]
381+ }
382+ if len(captured) != 12 || len(replies) != 11 {
383+ t.Fatal("missing captured requests or replies")
384+ }
385+ for name, p := range replies {
386+ path := filepath.Join("../../testdata/golden", name+".reply.hex")
387+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
388+ t.Fatal(err)
389+ }
390+ text := "# source: Xvfb reply via raw socketpair tee to an independent reference request\n# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace\n" + hex.EncodeToString(p) + "\n"
391+ if err := os.WriteFile(path, []byte(text), 0644); err != nil {
392+ t.Fatal(err)
393+ }
394+ }
395+ for name, p := range captured {
396+ path := filepath.Join("../../testdata/golden", name+".req.hex")
397+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
398+ t.Fatal(err)
399+ }
400+ text := "# source: Xvfb via raw socketpair tee; independent reference encoder; server accepted\n# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace\n" + hex.EncodeToString(p) + "\n"
401+ if err := os.WriteFile(path, []byte(text), 0644); err != nil {
402+ t.Fatal(err)
403+ }
404+ }
405+}
A internal/xtest/golden.go +22 -0
@@ -0,0 +1,22 @@
1+// Package xtest provides wire fixtures shared by binding tests.
2+package xtest
3+
4+import (
5+ "encoding/hex"
6+ "os"
7+ "strings"
8+)
9+
10+// LoadHex reads whitespace-separated hex with optional # comments.
11+func LoadHex(path string) ([]byte, error) {
12+ b, err := os.ReadFile(path)
13+ if err != nil {
14+ return nil, err
15+ }
16+ var text strings.Builder
17+ for line := range strings.SplitSeq(string(b), "\n") {
18+ line, _, _ = strings.Cut(line, "#")
19+ text.WriteString(strings.Join(strings.Fields(line), ""))
20+ }
21+ return hex.DecodeString(text.String())
22+}
@@ -0,0 +1,22 @@
1+// Package xtest provides wire fixtures shared by binding tests.
2+package xtest
3+
4+import (
5+ "encoding/hex"
6+ "os"
7+ "strings"
8+)
9+
10+// LoadHex reads whitespace-separated hex with optional # comments.
11+func LoadHex(path string) ([]byte, error) {
12+ b, err := os.ReadFile(path)
13+ if err != nil {
14+ return nil, err
15+ }
16+ var text strings.Builder
17+ for line := range strings.SplitSeq(string(b), "\n") {
18+ line, _, _ = strings.Cut(line, "#")
19+ text.WriteString(strings.Join(strings.Fields(line), ""))
20+ }
21+ return hex.DecodeString(text.String())
22+}
A lists_test.go +87 -0
@@ -0,0 +1,87 @@
1+package xturbo_test
2+
3+import (
4+ "bytes"
5+ "encoding/binary"
6+ "strings"
7+ "testing"
8+
9+ "bnema.dev/xturbo"
10+ "bnema.dev/xturbo/internal/xtest"
11+ "bnema.dev/xturbo/render"
12+ "bnema.dev/xturbo/res"
13+ "bnema.dev/xturbo/xproto"
14+)
15+
16+func TestListGoldens(t *testing.T) {
17+ for _, name := range []string{"xproto/InternAtom", "xproto/ChangeProperty", "res/QueryClientIds"} {
18+ t.Run(name, func(t *testing.T) {
19+ want, err := xtest.LoadHex("testdata/golden/" + name + ".req.hex")
20+ if err != nil {
21+ t.Fatal(err)
22+ }
23+ b := xturbo.NewBuf(0)
24+ switch name {
25+ case "xproto/InternAtom":
26+ xproto.InternAtom(b, false, "XTURBO_")
27+ case "xproto/ChangeProperty":
28+ xproto.ChangeProperty(b, xproto.PropModeReplace, xproto.Window(binary.LittleEndian.Uint32(want[4:])), xproto.Atom(binary.LittleEndian.Uint32(want[8:])), xproto.AtomString, 8, 3, []byte("abc"))
29+ case "res/QueryClientIds":
30+ var ext xturbo.ExtTable
31+ ext.Set(xturbo.ExtRes, want[0], 0, 0)
32+ b.Ext = &ext
33+ res.QueryClientIds(b, []res.ClientIdSpec{{Client: 0, Mask: res.ClientIdMaskClientXID}})
34+ }
35+ if b.Err() != nil || !bytes.Equal(b.Bytes(), want) {
36+ t.Fatalf("err %v got %x want %x", b.Err(), b.Bytes(), want)
37+ }
38+ })
39+ }
40+}
41+func TestListLengths(t *testing.T) {
42+ b := xturbo.NewBuf(0)
43+ if xproto.ChangeProperty(b, 0, 1, 1, 1, 16, 2, []byte{1, 2}) != 0 || b.Err() != xturbo.ErrBadLength || b.Len() != 0 {
44+ t.Fatal("expression length mismatch")
45+ }
46+ b = xturbo.NewBuf(0)
47+ if xproto.InternAtom(b, false, strings.Repeat("x", 65536)) != 0 || b.Err() != xturbo.ErrBadLength {
48+ t.Fatal("derived length truncation")
49+ }
50+ b = xturbo.NewBuf(0)
51+ xproto.SetFontPath(b, []xproto.Str{{NameLen: 3, Name: "abc"}, {NameLen: 1, Name: "d"}})
52+ want := []byte{51, 0, 4, 0, 2, 0, 0, 0, 3, 'a', 'b', 'c', 1, 'd', 0, 0}
53+ if b.Err() != nil || !bytes.Equal(b.Bytes(), want) {
54+ t.Fatalf("variable structs: %x err %v", b.Bytes(), b.Err())
55+ }
56+ b = xturbo.NewBuf(0)
57+ xproto.QueryTextExtents(b, 1, []xproto.Char2b{{Byte1: 0, Byte2: 'a'}})
58+ if b.Bytes()[1] != 1 {
59+ t.Fatal("implicit list length exprfield")
60+ }
61+ var ext xturbo.ExtTable
62+ ext.Set(xturbo.ExtRender, 140, 0, 0)
63+ b = xturbo.NewBuf(0)
64+ b.Ext = &ext
65+ if render.AddGlyphs(b, 1, 2, []uint32{1}, nil, nil) != 0 || b.Err() != xturbo.ErrBadLength {
66+ t.Fatal("shared length must remain explicit")
67+ }
68+}
69+func TestAllocListRequests(t *testing.T) {
70+ b := xturbo.NewBuf(0)
71+ data := make([]byte, 4096)
72+ specs := []res.ClientIdSpec{{Client: 1, Mask: res.ClientIdMaskClientXID}}
73+ var ext xturbo.ExtTable
74+ ext.Set(xturbo.ExtRes, 141, 0, 0)
75+ b.Ext = &ext
76+ if n := testing.AllocsPerRun(1000, func() {
77+ b.Reset()
78+ xproto.InternAtom(b, false, "WM_CLASS")
79+ xproto.ChangeProperty(b, 0, 1, 1, 1, 8, 4096, data)
80+ res.QueryClientIds(b, specs)
81+ }); n != 0 {
82+ t.Fatal(n)
83+ }
84+ if b.Err() != nil {
85+ t.Fatal(b.Err())
86+ }
87+}
@@ -0,0 +1,87 @@
1+package xturbo_test
2+
3+import (
4+ "bytes"
5+ "encoding/binary"
6+ "strings"
7+ "testing"
8+
9+ "bnema.dev/xturbo"
10+ "bnema.dev/xturbo/internal/xtest"
11+ "bnema.dev/xturbo/render"
12+ "bnema.dev/xturbo/res"
13+ "bnema.dev/xturbo/xproto"
14+)
15+
16+func TestListGoldens(t *testing.T) {
17+ for _, name := range []string{"xproto/InternAtom", "xproto/ChangeProperty", "res/QueryClientIds"} {
18+ t.Run(name, func(t *testing.T) {
19+ want, err := xtest.LoadHex("testdata/golden/" + name + ".req.hex")
20+ if err != nil {
21+ t.Fatal(err)
22+ }
23+ b := xturbo.NewBuf(0)
24+ switch name {
25+ case "xproto/InternAtom":
26+ xproto.InternAtom(b, false, "XTURBO_")
27+ case "xproto/ChangeProperty":
28+ xproto.ChangeProperty(b, xproto.PropModeReplace, xproto.Window(binary.LittleEndian.Uint32(want[4:])), xproto.Atom(binary.LittleEndian.Uint32(want[8:])), xproto.AtomString, 8, 3, []byte("abc"))
29+ case "res/QueryClientIds":
30+ var ext xturbo.ExtTable
31+ ext.Set(xturbo.ExtRes, want[0], 0, 0)
32+ b.Ext = &ext
33+ res.QueryClientIds(b, []res.ClientIdSpec{{Client: 0, Mask: res.ClientIdMaskClientXID}})
34+ }
35+ if b.Err() != nil || !bytes.Equal(b.Bytes(), want) {
36+ t.Fatalf("err %v got %x want %x", b.Err(), b.Bytes(), want)
37+ }
38+ })
39+ }
40+}
41+func TestListLengths(t *testing.T) {
42+ b := xturbo.NewBuf(0)
43+ if xproto.ChangeProperty(b, 0, 1, 1, 1, 16, 2, []byte{1, 2}) != 0 || b.Err() != xturbo.ErrBadLength || b.Len() != 0 {
44+ t.Fatal("expression length mismatch")
45+ }
46+ b = xturbo.NewBuf(0)
47+ if xproto.InternAtom(b, false, strings.Repeat("x", 65536)) != 0 || b.Err() != xturbo.ErrBadLength {
48+ t.Fatal("derived length truncation")
49+ }
50+ b = xturbo.NewBuf(0)
51+ xproto.SetFontPath(b, []xproto.Str{{NameLen: 3, Name: "abc"}, {NameLen: 1, Name: "d"}})
52+ want := []byte{51, 0, 4, 0, 2, 0, 0, 0, 3, 'a', 'b', 'c', 1, 'd', 0, 0}
53+ if b.Err() != nil || !bytes.Equal(b.Bytes(), want) {
54+ t.Fatalf("variable structs: %x err %v", b.Bytes(), b.Err())
55+ }
56+ b = xturbo.NewBuf(0)
57+ xproto.QueryTextExtents(b, 1, []xproto.Char2b{{Byte1: 0, Byte2: 'a'}})
58+ if b.Bytes()[1] != 1 {
59+ t.Fatal("implicit list length exprfield")
60+ }
61+ var ext xturbo.ExtTable
62+ ext.Set(xturbo.ExtRender, 140, 0, 0)
63+ b = xturbo.NewBuf(0)
64+ b.Ext = &ext
65+ if render.AddGlyphs(b, 1, 2, []uint32{1}, nil, nil) != 0 || b.Err() != xturbo.ErrBadLength {
66+ t.Fatal("shared length must remain explicit")
67+ }
68+}
69+func TestAllocListRequests(t *testing.T) {
70+ b := xturbo.NewBuf(0)
71+ data := make([]byte, 4096)
72+ specs := []res.ClientIdSpec{{Client: 1, Mask: res.ClientIdMaskClientXID}}
73+ var ext xturbo.ExtTable
74+ ext.Set(xturbo.ExtRes, 141, 0, 0)
75+ b.Ext = &ext
76+ if n := testing.AllocsPerRun(1000, func() {
77+ b.Reset()
78+ xproto.InternAtom(b, false, "WM_CLASS")
79+ xproto.ChangeProperty(b, 0, 1, 1, 1, 8, 4096, data)
80+ res.QueryClientIds(b, specs)
81+ }); n != 0 {
82+ t.Fatal(n)
83+ }
84+ if b.Err() != nil {
85+ t.Fatal(b.Err())
86+ }
87+}
A render/replies_fuzz_test.go +135 -0
@@ -0,0 +1,135 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "testing"
5+import "bnema.dev/xturbo/internal/xtest"
6+
7+func FuzzParseQueryVersionReply(f *testing.F) {
8+ f.Add(make([]byte, 32))
9+ f.Fuzz(func(t *testing.T, raw []byte) {
10+ r, ok := ParseQueryVersionReply(raw)
11+ if !ok {
12+ if len(raw) >= 32 {
13+ t.Fatal("rejected a complete fixed-size reply")
14+ }
15+ return
16+ }
17+ if len(r.Raw()) != len(raw) {
18+ t.Fatal("Raw must return the parsed bytes")
19+ }
20+ _ = r.MajorVersion()
21+ _ = r.MinorVersion()
22+ })
23+}
24+
25+func FuzzParseQueryPictFormatsReply(f *testing.F) {
26+ f.Add(make([]byte, 32))
27+ if raw, err := xtest.LoadHex("../testdata/golden/render/QueryPictFormats.reply.hex"); err != nil {
28+ f.Fatal(err)
29+ } else {
30+ f.Add(raw)
31+ }
32+ f.Fuzz(func(t *testing.T, raw []byte) {
33+ r, ok := ParseQueryPictFormatsReply(raw)
34+ if !ok {
35+ return
36+ }
37+ if len(r.Raw()) != len(raw) {
38+ t.Fatal("Raw must return the parsed bytes")
39+ }
40+ _ = r.NumFormats()
41+ _ = r.NumScreens()
42+ _ = r.NumDepths()
43+ _ = r.NumVisuals()
44+ _ = r.NumSubpixel()
45+ {
46+ l0 := r.Formats()
47+ for i := 0; i < l0.Len(); i++ {
48+ _ = l0.At(i)
49+ }
50+ }
51+ {
52+ it0 := r.Screens()
53+ for it0.Next() {
54+ _ = it0.Value().NumDepths()
55+ _ = it0.Value().Fallback()
56+ {
57+ it1 := it0.Value().Depths()
58+ for it1.Next() {
59+ _ = it1.Value().Depth()
60+ _ = it1.Value().NumVisuals()
61+ {
62+ l2 := it1.Value().Visuals()
63+ for i := 0; i < l2.Len(); i++ {
64+ _ = l2.At(i)
65+ }
66+ }
67+ }
68+ if it1.Err() {
69+ t.Fatal("iterator error after successful Parse")
70+ }
71+ }
72+ }
73+ if it0.Err() {
74+ t.Fatal("iterator error after successful Parse")
75+ }
76+ }
77+ {
78+ l0 := r.Subpixels()
79+ for i := 0; i < l0.Len(); i++ {
80+ _ = l0.At(i)
81+ }
82+ }
83+ })
84+}
85+
86+func FuzzParseQueryPictIndexValuesReply(f *testing.F) {
87+ f.Add(make([]byte, 32))
88+ f.Fuzz(func(t *testing.T, raw []byte) {
89+ r, ok := ParseQueryPictIndexValuesReply(raw)
90+ if !ok {
91+ return
92+ }
93+ if len(r.Raw()) != len(raw) {
94+ t.Fatal("Raw must return the parsed bytes")
95+ }
96+ _ = r.NumValues()
97+ {
98+ l0 := r.Values()
99+ for i := 0; i < l0.Len(); i++ {
100+ _ = l0.At(i)
101+ }
102+ }
103+ })
104+}
105+
106+func FuzzParseQueryFiltersReply(f *testing.F) {
107+ f.Add(make([]byte, 32))
108+ f.Fuzz(func(t *testing.T, raw []byte) {
109+ r, ok := ParseQueryFiltersReply(raw)
110+ if !ok {
111+ return
112+ }
113+ if len(r.Raw()) != len(raw) {
114+ t.Fatal("Raw must return the parsed bytes")
115+ }
116+ _ = r.NumAliases()
117+ _ = r.NumFilters()
118+ {
119+ l0 := r.Aliases()
120+ for i := 0; i < l0.Len(); i++ {
121+ _ = l0.At(i)
122+ }
123+ }
124+ {
125+ it0 := r.Filters()
126+ for it0.Next() {
127+ _ = it0.Value().NameLen()
128+ _ = len(it0.Value().Name())
129+ }
130+ if it0.Err() {
131+ t.Fatal("iterator error after successful Parse")
132+ }
133+ }
134+ })
135+}
@@ -0,0 +1,135 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "testing"
5+import "bnema.dev/xturbo/internal/xtest"
6+
7+func FuzzParseQueryVersionReply(f *testing.F) {
8+ f.Add(make([]byte, 32))
9+ f.Fuzz(func(t *testing.T, raw []byte) {
10+ r, ok := ParseQueryVersionReply(raw)
11+ if !ok {
12+ if len(raw) >= 32 {
13+ t.Fatal("rejected a complete fixed-size reply")
14+ }
15+ return
16+ }
17+ if len(r.Raw()) != len(raw) {
18+ t.Fatal("Raw must return the parsed bytes")
19+ }
20+ _ = r.MajorVersion()
21+ _ = r.MinorVersion()
22+ })
23+}
24+
25+func FuzzParseQueryPictFormatsReply(f *testing.F) {
26+ f.Add(make([]byte, 32))
27+ if raw, err := xtest.LoadHex("../testdata/golden/render/QueryPictFormats.reply.hex"); err != nil {
28+ f.Fatal(err)
29+ } else {
30+ f.Add(raw)
31+ }
32+ f.Fuzz(func(t *testing.T, raw []byte) {
33+ r, ok := ParseQueryPictFormatsReply(raw)
34+ if !ok {
35+ return
36+ }
37+ if len(r.Raw()) != len(raw) {
38+ t.Fatal("Raw must return the parsed bytes")
39+ }
40+ _ = r.NumFormats()
41+ _ = r.NumScreens()
42+ _ = r.NumDepths()
43+ _ = r.NumVisuals()
44+ _ = r.NumSubpixel()
45+ {
46+ l0 := r.Formats()
47+ for i := 0; i < l0.Len(); i++ {
48+ _ = l0.At(i)
49+ }
50+ }
51+ {
52+ it0 := r.Screens()
53+ for it0.Next() {
54+ _ = it0.Value().NumDepths()
55+ _ = it0.Value().Fallback()
56+ {
57+ it1 := it0.Value().Depths()
58+ for it1.Next() {
59+ _ = it1.Value().Depth()
60+ _ = it1.Value().NumVisuals()
61+ {
62+ l2 := it1.Value().Visuals()
63+ for i := 0; i < l2.Len(); i++ {
64+ _ = l2.At(i)
65+ }
66+ }
67+ }
68+ if it1.Err() {
69+ t.Fatal("iterator error after successful Parse")
70+ }
71+ }
72+ }
73+ if it0.Err() {
74+ t.Fatal("iterator error after successful Parse")
75+ }
76+ }
77+ {
78+ l0 := r.Subpixels()
79+ for i := 0; i < l0.Len(); i++ {
80+ _ = l0.At(i)
81+ }
82+ }
83+ })
84+}
85+
86+func FuzzParseQueryPictIndexValuesReply(f *testing.F) {
87+ f.Add(make([]byte, 32))
88+ f.Fuzz(func(t *testing.T, raw []byte) {
89+ r, ok := ParseQueryPictIndexValuesReply(raw)
90+ if !ok {
91+ return
92+ }
93+ if len(r.Raw()) != len(raw) {
94+ t.Fatal("Raw must return the parsed bytes")
95+ }
96+ _ = r.NumValues()
97+ {
98+ l0 := r.Values()
99+ for i := 0; i < l0.Len(); i++ {
100+ _ = l0.At(i)
101+ }
102+ }
103+ })
104+}
105+
106+func FuzzParseQueryFiltersReply(f *testing.F) {
107+ f.Add(make([]byte, 32))
108+ f.Fuzz(func(t *testing.T, raw []byte) {
109+ r, ok := ParseQueryFiltersReply(raw)
110+ if !ok {
111+ return
112+ }
113+ if len(r.Raw()) != len(raw) {
114+ t.Fatal("Raw must return the parsed bytes")
115+ }
116+ _ = r.NumAliases()
117+ _ = r.NumFilters()
118+ {
119+ l0 := r.Aliases()
120+ for i := 0; i < l0.Len(); i++ {
121+ _ = l0.At(i)
122+ }
123+ }
124+ {
125+ it0 := r.Filters()
126+ for it0.Next() {
127+ _ = it0.Value().NameLen()
128+ _ = len(it0.Value().Name())
129+ }
130+ if it0.Err() {
131+ t.Fatal("iterator error after successful Parse")
132+ }
133+ }
134+ })
135+}
A render/replies_generated.go +380 -0
@@ -0,0 +1,380 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
8+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
9+type QueryVersionReply struct{ raw []byte }
10+
11+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
12+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
13+ if len(raw) < 32 {
14+ return QueryVersionReply{}, false
15+ }
16+ r := QueryVersionReply{raw: raw}
17+ return r, true
18+}
19+func (r QueryVersionReply) Raw() []byte { return r.raw }
20+func (r QueryVersionReply) MajorVersion() uint32 {
21+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
22+}
23+func (r QueryVersionReply) MinorVersion() uint32 {
24+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
25+}
26+
27+// QueryPictFormatsReply is a zero-copy view over a QueryPictFormats reply; offsets count from reply byte 0.
28+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
29+type QueryPictFormatsReply struct{ raw []byte }
30+
31+// ParseQueryPictFormatsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
32+func ParseQueryPictFormatsReply(raw []byte) (QueryPictFormatsReply, bool) {
33+ if len(raw) < 32 {
34+ return QueryPictFormatsReply{}, false
35+ }
36+ r := QueryPictFormatsReply{raw: raw}
37+ off := 32
38+ {
39+ n := int(r.NumFormats())
40+ if n < 0 || n > len(r.raw) {
41+ return QueryPictFormatsReply{}, false
42+ }
43+ off += n * 28
44+ }
45+ if off > len(r.raw) {
46+ return QueryPictFormatsReply{}, false
47+ }
48+ {
49+ n := int(r.NumScreens())
50+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
51+ return QueryPictFormatsReply{}, false
52+ }
53+ it := NewPictscreenIter(r.raw[off:], n)
54+ for it.Next() {
55+ off += it.Value().Size()
56+ }
57+ if it.Err() {
58+ return QueryPictFormatsReply{}, false
59+ }
60+ }
61+ {
62+ n := int(r.NumSubpixel())
63+ if n < 0 || n > len(r.raw) {
64+ return QueryPictFormatsReply{}, false
65+ }
66+ off += n * 4
67+ }
68+ if off > len(r.raw) {
69+ return QueryPictFormatsReply{}, false
70+ }
71+ if off > len(r.raw) {
72+ return QueryPictFormatsReply{}, false
73+ }
74+ return r, true
75+}
76+func (r QueryPictFormatsReply) Raw() []byte { return r.raw }
77+func (r QueryPictFormatsReply) NumFormats() uint32 {
78+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
79+}
80+func (r QueryPictFormatsReply) NumScreens() uint32 {
81+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
82+}
83+func (r QueryPictFormatsReply) NumDepths() uint32 {
84+ return uint32(binary.LittleEndian.Uint32(r.raw[16:]))
85+}
86+func (r QueryPictFormatsReply) NumVisuals() uint32 {
87+ return uint32(binary.LittleEndian.Uint32(r.raw[20:]))
88+}
89+func (r QueryPictFormatsReply) NumSubpixel() uint32 {
90+ return uint32(binary.LittleEndian.Uint32(r.raw[24:]))
91+}
92+func (r QueryPictFormatsReply) Formats() PictforminfoList {
93+ off := 32
94+ end := off + (int(r.NumFormats()))*28
95+ return PictforminfoList{b: r.raw[off:end:end]}
96+}
97+func (r QueryPictFormatsReply) Screens() PictscreenIter {
98+ off := 32
99+ {
100+ n := int(r.NumFormats())
101+ off += n * 28
102+ }
103+ return NewPictscreenIter(r.raw[off:], int(r.NumScreens()))
104+}
105+func (r QueryPictFormatsReply) Subpixels() SubPixelList {
106+ off := 32
107+ {
108+ n := int(r.NumFormats())
109+ off += n * 28
110+ }
111+ {
112+ n := int(r.NumScreens())
113+ it := NewPictscreenIter(r.raw[off:], n)
114+ for it.Next() {
115+ off += it.Value().Size()
116+ }
117+ }
118+ end := off + (int(r.NumSubpixel()))*4
119+ return SubPixelList{b: r.raw[off:end:end]}
120+}
121+
122+// QueryPictIndexValuesReply is a zero-copy view over a QueryPictIndexValues reply; offsets count from reply byte 0.
123+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
124+type QueryPictIndexValuesReply struct{ raw []byte }
125+
126+// ParseQueryPictIndexValuesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
127+func ParseQueryPictIndexValuesReply(raw []byte) (QueryPictIndexValuesReply, bool) {
128+ if len(raw) < 32 {
129+ return QueryPictIndexValuesReply{}, false
130+ }
131+ r := QueryPictIndexValuesReply{raw: raw}
132+ off := 32
133+ {
134+ n := int(r.NumValues())
135+ if n < 0 || n > len(r.raw) {
136+ return QueryPictIndexValuesReply{}, false
137+ }
138+ off += n * 12
139+ }
140+ if off > len(r.raw) {
141+ return QueryPictIndexValuesReply{}, false
142+ }
143+ if off > len(r.raw) {
144+ return QueryPictIndexValuesReply{}, false
145+ }
146+ return r, true
147+}
148+func (r QueryPictIndexValuesReply) Raw() []byte { return r.raw }
149+func (r QueryPictIndexValuesReply) NumValues() uint32 {
150+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
151+}
152+func (r QueryPictIndexValuesReply) Values() IndexvalueList {
153+ off := 32
154+ end := off + (int(r.NumValues()))*12
155+ return IndexvalueList{b: r.raw[off:end:end]}
156+}
157+
158+// QueryFiltersReply is a zero-copy view over a QueryFilters reply; offsets count from reply byte 0.
159+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
160+type QueryFiltersReply struct{ raw []byte }
161+
162+// ParseQueryFiltersReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
163+func ParseQueryFiltersReply(raw []byte) (QueryFiltersReply, bool) {
164+ if len(raw) < 32 {
165+ return QueryFiltersReply{}, false
166+ }
167+ r := QueryFiltersReply{raw: raw}
168+ off := 32
169+ {
170+ n := int(r.NumAliases())
171+ if n < 0 || n > len(r.raw) {
172+ return QueryFiltersReply{}, false
173+ }
174+ off += n * 2
175+ }
176+ if off > len(r.raw) {
177+ return QueryFiltersReply{}, false
178+ }
179+ {
180+ n := int(r.NumFilters())
181+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
182+ return QueryFiltersReply{}, false
183+ }
184+ it := xproto.NewStrIter(r.raw[off:], n)
185+ for it.Next() {
186+ off += it.Value().Size()
187+ }
188+ if it.Err() {
189+ return QueryFiltersReply{}, false
190+ }
191+ }
192+ if off > len(r.raw) {
193+ return QueryFiltersReply{}, false
194+ }
195+ return r, true
196+}
197+func (r QueryFiltersReply) Raw() []byte { return r.raw }
198+func (r QueryFiltersReply) NumAliases() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[8:])) }
199+func (r QueryFiltersReply) NumFilters() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[12:])) }
200+func (r QueryFiltersReply) Aliases() Uint16List {
201+ off := 32
202+ end := off + (int(r.NumAliases()))*2
203+ return Uint16List{b: r.raw[off:end:end]}
204+}
205+func (r QueryFiltersReply) Filters() xproto.StrIter {
206+ off := 32
207+ {
208+ n := int(r.NumAliases())
209+ off += n * 2
210+ }
211+ return xproto.NewStrIter(r.raw[off:], int(r.NumFilters()))
212+}
213+
214+// PictdepthView is a zero-copy view over one wire-encoded Pictdepth, yielded by PictdepthIter.
215+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
216+type PictdepthView struct{ raw []byte }
217+
218+func (v PictdepthView) Raw() []byte { return v.raw }
219+
220+// Size is the element's wire size, including trailing alignment.
221+func (v PictdepthView) Size() int { return len(v.raw) }
222+func sizePictdepth(p []byte) int {
223+ if len(p) < 8 {
224+ return -1
225+ }
226+ v := PictdepthView{raw: p}
227+ _ = v
228+ off := 8
229+ {
230+ n := int(v.NumVisuals())
231+ if n < 0 || n > len(v.raw) {
232+ return -1
233+ }
234+ off += n * 8
235+ }
236+ if off > len(v.raw) {
237+ return -1
238+ }
239+ if off > len(v.raw) {
240+ return -1
241+ }
242+ return off
243+}
244+func (v PictdepthView) Depth() uint8 { return uint8(v.raw[0:][0]) }
245+func (v PictdepthView) NumVisuals() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[2:])) }
246+func (v PictdepthView) Visuals() PictvisualList {
247+ off := 8
248+ end := off + (int(v.NumVisuals()))*8
249+ return PictvisualList{b: v.raw[off:end:end]}
250+}
251+
252+// PictdepthIter walks n consecutive Pictdepth elements; Err reports a malformed length.
253+// It shares the lifetime of the bytes it walks.
254+type PictdepthIter struct {
255+ b []byte
256+ n int
257+ v PictdepthView
258+ err bool
259+}
260+
261+// NewPictdepthIter iterates n elements encoded at the start of b.
262+func NewPictdepthIter(b []byte, n int) PictdepthIter { return PictdepthIter{b: b, n: n} }
263+func (it *PictdepthIter) Next() bool {
264+ if it.n <= 0 || it.err {
265+ return false
266+ }
267+ s := sizePictdepth(it.b)
268+ if s < 0 {
269+ it.err = true
270+ it.n = 0
271+ return false
272+ }
273+ it.v = PictdepthView{raw: it.b[:s:s]}
274+ it.b = it.b[s:]
275+ it.n--
276+ return true
277+}
278+func (it *PictdepthIter) Value() PictdepthView { return it.v }
279+func (it *PictdepthIter) Err() bool { return it.err }
280+
281+// PictscreenView is a zero-copy view over one wire-encoded Pictscreen, yielded by PictscreenIter.
282+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
283+type PictscreenView struct{ raw []byte }
284+
285+func (v PictscreenView) Raw() []byte { return v.raw }
286+
287+// Size is the element's wire size, including trailing alignment.
288+func (v PictscreenView) Size() int { return len(v.raw) }
289+func sizePictscreen(p []byte) int {
290+ if len(p) < 8 {
291+ return -1
292+ }
293+ v := PictscreenView{raw: p}
294+ _ = v
295+ off := 8
296+ {
297+ n := int(v.NumDepths())
298+ if off > len(v.raw) || n < 0 || n > len(v.raw) {
299+ return -1
300+ }
301+ it := NewPictdepthIter(v.raw[off:], n)
302+ for it.Next() {
303+ off += it.Value().Size()
304+ }
305+ if it.Err() {
306+ return -1
307+ }
308+ }
309+ if off > len(v.raw) {
310+ return -1
311+ }
312+ return off
313+}
314+func (v PictscreenView) NumDepths() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[0:])) }
315+func (v PictscreenView) Fallback() Pictformat {
316+ return Pictformat(binary.LittleEndian.Uint32(v.raw[4:]))
317+}
318+func (v PictscreenView) Depths() PictdepthIter {
319+ off := 8
320+ return NewPictdepthIter(v.raw[off:], int(v.NumDepths()))
321+}
322+
323+// PictscreenIter walks n consecutive Pictscreen elements; Err reports a malformed length.
324+// It shares the lifetime of the bytes it walks.
325+type PictscreenIter struct {
326+ b []byte
327+ n int
328+ v PictscreenView
329+ err bool
330+}
331+
332+// NewPictscreenIter iterates n elements encoded at the start of b.
333+func NewPictscreenIter(b []byte, n int) PictscreenIter { return PictscreenIter{b: b, n: n} }
334+func (it *PictscreenIter) Next() bool {
335+ if it.n <= 0 || it.err {
336+ return false
337+ }
338+ s := sizePictscreen(it.b)
339+ if s < 0 {
340+ it.err = true
341+ it.n = 0
342+ return false
343+ }
344+ it.v = PictscreenView{raw: it.b[:s:s]}
345+ it.b = it.b[s:]
346+ it.n--
347+ return true
348+}
349+func (it *PictscreenIter) Value() PictscreenView { return it.v }
350+func (it *PictscreenIter) Err() bool { return it.err }
351+
352+// IndexvalueList is a borrowed view over a wire list of Indexvalue. It shares the lifetime of the bytes it was taken from; copy what you keep.
353+type IndexvalueList struct{ b []byte }
354+
355+func (l IndexvalueList) Len() int { return len(l.b) / 12 }
356+func (l IndexvalueList) At(i int) Indexvalue { return GetIndexvalue(l.b[i*12:]) }
357+
358+// PictforminfoList is a borrowed view over a wire list of Pictforminfo. It shares the lifetime of the bytes it was taken from; copy what you keep.
359+type PictforminfoList struct{ b []byte }
360+
361+func (l PictforminfoList) Len() int { return len(l.b) / 28 }
362+func (l PictforminfoList) At(i int) Pictforminfo { return GetPictforminfo(l.b[i*28:]) }
363+
364+// PictvisualList is a borrowed view over a wire list of Pictvisual. It shares the lifetime of the bytes it was taken from; copy what you keep.
365+type PictvisualList struct{ b []byte }
366+
367+func (l PictvisualList) Len() int { return len(l.b) / 8 }
368+func (l PictvisualList) At(i int) Pictvisual { return GetPictvisual(l.b[i*8:]) }
369+
370+// SubPixelList is a borrowed view over a wire list of SubPixel. It shares the lifetime of the bytes it was taken from; copy what you keep.
371+type SubPixelList struct{ b []byte }
372+
373+func (l SubPixelList) Len() int { return len(l.b) / 4 }
374+func (l SubPixelList) At(i int) SubPixel { return SubPixel(binary.LittleEndian.Uint32(l.b[i*4:])) }
375+
376+// Uint16List is a borrowed view over a wire list of uint16. It shares the lifetime of the bytes it was taken from; copy what you keep.
377+type Uint16List struct{ b []byte }
378+
379+func (l Uint16List) Len() int { return len(l.b) / 2 }
380+func (l Uint16List) At(i int) uint16 { return uint16(binary.LittleEndian.Uint16(l.b[i*2:])) }
@@ -0,0 +1,380 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
8+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
9+type QueryVersionReply struct{ raw []byte }
10+
11+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
12+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
13+ if len(raw) < 32 {
14+ return QueryVersionReply{}, false
15+ }
16+ r := QueryVersionReply{raw: raw}
17+ return r, true
18+}
19+func (r QueryVersionReply) Raw() []byte { return r.raw }
20+func (r QueryVersionReply) MajorVersion() uint32 {
21+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
22+}
23+func (r QueryVersionReply) MinorVersion() uint32 {
24+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
25+}
26+
27+// QueryPictFormatsReply is a zero-copy view over a QueryPictFormats reply; offsets count from reply byte 0.
28+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
29+type QueryPictFormatsReply struct{ raw []byte }
30+
31+// ParseQueryPictFormatsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
32+func ParseQueryPictFormatsReply(raw []byte) (QueryPictFormatsReply, bool) {
33+ if len(raw) < 32 {
34+ return QueryPictFormatsReply{}, false
35+ }
36+ r := QueryPictFormatsReply{raw: raw}
37+ off := 32
38+ {
39+ n := int(r.NumFormats())
40+ if n < 0 || n > len(r.raw) {
41+ return QueryPictFormatsReply{}, false
42+ }
43+ off += n * 28
44+ }
45+ if off > len(r.raw) {
46+ return QueryPictFormatsReply{}, false
47+ }
48+ {
49+ n := int(r.NumScreens())
50+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
51+ return QueryPictFormatsReply{}, false
52+ }
53+ it := NewPictscreenIter(r.raw[off:], n)
54+ for it.Next() {
55+ off += it.Value().Size()
56+ }
57+ if it.Err() {
58+ return QueryPictFormatsReply{}, false
59+ }
60+ }
61+ {
62+ n := int(r.NumSubpixel())
63+ if n < 0 || n > len(r.raw) {
64+ return QueryPictFormatsReply{}, false
65+ }
66+ off += n * 4
67+ }
68+ if off > len(r.raw) {
69+ return QueryPictFormatsReply{}, false
70+ }
71+ if off > len(r.raw) {
72+ return QueryPictFormatsReply{}, false
73+ }
74+ return r, true
75+}
76+func (r QueryPictFormatsReply) Raw() []byte { return r.raw }
77+func (r QueryPictFormatsReply) NumFormats() uint32 {
78+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
79+}
80+func (r QueryPictFormatsReply) NumScreens() uint32 {
81+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
82+}
83+func (r QueryPictFormatsReply) NumDepths() uint32 {
84+ return uint32(binary.LittleEndian.Uint32(r.raw[16:]))
85+}
86+func (r QueryPictFormatsReply) NumVisuals() uint32 {
87+ return uint32(binary.LittleEndian.Uint32(r.raw[20:]))
88+}
89+func (r QueryPictFormatsReply) NumSubpixel() uint32 {
90+ return uint32(binary.LittleEndian.Uint32(r.raw[24:]))
91+}
92+func (r QueryPictFormatsReply) Formats() PictforminfoList {
93+ off := 32
94+ end := off + (int(r.NumFormats()))*28
95+ return PictforminfoList{b: r.raw[off:end:end]}
96+}
97+func (r QueryPictFormatsReply) Screens() PictscreenIter {
98+ off := 32
99+ {
100+ n := int(r.NumFormats())
101+ off += n * 28
102+ }
103+ return NewPictscreenIter(r.raw[off:], int(r.NumScreens()))
104+}
105+func (r QueryPictFormatsReply) Subpixels() SubPixelList {
106+ off := 32
107+ {
108+ n := int(r.NumFormats())
109+ off += n * 28
110+ }
111+ {
112+ n := int(r.NumScreens())
113+ it := NewPictscreenIter(r.raw[off:], n)
114+ for it.Next() {
115+ off += it.Value().Size()
116+ }
117+ }
118+ end := off + (int(r.NumSubpixel()))*4
119+ return SubPixelList{b: r.raw[off:end:end]}
120+}
121+
122+// QueryPictIndexValuesReply is a zero-copy view over a QueryPictIndexValues reply; offsets count from reply byte 0.
123+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
124+type QueryPictIndexValuesReply struct{ raw []byte }
125+
126+// ParseQueryPictIndexValuesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
127+func ParseQueryPictIndexValuesReply(raw []byte) (QueryPictIndexValuesReply, bool) {
128+ if len(raw) < 32 {
129+ return QueryPictIndexValuesReply{}, false
130+ }
131+ r := QueryPictIndexValuesReply{raw: raw}
132+ off := 32
133+ {
134+ n := int(r.NumValues())
135+ if n < 0 || n > len(r.raw) {
136+ return QueryPictIndexValuesReply{}, false
137+ }
138+ off += n * 12
139+ }
140+ if off > len(r.raw) {
141+ return QueryPictIndexValuesReply{}, false
142+ }
143+ if off > len(r.raw) {
144+ return QueryPictIndexValuesReply{}, false
145+ }
146+ return r, true
147+}
148+func (r QueryPictIndexValuesReply) Raw() []byte { return r.raw }
149+func (r QueryPictIndexValuesReply) NumValues() uint32 {
150+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
151+}
152+func (r QueryPictIndexValuesReply) Values() IndexvalueList {
153+ off := 32
154+ end := off + (int(r.NumValues()))*12
155+ return IndexvalueList{b: r.raw[off:end:end]}
156+}
157+
158+// QueryFiltersReply is a zero-copy view over a QueryFilters reply; offsets count from reply byte 0.
159+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
160+type QueryFiltersReply struct{ raw []byte }
161+
162+// ParseQueryFiltersReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
163+func ParseQueryFiltersReply(raw []byte) (QueryFiltersReply, bool) {
164+ if len(raw) < 32 {
165+ return QueryFiltersReply{}, false
166+ }
167+ r := QueryFiltersReply{raw: raw}
168+ off := 32
169+ {
170+ n := int(r.NumAliases())
171+ if n < 0 || n > len(r.raw) {
172+ return QueryFiltersReply{}, false
173+ }
174+ off += n * 2
175+ }
176+ if off > len(r.raw) {
177+ return QueryFiltersReply{}, false
178+ }
179+ {
180+ n := int(r.NumFilters())
181+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
182+ return QueryFiltersReply{}, false
183+ }
184+ it := xproto.NewStrIter(r.raw[off:], n)
185+ for it.Next() {
186+ off += it.Value().Size()
187+ }
188+ if it.Err() {
189+ return QueryFiltersReply{}, false
190+ }
191+ }
192+ if off > len(r.raw) {
193+ return QueryFiltersReply{}, false
194+ }
195+ return r, true
196+}
197+func (r QueryFiltersReply) Raw() []byte { return r.raw }
198+func (r QueryFiltersReply) NumAliases() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[8:])) }
199+func (r QueryFiltersReply) NumFilters() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[12:])) }
200+func (r QueryFiltersReply) Aliases() Uint16List {
201+ off := 32
202+ end := off + (int(r.NumAliases()))*2
203+ return Uint16List{b: r.raw[off:end:end]}
204+}
205+func (r QueryFiltersReply) Filters() xproto.StrIter {
206+ off := 32
207+ {
208+ n := int(r.NumAliases())
209+ off += n * 2
210+ }
211+ return xproto.NewStrIter(r.raw[off:], int(r.NumFilters()))
212+}
213+
214+// PictdepthView is a zero-copy view over one wire-encoded Pictdepth, yielded by PictdepthIter.
215+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
216+type PictdepthView struct{ raw []byte }
217+
218+func (v PictdepthView) Raw() []byte { return v.raw }
219+
220+// Size is the element's wire size, including trailing alignment.
221+func (v PictdepthView) Size() int { return len(v.raw) }
222+func sizePictdepth(p []byte) int {
223+ if len(p) < 8 {
224+ return -1
225+ }
226+ v := PictdepthView{raw: p}
227+ _ = v
228+ off := 8
229+ {
230+ n := int(v.NumVisuals())
231+ if n < 0 || n > len(v.raw) {
232+ return -1
233+ }
234+ off += n * 8
235+ }
236+ if off > len(v.raw) {
237+ return -1
238+ }
239+ if off > len(v.raw) {
240+ return -1
241+ }
242+ return off
243+}
244+func (v PictdepthView) Depth() uint8 { return uint8(v.raw[0:][0]) }
245+func (v PictdepthView) NumVisuals() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[2:])) }
246+func (v PictdepthView) Visuals() PictvisualList {
247+ off := 8
248+ end := off + (int(v.NumVisuals()))*8
249+ return PictvisualList{b: v.raw[off:end:end]}
250+}
251+
252+// PictdepthIter walks n consecutive Pictdepth elements; Err reports a malformed length.
253+// It shares the lifetime of the bytes it walks.
254+type PictdepthIter struct {
255+ b []byte
256+ n int
257+ v PictdepthView
258+ err bool
259+}
260+
261+// NewPictdepthIter iterates n elements encoded at the start of b.
262+func NewPictdepthIter(b []byte, n int) PictdepthIter { return PictdepthIter{b: b, n: n} }
263+func (it *PictdepthIter) Next() bool {
264+ if it.n <= 0 || it.err {
265+ return false
266+ }
267+ s := sizePictdepth(it.b)
268+ if s < 0 {
269+ it.err = true
270+ it.n = 0
271+ return false
272+ }
273+ it.v = PictdepthView{raw: it.b[:s:s]}
274+ it.b = it.b[s:]
275+ it.n--
276+ return true
277+}
278+func (it *PictdepthIter) Value() PictdepthView { return it.v }
279+func (it *PictdepthIter) Err() bool { return it.err }
280+
281+// PictscreenView is a zero-copy view over one wire-encoded Pictscreen, yielded by PictscreenIter.
282+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
283+type PictscreenView struct{ raw []byte }
284+
285+func (v PictscreenView) Raw() []byte { return v.raw }
286+
287+// Size is the element's wire size, including trailing alignment.
288+func (v PictscreenView) Size() int { return len(v.raw) }
289+func sizePictscreen(p []byte) int {
290+ if len(p) < 8 {
291+ return -1
292+ }
293+ v := PictscreenView{raw: p}
294+ _ = v
295+ off := 8
296+ {
297+ n := int(v.NumDepths())
298+ if off > len(v.raw) || n < 0 || n > len(v.raw) {
299+ return -1
300+ }
301+ it := NewPictdepthIter(v.raw[off:], n)
302+ for it.Next() {
303+ off += it.Value().Size()
304+ }
305+ if it.Err() {
306+ return -1
307+ }
308+ }
309+ if off > len(v.raw) {
310+ return -1
311+ }
312+ return off
313+}
314+func (v PictscreenView) NumDepths() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[0:])) }
315+func (v PictscreenView) Fallback() Pictformat {
316+ return Pictformat(binary.LittleEndian.Uint32(v.raw[4:]))
317+}
318+func (v PictscreenView) Depths() PictdepthIter {
319+ off := 8
320+ return NewPictdepthIter(v.raw[off:], int(v.NumDepths()))
321+}
322+
323+// PictscreenIter walks n consecutive Pictscreen elements; Err reports a malformed length.
324+// It shares the lifetime of the bytes it walks.
325+type PictscreenIter struct {
326+ b []byte
327+ n int
328+ v PictscreenView
329+ err bool
330+}
331+
332+// NewPictscreenIter iterates n elements encoded at the start of b.
333+func NewPictscreenIter(b []byte, n int) PictscreenIter { return PictscreenIter{b: b, n: n} }
334+func (it *PictscreenIter) Next() bool {
335+ if it.n <= 0 || it.err {
336+ return false
337+ }
338+ s := sizePictscreen(it.b)
339+ if s < 0 {
340+ it.err = true
341+ it.n = 0
342+ return false
343+ }
344+ it.v = PictscreenView{raw: it.b[:s:s]}
345+ it.b = it.b[s:]
346+ it.n--
347+ return true
348+}
349+func (it *PictscreenIter) Value() PictscreenView { return it.v }
350+func (it *PictscreenIter) Err() bool { return it.err }
351+
352+// IndexvalueList is a borrowed view over a wire list of Indexvalue. It shares the lifetime of the bytes it was taken from; copy what you keep.
353+type IndexvalueList struct{ b []byte }
354+
355+func (l IndexvalueList) Len() int { return len(l.b) / 12 }
356+func (l IndexvalueList) At(i int) Indexvalue { return GetIndexvalue(l.b[i*12:]) }
357+
358+// PictforminfoList is a borrowed view over a wire list of Pictforminfo. It shares the lifetime of the bytes it was taken from; copy what you keep.
359+type PictforminfoList struct{ b []byte }
360+
361+func (l PictforminfoList) Len() int { return len(l.b) / 28 }
362+func (l PictforminfoList) At(i int) Pictforminfo { return GetPictforminfo(l.b[i*28:]) }
363+
364+// PictvisualList is a borrowed view over a wire list of Pictvisual. It shares the lifetime of the bytes it was taken from; copy what you keep.
365+type PictvisualList struct{ b []byte }
366+
367+func (l PictvisualList) Len() int { return len(l.b) / 8 }
368+func (l PictvisualList) At(i int) Pictvisual { return GetPictvisual(l.b[i*8:]) }
369+
370+// SubPixelList is a borrowed view over a wire list of SubPixel. It shares the lifetime of the bytes it was taken from; copy what you keep.
371+type SubPixelList struct{ b []byte }
372+
373+func (l SubPixelList) Len() int { return len(l.b) / 4 }
374+func (l SubPixelList) At(i int) SubPixel { return SubPixel(binary.LittleEndian.Uint32(l.b[i*4:])) }
375+
376+// Uint16List is a borrowed view over a wire list of uint16. It shares the lifetime of the bytes it was taken from; copy what you keep.
377+type Uint16List struct{ b []byte }
378+
379+func (l Uint16List) Len() int { return len(l.b) / 2 }
380+func (l Uint16List) At(i int) uint16 { return uint16(binary.LittleEndian.Uint16(l.b[i*2:])) }
A render/requests_generated.go +1276 -0
@@ -0,0 +1,1276 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+import "math/bits"
7+import "bnema.dev/xturbo"
8+
9+func QueryVersion(b *xturbo.Buf, clientMajorVersion uint32, clientMinorVersion uint32) xturbo.Seq {
10+ if b.Ext == nil {
11+ b.Fail(xturbo.ErrExtMissing)
12+ return 0
13+ }
14+ major := b.Ext.Major(xturbo.ExtRender)
15+ if major == 0 {
16+ b.Fail(xturbo.ErrExtMissing)
17+ return 0
18+ }
19+ p, seq := b.Request(major, 0, 8)
20+ if p == nil {
21+ return 0
22+ }
23+ binary.LittleEndian.PutUint32(p[0:], uint32(clientMajorVersion))
24+ binary.LittleEndian.PutUint32(p[4:], uint32(clientMinorVersion))
25+ return seq
26+}
27+
28+func QueryPictFormats(b *xturbo.Buf) xturbo.Seq {
29+ if b.Ext == nil {
30+ b.Fail(xturbo.ErrExtMissing)
31+ return 0
32+ }
33+ major := b.Ext.Major(xturbo.ExtRender)
34+ if major == 0 {
35+ b.Fail(xturbo.ErrExtMissing)
36+ return 0
37+ }
38+ p, seq := b.Request(major, 1, 0)
39+ if p == nil {
40+ return 0
41+ }
42+ return seq
43+}
44+
45+func QueryPictIndexValues(b *xturbo.Buf, format Pictformat) xturbo.Seq {
46+ if b.Ext == nil {
47+ b.Fail(xturbo.ErrExtMissing)
48+ return 0
49+ }
50+ major := b.Ext.Major(xturbo.ExtRender)
51+ if major == 0 {
52+ b.Fail(xturbo.ErrExtMissing)
53+ return 0
54+ }
55+ p, seq := b.Request(major, 2, 4)
56+ if p == nil {
57+ return 0
58+ }
59+ binary.LittleEndian.PutUint32(p[0:], uint32(format))
60+ return seq
61+}
62+
63+type CreatePictureValues struct {
64+ Mask Cp
65+ Repeat Repeat
66+ Alphamap Picture
67+ Alphaxorigin int32
68+ Alphayorigin int32
69+ Clipxorigin int32
70+ Clipyorigin int32
71+ Clipmask xproto.Pixmap
72+ Graphicsexposure uint32
73+ Subwindowmode xproto.SubwindowMode
74+ Polyedge PolyEdge
75+ Polymode PolyMode
76+ Dither xproto.Atom
77+ Componentalpha uint32
78+}
79+
80+func (v *CreatePictureValues) SetRepeat(x Repeat) *CreatePictureValues {
81+ v.Mask |= CpRepeat
82+ v.Repeat = x
83+ return v
84+}
85+func (v *CreatePictureValues) SetAlphamap(x Picture) *CreatePictureValues {
86+ v.Mask |= CpAlphaMap
87+ v.Alphamap = x
88+ return v
89+}
90+func (v *CreatePictureValues) SetAlphaxorigin(x int32) *CreatePictureValues {
91+ v.Mask |= CpAlphaXOrigin
92+ v.Alphaxorigin = x
93+ return v
94+}
95+func (v *CreatePictureValues) SetAlphayorigin(x int32) *CreatePictureValues {
96+ v.Mask |= CpAlphaYOrigin
97+ v.Alphayorigin = x
98+ return v
99+}
100+func (v *CreatePictureValues) SetClipxorigin(x int32) *CreatePictureValues {
101+ v.Mask |= CpClipXOrigin
102+ v.Clipxorigin = x
103+ return v
104+}
105+func (v *CreatePictureValues) SetClipyorigin(x int32) *CreatePictureValues {
106+ v.Mask |= CpClipYOrigin
107+ v.Clipyorigin = x
108+ return v
109+}
110+func (v *CreatePictureValues) SetClipmask(x xproto.Pixmap) *CreatePictureValues {
111+ v.Mask |= CpClipMask
112+ v.Clipmask = x
113+ return v
114+}
115+func (v *CreatePictureValues) SetGraphicsexposure(x uint32) *CreatePictureValues {
116+ v.Mask |= CpGraphicsExposure
117+ v.Graphicsexposure = x
118+ return v
119+}
120+func (v *CreatePictureValues) SetSubwindowmode(x xproto.SubwindowMode) *CreatePictureValues {
121+ v.Mask |= CpSubwindowMode
122+ v.Subwindowmode = x
123+ return v
124+}
125+func (v *CreatePictureValues) SetPolyedge(x PolyEdge) *CreatePictureValues {
126+ v.Mask |= CpPolyEdge
127+ v.Polyedge = x
128+ return v
129+}
130+func (v *CreatePictureValues) SetPolymode(x PolyMode) *CreatePictureValues {
131+ v.Mask |= CpPolyMode
132+ v.Polymode = x
133+ return v
134+}
135+func (v *CreatePictureValues) SetDither(x xproto.Atom) *CreatePictureValues {
136+ v.Mask |= CpDither
137+ v.Dither = x
138+ return v
139+}
140+func (v *CreatePictureValues) SetComponentalpha(x uint32) *CreatePictureValues {
141+ v.Mask |= CpComponentAlpha
142+ v.Componentalpha = x
143+ return v
144+}
145+func CreatePicture(b *xturbo.Buf, pid Picture, drawable xproto.Drawable, format Pictformat, v *CreatePictureValues) xturbo.Seq {
146+ if b.Ext == nil {
147+ b.Fail(xturbo.ErrExtMissing)
148+ return 0
149+ }
150+ major := b.Ext.Major(xturbo.ExtRender)
151+ if major == 0 {
152+ b.Fail(xturbo.ErrExtMissing)
153+ return 0
154+ }
155+ p, seq := b.Request(major, 4, 16+4*bits.OnesCount32(uint32(v.Mask)))
156+ if p == nil {
157+ return 0
158+ }
159+ binary.LittleEndian.PutUint32(p[0:], uint32(pid))
160+ binary.LittleEndian.PutUint32(p[4:], uint32(drawable))
161+ binary.LittleEndian.PutUint32(p[8:], uint32(format))
162+ binary.LittleEndian.PutUint32(p[12:], uint32(v.Mask))
163+ off := 16
164+ if v.Mask&CpRepeat != 0 {
165+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Repeat))
166+ off += 4
167+ }
168+ if v.Mask&CpAlphaMap != 0 {
169+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphamap))
170+ off += 4
171+ }
172+ if v.Mask&CpAlphaXOrigin != 0 {
173+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphaxorigin))
174+ off += 4
175+ }
176+ if v.Mask&CpAlphaYOrigin != 0 {
177+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphayorigin))
178+ off += 4
179+ }
180+ if v.Mask&CpClipXOrigin != 0 {
181+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipxorigin))
182+ off += 4
183+ }
184+ if v.Mask&CpClipYOrigin != 0 {
185+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipyorigin))
186+ off += 4
187+ }
188+ if v.Mask&CpClipMask != 0 {
189+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipmask))
190+ off += 4
191+ }
192+ if v.Mask&CpGraphicsExposure != 0 {
193+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Graphicsexposure))
194+ off += 4
195+ }
196+ if v.Mask&CpSubwindowMode != 0 {
197+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Subwindowmode))
198+ off += 4
199+ }
200+ if v.Mask&CpPolyEdge != 0 {
201+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Polyedge))
202+ off += 4
203+ }
204+ if v.Mask&CpPolyMode != 0 {
205+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Polymode))
206+ off += 4
207+ }
208+ if v.Mask&CpDither != 0 {
209+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Dither))
210+ off += 4
211+ }
212+ if v.Mask&CpComponentAlpha != 0 {
213+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Componentalpha))
214+ off += 4
215+ }
216+ _ = off
217+ return seq
218+}
219+
220+type ChangePictureValues struct {
221+ Mask Cp
222+ Repeat Repeat
223+ Alphamap Picture
224+ Alphaxorigin int32
225+ Alphayorigin int32
226+ Clipxorigin int32
227+ Clipyorigin int32
228+ Clipmask xproto.Pixmap
229+ Graphicsexposure uint32
230+ Subwindowmode xproto.SubwindowMode
231+ Polyedge PolyEdge
232+ Polymode PolyMode
233+ Dither xproto.Atom
234+ Componentalpha uint32
235+}
236+
237+func (v *ChangePictureValues) SetRepeat(x Repeat) *ChangePictureValues {
238+ v.Mask |= CpRepeat
239+ v.Repeat = x
240+ return v
241+}
242+func (v *ChangePictureValues) SetAlphamap(x Picture) *ChangePictureValues {
243+ v.Mask |= CpAlphaMap
244+ v.Alphamap = x
245+ return v
246+}
247+func (v *ChangePictureValues) SetAlphaxorigin(x int32) *ChangePictureValues {
248+ v.Mask |= CpAlphaXOrigin
249+ v.Alphaxorigin = x
250+ return v
251+}
252+func (v *ChangePictureValues) SetAlphayorigin(x int32) *ChangePictureValues {
253+ v.Mask |= CpAlphaYOrigin
254+ v.Alphayorigin = x
255+ return v
256+}
257+func (v *ChangePictureValues) SetClipxorigin(x int32) *ChangePictureValues {
258+ v.Mask |= CpClipXOrigin
259+ v.Clipxorigin = x
260+ return v
261+}
262+func (v *ChangePictureValues) SetClipyorigin(x int32) *ChangePictureValues {
263+ v.Mask |= CpClipYOrigin
264+ v.Clipyorigin = x
265+ return v
266+}
267+func (v *ChangePictureValues) SetClipmask(x xproto.Pixmap) *ChangePictureValues {
268+ v.Mask |= CpClipMask
269+ v.Clipmask = x
270+ return v
271+}
272+func (v *ChangePictureValues) SetGraphicsexposure(x uint32) *ChangePictureValues {
273+ v.Mask |= CpGraphicsExposure
274+ v.Graphicsexposure = x
275+ return v
276+}
277+func (v *ChangePictureValues) SetSubwindowmode(x xproto.SubwindowMode) *ChangePictureValues {
278+ v.Mask |= CpSubwindowMode
279+ v.Subwindowmode = x
280+ return v
281+}
282+func (v *ChangePictureValues) SetPolyedge(x PolyEdge) *ChangePictureValues {
283+ v.Mask |= CpPolyEdge
284+ v.Polyedge = x
285+ return v
286+}
287+func (v *ChangePictureValues) SetPolymode(x PolyMode) *ChangePictureValues {
288+ v.Mask |= CpPolyMode
289+ v.Polymode = x
290+ return v
291+}
292+func (v *ChangePictureValues) SetDither(x xproto.Atom) *ChangePictureValues {
293+ v.Mask |= CpDither
294+ v.Dither = x
295+ return v
296+}
297+func (v *ChangePictureValues) SetComponentalpha(x uint32) *ChangePictureValues {
298+ v.Mask |= CpComponentAlpha
299+ v.Componentalpha = x
300+ return v
301+}
302+func ChangePicture(b *xturbo.Buf, picture Picture, v *ChangePictureValues) xturbo.Seq {
303+ if b.Ext == nil {
304+ b.Fail(xturbo.ErrExtMissing)
305+ return 0
306+ }
307+ major := b.Ext.Major(xturbo.ExtRender)
308+ if major == 0 {
309+ b.Fail(xturbo.ErrExtMissing)
310+ return 0
311+ }
312+ p, seq := b.Request(major, 5, 8+4*bits.OnesCount32(uint32(v.Mask)))
313+ if p == nil {
314+ return 0
315+ }
316+ binary.LittleEndian.PutUint32(p[0:], uint32(picture))
317+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Mask))
318+ off := 8
319+ if v.Mask&CpRepeat != 0 {
320+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Repeat))
321+ off += 4
322+ }
323+ if v.Mask&CpAlphaMap != 0 {
324+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphamap))
325+ off += 4
326+ }
327+ if v.Mask&CpAlphaXOrigin != 0 {
328+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphaxorigin))
329+ off += 4
330+ }
331+ if v.Mask&CpAlphaYOrigin != 0 {
332+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphayorigin))
333+ off += 4
334+ }
335+ if v.Mask&CpClipXOrigin != 0 {
336+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipxorigin))
337+ off += 4
338+ }
339+ if v.Mask&CpClipYOrigin != 0 {
340+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipyorigin))
341+ off += 4
342+ }
343+ if v.Mask&CpClipMask != 0 {
344+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipmask))
345+ off += 4
346+ }
347+ if v.Mask&CpGraphicsExposure != 0 {
348+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Graphicsexposure))
349+ off += 4
350+ }
351+ if v.Mask&CpSubwindowMode != 0 {
352+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Subwindowmode))
353+ off += 4
354+ }
355+ if v.Mask&CpPolyEdge != 0 {
356+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Polyedge))
357+ off += 4
358+ }
359+ if v.Mask&CpPolyMode != 0 {
360+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Polymode))
361+ off += 4
362+ }
363+ if v.Mask&CpDither != 0 {
364+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Dither))
365+ off += 4
366+ }
367+ if v.Mask&CpComponentAlpha != 0 {
368+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Componentalpha))
369+ off += 4
370+ }
371+ _ = off
372+ return seq
373+}
374+
375+func SetPictureClipRectangles(b *xturbo.Buf, picture Picture, clipXOrigin int16, clipYOrigin int16, rectangles []xproto.Rectangle) xturbo.Seq {
376+ size := 0
377+ size += 4
378+ size += 2
379+ size += 2
380+ size += len(rectangles) * 8
381+ size = (size + 3) &^ 3
382+ if b.Ext == nil {
383+ b.Fail(xturbo.ErrExtMissing)
384+ return 0
385+ }
386+ major := b.Ext.Major(xturbo.ExtRender)
387+ if major == 0 {
388+ b.Fail(xturbo.ErrExtMissing)
389+ return 0
390+ }
391+ p, seq := b.Request(major, 6, size)
392+ if p == nil {
393+ return 0
394+ }
395+ off := 0
396+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
397+ off += 4
398+ binary.LittleEndian.PutUint16(p[off:], uint16(clipXOrigin))
399+ off += 2
400+ binary.LittleEndian.PutUint16(p[off:], uint16(clipYOrigin))
401+ off += 2
402+ for i := range rectangles {
403+ rectangles[i].Put(p[off:])
404+ off += 8
405+ }
406+ return seq
407+}
408+
409+func FreePicture(b *xturbo.Buf, picture Picture) xturbo.Seq {
410+ if b.Ext == nil {
411+ b.Fail(xturbo.ErrExtMissing)
412+ return 0
413+ }
414+ major := b.Ext.Major(xturbo.ExtRender)
415+ if major == 0 {
416+ b.Fail(xturbo.ErrExtMissing)
417+ return 0
418+ }
419+ p, seq := b.Request(major, 7, 4)
420+ if p == nil {
421+ return 0
422+ }
423+ binary.LittleEndian.PutUint32(p[0:], uint32(picture))
424+ return seq
425+}
426+
427+func Composite(b *xturbo.Buf, op PictOp, src Picture, mask Picture, dst Picture, srcX int16, srcY int16, maskX int16, maskY int16, dstX int16, dstY int16, width uint16, height uint16) xturbo.Seq {
428+ if b.Ext == nil {
429+ b.Fail(xturbo.ErrExtMissing)
430+ return 0
431+ }
432+ major := b.Ext.Major(xturbo.ExtRender)
433+ if major == 0 {
434+ b.Fail(xturbo.ErrExtMissing)
435+ return 0
436+ }
437+ p, seq := b.Request(major, 8, 32)
438+ if p == nil {
439+ return 0
440+ }
441+ p[0:][0] = byte(op)
442+ binary.LittleEndian.PutUint32(p[4:], uint32(src))
443+ binary.LittleEndian.PutUint32(p[8:], uint32(mask))
444+ binary.LittleEndian.PutUint32(p[12:], uint32(dst))
445+ binary.LittleEndian.PutUint16(p[16:], uint16(srcX))
446+ binary.LittleEndian.PutUint16(p[18:], uint16(srcY))
447+ binary.LittleEndian.PutUint16(p[20:], uint16(maskX))
448+ binary.LittleEndian.PutUint16(p[22:], uint16(maskY))
449+ binary.LittleEndian.PutUint16(p[24:], uint16(dstX))
450+ binary.LittleEndian.PutUint16(p[26:], uint16(dstY))
451+ binary.LittleEndian.PutUint16(p[28:], uint16(width))
452+ binary.LittleEndian.PutUint16(p[30:], uint16(height))
453+ return seq
454+}
455+
456+func Trapezoids(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, srcX int16, srcY int16, traps []Trapezoid) xturbo.Seq {
457+ size := 0
458+ size += 1
459+ size += 3
460+ size += 4
461+ size += 4
462+ size += 4
463+ size += 2
464+ size += 2
465+ size += len(traps) * 40
466+ size = (size + 3) &^ 3
467+ if b.Ext == nil {
468+ b.Fail(xturbo.ErrExtMissing)
469+ return 0
470+ }
471+ major := b.Ext.Major(xturbo.ExtRender)
472+ if major == 0 {
473+ b.Fail(xturbo.ErrExtMissing)
474+ return 0
475+ }
476+ p, seq := b.Request(major, 10, size)
477+ if p == nil {
478+ return 0
479+ }
480+ off := 0
481+ p[off:][0] = byte(op)
482+ off += 1
483+ off += 3
484+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
485+ off += 4
486+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
487+ off += 4
488+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
489+ off += 4
490+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
491+ off += 2
492+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
493+ off += 2
494+ for i := range traps {
495+ traps[i].Put(p[off:])
496+ off += 40
497+ }
498+ return seq
499+}
500+
501+func Triangles(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, srcX int16, srcY int16, triangles []Triangle) xturbo.Seq {
502+ size := 0
503+ size += 1
504+ size += 3
505+ size += 4
506+ size += 4
507+ size += 4
508+ size += 2
509+ size += 2
510+ size += len(triangles) * 24
511+ size = (size + 3) &^ 3
512+ if b.Ext == nil {
513+ b.Fail(xturbo.ErrExtMissing)
514+ return 0
515+ }
516+ major := b.Ext.Major(xturbo.ExtRender)
517+ if major == 0 {
518+ b.Fail(xturbo.ErrExtMissing)
519+ return 0
520+ }
521+ p, seq := b.Request(major, 11, size)
522+ if p == nil {
523+ return 0
524+ }
525+ off := 0
526+ p[off:][0] = byte(op)
527+ off += 1
528+ off += 3
529+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
530+ off += 4
531+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
532+ off += 4
533+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
534+ off += 4
535+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
536+ off += 2
537+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
538+ off += 2
539+ for i := range triangles {
540+ triangles[i].Put(p[off:])
541+ off += 24
542+ }
543+ return seq
544+}
545+
546+func TriStrip(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, srcX int16, srcY int16, points []Pointfix) xturbo.Seq {
547+ size := 0
548+ size += 1
549+ size += 3
550+ size += 4
551+ size += 4
552+ size += 4
553+ size += 2
554+ size += 2
555+ size += len(points) * 8
556+ size = (size + 3) &^ 3
557+ if b.Ext == nil {
558+ b.Fail(xturbo.ErrExtMissing)
559+ return 0
560+ }
561+ major := b.Ext.Major(xturbo.ExtRender)
562+ if major == 0 {
563+ b.Fail(xturbo.ErrExtMissing)
564+ return 0
565+ }
566+ p, seq := b.Request(major, 12, size)
567+ if p == nil {
568+ return 0
569+ }
570+ off := 0
571+ p[off:][0] = byte(op)
572+ off += 1
573+ off += 3
574+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
575+ off += 4
576+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
577+ off += 4
578+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
579+ off += 4
580+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
581+ off += 2
582+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
583+ off += 2
584+ for i := range points {
585+ points[i].Put(p[off:])
586+ off += 8
587+ }
588+ return seq
589+}
590+
591+func TriFan(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, srcX int16, srcY int16, points []Pointfix) xturbo.Seq {
592+ size := 0
593+ size += 1
594+ size += 3
595+ size += 4
596+ size += 4
597+ size += 4
598+ size += 2
599+ size += 2
600+ size += len(points) * 8
601+ size = (size + 3) &^ 3
602+ if b.Ext == nil {
603+ b.Fail(xturbo.ErrExtMissing)
604+ return 0
605+ }
606+ major := b.Ext.Major(xturbo.ExtRender)
607+ if major == 0 {
608+ b.Fail(xturbo.ErrExtMissing)
609+ return 0
610+ }
611+ p, seq := b.Request(major, 13, size)
612+ if p == nil {
613+ return 0
614+ }
615+ off := 0
616+ p[off:][0] = byte(op)
617+ off += 1
618+ off += 3
619+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
620+ off += 4
621+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
622+ off += 4
623+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
624+ off += 4
625+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
626+ off += 2
627+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
628+ off += 2
629+ for i := range points {
630+ points[i].Put(p[off:])
631+ off += 8
632+ }
633+ return seq
634+}
635+
636+func CreateGlyphSet(b *xturbo.Buf, gsid Glyphset, format Pictformat) xturbo.Seq {
637+ if b.Ext == nil {
638+ b.Fail(xturbo.ErrExtMissing)
639+ return 0
640+ }
641+ major := b.Ext.Major(xturbo.ExtRender)
642+ if major == 0 {
643+ b.Fail(xturbo.ErrExtMissing)
644+ return 0
645+ }
646+ p, seq := b.Request(major, 17, 8)
647+ if p == nil {
648+ return 0
649+ }
650+ binary.LittleEndian.PutUint32(p[0:], uint32(gsid))
651+ binary.LittleEndian.PutUint32(p[4:], uint32(format))
652+ return seq
653+}
654+
655+func ReferenceGlyphSet(b *xturbo.Buf, gsid Glyphset, existing Glyphset) xturbo.Seq {
656+ if b.Ext == nil {
657+ b.Fail(xturbo.ErrExtMissing)
658+ return 0
659+ }
660+ major := b.Ext.Major(xturbo.ExtRender)
661+ if major == 0 {
662+ b.Fail(xturbo.ErrExtMissing)
663+ return 0
664+ }
665+ p, seq := b.Request(major, 18, 8)
666+ if p == nil {
667+ return 0
668+ }
669+ binary.LittleEndian.PutUint32(p[0:], uint32(gsid))
670+ binary.LittleEndian.PutUint32(p[4:], uint32(existing))
671+ return seq
672+}
673+
674+func FreeGlyphSet(b *xturbo.Buf, glyphset Glyphset) xturbo.Seq {
675+ if b.Ext == nil {
676+ b.Fail(xturbo.ErrExtMissing)
677+ return 0
678+ }
679+ major := b.Ext.Major(xturbo.ExtRender)
680+ if major == 0 {
681+ b.Fail(xturbo.ErrExtMissing)
682+ return 0
683+ }
684+ p, seq := b.Request(major, 19, 4)
685+ if p == nil {
686+ return 0
687+ }
688+ binary.LittleEndian.PutUint32(p[0:], uint32(glyphset))
689+ return seq
690+}
691+
692+func AddGlyphs(b *xturbo.Buf, glyphset Glyphset, glyphsLen uint32, glyphids []uint32, glyphs []Glyphinfo, data []byte) xturbo.Seq {
693+ if len(glyphids) != int(glyphsLen) {
694+ b.Fail(xturbo.ErrBadLength)
695+ return 0
696+ }
697+ if len(glyphs) != int(glyphsLen) {
698+ b.Fail(xturbo.ErrBadLength)
699+ return 0
700+ }
701+ size := 0
702+ size += 4
703+ size += 4
704+ size += len(glyphids) * 4
705+ size += len(glyphs) * 12
706+ size += len(data) * 1
707+ size = (size + 3) &^ 3
708+ if b.Ext == nil {
709+ b.Fail(xturbo.ErrExtMissing)
710+ return 0
711+ }
712+ major := b.Ext.Major(xturbo.ExtRender)
713+ if major == 0 {
714+ b.Fail(xturbo.ErrExtMissing)
715+ return 0
716+ }
717+ p, seq := b.Request(major, 20, size)
718+ if p == nil {
719+ return 0
720+ }
721+ off := 0
722+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
723+ off += 4
724+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphsLen))
725+ off += 4
726+ for i := range glyphids {
727+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphids[i]))
728+ off += 4
729+ }
730+ for i := range glyphs {
731+ glyphs[i].Put(p[off:])
732+ off += 12
733+ }
734+ copy(p[off:], data)
735+ off += len(data)
736+ return seq
737+}
738+
739+func FreeGlyphs(b *xturbo.Buf, glyphset Glyphset, glyphs []Glyph) xturbo.Seq {
740+ size := 0
741+ size += 4
742+ size += len(glyphs) * 4
743+ size = (size + 3) &^ 3
744+ if b.Ext == nil {
745+ b.Fail(xturbo.ErrExtMissing)
746+ return 0
747+ }
748+ major := b.Ext.Major(xturbo.ExtRender)
749+ if major == 0 {
750+ b.Fail(xturbo.ErrExtMissing)
751+ return 0
752+ }
753+ p, seq := b.Request(major, 22, size)
754+ if p == nil {
755+ return 0
756+ }
757+ off := 0
758+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
759+ off += 4
760+ for i := range glyphs {
761+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphs[i]))
762+ off += 4
763+ }
764+ return seq
765+}
766+
767+func CompositeGlyphs8(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, glyphset Glyphset, srcX int16, srcY int16, glyphcmds []byte) xturbo.Seq {
768+ size := 0
769+ size += 1
770+ size += 3
771+ size += 4
772+ size += 4
773+ size += 4
774+ size += 4
775+ size += 2
776+ size += 2
777+ size += len(glyphcmds) * 1
778+ size = (size + 3) &^ 3
779+ if b.Ext == nil {
780+ b.Fail(xturbo.ErrExtMissing)
781+ return 0
782+ }
783+ major := b.Ext.Major(xturbo.ExtRender)
784+ if major == 0 {
785+ b.Fail(xturbo.ErrExtMissing)
786+ return 0
787+ }
788+ p, seq := b.Request(major, 23, size)
789+ if p == nil {
790+ return 0
791+ }
792+ off := 0
793+ p[off:][0] = byte(op)
794+ off += 1
795+ off += 3
796+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
797+ off += 4
798+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
799+ off += 4
800+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
801+ off += 4
802+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
803+ off += 4
804+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
805+ off += 2
806+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
807+ off += 2
808+ copy(p[off:], glyphcmds)
809+ off += len(glyphcmds)
810+ return seq
811+}
812+
813+func CompositeGlyphs16(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, glyphset Glyphset, srcX int16, srcY int16, glyphcmds []byte) xturbo.Seq {
814+ size := 0
815+ size += 1
816+ size += 3
817+ size += 4
818+ size += 4
819+ size += 4
820+ size += 4
821+ size += 2
822+ size += 2
823+ size += len(glyphcmds) * 1
824+ size = (size + 3) &^ 3
825+ if b.Ext == nil {
826+ b.Fail(xturbo.ErrExtMissing)
827+ return 0
828+ }
829+ major := b.Ext.Major(xturbo.ExtRender)
830+ if major == 0 {
831+ b.Fail(xturbo.ErrExtMissing)
832+ return 0
833+ }
834+ p, seq := b.Request(major, 24, size)
835+ if p == nil {
836+ return 0
837+ }
838+ off := 0
839+ p[off:][0] = byte(op)
840+ off += 1
841+ off += 3
842+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
843+ off += 4
844+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
845+ off += 4
846+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
847+ off += 4
848+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
849+ off += 4
850+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
851+ off += 2
852+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
853+ off += 2
854+ copy(p[off:], glyphcmds)
855+ off += len(glyphcmds)
856+ return seq
857+}
858+
859+func CompositeGlyphs32(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, glyphset Glyphset, srcX int16, srcY int16, glyphcmds []byte) xturbo.Seq {
860+ size := 0
861+ size += 1
862+ size += 3
863+ size += 4
864+ size += 4
865+ size += 4
866+ size += 4
867+ size += 2
868+ size += 2
869+ size += len(glyphcmds) * 1
870+ size = (size + 3) &^ 3
871+ if b.Ext == nil {
872+ b.Fail(xturbo.ErrExtMissing)
873+ return 0
874+ }
875+ major := b.Ext.Major(xturbo.ExtRender)
876+ if major == 0 {
877+ b.Fail(xturbo.ErrExtMissing)
878+ return 0
879+ }
880+ p, seq := b.Request(major, 25, size)
881+ if p == nil {
882+ return 0
883+ }
884+ off := 0
885+ p[off:][0] = byte(op)
886+ off += 1
887+ off += 3
888+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
889+ off += 4
890+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
891+ off += 4
892+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
893+ off += 4
894+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
895+ off += 4
896+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
897+ off += 2
898+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
899+ off += 2
900+ copy(p[off:], glyphcmds)
901+ off += len(glyphcmds)
902+ return seq
903+}
904+
905+func FillRectangles(b *xturbo.Buf, op PictOp, dst Picture, color Color, rects []xproto.Rectangle) xturbo.Seq {
906+ size := 0
907+ size += 1
908+ size += 3
909+ size += 4
910+ size += 8
911+ size += len(rects) * 8
912+ size = (size + 3) &^ 3
913+ if b.Ext == nil {
914+ b.Fail(xturbo.ErrExtMissing)
915+ return 0
916+ }
917+ major := b.Ext.Major(xturbo.ExtRender)
918+ if major == 0 {
919+ b.Fail(xturbo.ErrExtMissing)
920+ return 0
921+ }
922+ p, seq := b.Request(major, 26, size)
923+ if p == nil {
924+ return 0
925+ }
926+ off := 0
927+ p[off:][0] = byte(op)
928+ off += 1
929+ off += 3
930+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
931+ off += 4
932+ color.Put(p[off:])
933+ off += 8
934+ for i := range rects {
935+ rects[i].Put(p[off:])
936+ off += 8
937+ }
938+ return seq
939+}
940+
941+func CreateCursor(b *xturbo.Buf, cid xproto.Cursor, source Picture, x uint16, y uint16) xturbo.Seq {
942+ if b.Ext == nil {
943+ b.Fail(xturbo.ErrExtMissing)
944+ return 0
945+ }
946+ major := b.Ext.Major(xturbo.ExtRender)
947+ if major == 0 {
948+ b.Fail(xturbo.ErrExtMissing)
949+ return 0
950+ }
951+ p, seq := b.Request(major, 27, 12)
952+ if p == nil {
953+ return 0
954+ }
955+ binary.LittleEndian.PutUint32(p[0:], uint32(cid))
956+ binary.LittleEndian.PutUint32(p[4:], uint32(source))
957+ binary.LittleEndian.PutUint16(p[8:], uint16(x))
958+ binary.LittleEndian.PutUint16(p[10:], uint16(y))
959+ return seq
960+}
961+
962+func SetPictureTransform(b *xturbo.Buf, picture Picture, transform Transform) xturbo.Seq {
963+ if b.Ext == nil {
964+ b.Fail(xturbo.ErrExtMissing)
965+ return 0
966+ }
967+ major := b.Ext.Major(xturbo.ExtRender)
968+ if major == 0 {
969+ b.Fail(xturbo.ErrExtMissing)
970+ return 0
971+ }
972+ p, seq := b.Request(major, 28, 40)
973+ if p == nil {
974+ return 0
975+ }
976+ binary.LittleEndian.PutUint32(p[0:], uint32(picture))
977+ transform.Put(p[4:])
978+ return seq
979+}
980+
981+func QueryFilters(b *xturbo.Buf, drawable xproto.Drawable) xturbo.Seq {
982+ if b.Ext == nil {
983+ b.Fail(xturbo.ErrExtMissing)
984+ return 0
985+ }
986+ major := b.Ext.Major(xturbo.ExtRender)
987+ if major == 0 {
988+ b.Fail(xturbo.ErrExtMissing)
989+ return 0
990+ }
991+ p, seq := b.Request(major, 29, 4)
992+ if p == nil {
993+ return 0
994+ }
995+ binary.LittleEndian.PutUint32(p[0:], uint32(drawable))
996+ return seq
997+}
998+
999+func SetPictureFilter(b *xturbo.Buf, picture Picture, filter string, values []Fixed) xturbo.Seq {
1000+ if uint64(len(filter)) > 65535 {
1001+ b.Fail(xturbo.ErrBadLength)
1002+ return 0
1003+ }
1004+ size := 0
1005+ size += 4
1006+ size += 2
1007+ size += 2
1008+ size += len(filter) * 1
1009+ size = (size + 3) &^ 3
1010+ size += len(values) * 4
1011+ size = (size + 3) &^ 3
1012+ if b.Ext == nil {
1013+ b.Fail(xturbo.ErrExtMissing)
1014+ return 0
1015+ }
1016+ major := b.Ext.Major(xturbo.ExtRender)
1017+ if major == 0 {
1018+ b.Fail(xturbo.ErrExtMissing)
1019+ return 0
1020+ }
1021+ p, seq := b.Request(major, 30, size)
1022+ if p == nil {
1023+ return 0
1024+ }
1025+ off := 0
1026+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1027+ off += 4
1028+ binary.LittleEndian.PutUint16(p[off:], uint16(len(filter)))
1029+ off += 2
1030+ off += 2
1031+ copy(p[off:], filter)
1032+ off += len(filter)
1033+ off = (off + 3) &^ 3
1034+ for i := range values {
1035+ binary.LittleEndian.PutUint32(p[off:], uint32(values[i]))
1036+ off += 4
1037+ }
1038+ return seq
1039+}
1040+
1041+func CreateAnimCursor(b *xturbo.Buf, cid xproto.Cursor, cursors []Animcursorelt) xturbo.Seq {
1042+ size := 0
1043+ size += 4
1044+ size += len(cursors) * 8
1045+ size = (size + 3) &^ 3
1046+ if b.Ext == nil {
1047+ b.Fail(xturbo.ErrExtMissing)
1048+ return 0
1049+ }
1050+ major := b.Ext.Major(xturbo.ExtRender)
1051+ if major == 0 {
1052+ b.Fail(xturbo.ErrExtMissing)
1053+ return 0
1054+ }
1055+ p, seq := b.Request(major, 31, size)
1056+ if p == nil {
1057+ return 0
1058+ }
1059+ off := 0
1060+ binary.LittleEndian.PutUint32(p[off:], uint32(cid))
1061+ off += 4
1062+ for i := range cursors {
1063+ cursors[i].Put(p[off:])
1064+ off += 8
1065+ }
1066+ return seq
1067+}
1068+
1069+func AddTraps(b *xturbo.Buf, picture Picture, xOff int16, yOff int16, traps []Trap) xturbo.Seq {
1070+ size := 0
1071+ size += 4
1072+ size += 2
1073+ size += 2
1074+ size += len(traps) * 24
1075+ size = (size + 3) &^ 3
1076+ if b.Ext == nil {
1077+ b.Fail(xturbo.ErrExtMissing)
1078+ return 0
1079+ }
1080+ major := b.Ext.Major(xturbo.ExtRender)
1081+ if major == 0 {
1082+ b.Fail(xturbo.ErrExtMissing)
1083+ return 0
1084+ }
1085+ p, seq := b.Request(major, 32, size)
1086+ if p == nil {
1087+ return 0
1088+ }
1089+ off := 0
1090+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1091+ off += 4
1092+ binary.LittleEndian.PutUint16(p[off:], uint16(xOff))
1093+ off += 2
1094+ binary.LittleEndian.PutUint16(p[off:], uint16(yOff))
1095+ off += 2
1096+ for i := range traps {
1097+ traps[i].Put(p[off:])
1098+ off += 24
1099+ }
1100+ return seq
1101+}
1102+
1103+func CreateSolidFill(b *xturbo.Buf, picture Picture, color Color) xturbo.Seq {
1104+ if b.Ext == nil {
1105+ b.Fail(xturbo.ErrExtMissing)
1106+ return 0
1107+ }
1108+ major := b.Ext.Major(xturbo.ExtRender)
1109+ if major == 0 {
1110+ b.Fail(xturbo.ErrExtMissing)
1111+ return 0
1112+ }
1113+ p, seq := b.Request(major, 33, 12)
1114+ if p == nil {
1115+ return 0
1116+ }
1117+ binary.LittleEndian.PutUint32(p[0:], uint32(picture))
1118+ color.Put(p[4:])
1119+ return seq
1120+}
1121+
1122+func CreateLinearGradient(b *xturbo.Buf, picture Picture, p1 Pointfix, p2 Pointfix, numStops uint32, stops []Fixed, colors []Color) xturbo.Seq {
1123+ if len(stops) != int(numStops) {
1124+ b.Fail(xturbo.ErrBadLength)
1125+ return 0
1126+ }
1127+ if len(colors) != int(numStops) {
1128+ b.Fail(xturbo.ErrBadLength)
1129+ return 0
1130+ }
1131+ size := 0
1132+ size += 4
1133+ size += 8
1134+ size += 8
1135+ size += 4
1136+ size += len(stops) * 4
1137+ size += len(colors) * 8
1138+ size = (size + 3) &^ 3
1139+ if b.Ext == nil {
1140+ b.Fail(xturbo.ErrExtMissing)
1141+ return 0
1142+ }
1143+ major := b.Ext.Major(xturbo.ExtRender)
1144+ if major == 0 {
1145+ b.Fail(xturbo.ErrExtMissing)
1146+ return 0
1147+ }
1148+ p, seq := b.Request(major, 34, size)
1149+ if p == nil {
1150+ return 0
1151+ }
1152+ off := 0
1153+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1154+ off += 4
1155+ p1.Put(p[off:])
1156+ off += 8
1157+ p2.Put(p[off:])
1158+ off += 8
1159+ binary.LittleEndian.PutUint32(p[off:], uint32(numStops))
1160+ off += 4
1161+ for i := range stops {
1162+ binary.LittleEndian.PutUint32(p[off:], uint32(stops[i]))
1163+ off += 4
1164+ }
1165+ for i := range colors {
1166+ colors[i].Put(p[off:])
1167+ off += 8
1168+ }
1169+ return seq
1170+}
1171+
1172+func CreateRadialGradient(b *xturbo.Buf, picture Picture, inner Pointfix, outer Pointfix, innerRadius Fixed, outerRadius Fixed, numStops uint32, stops []Fixed, colors []Color) xturbo.Seq {
1173+ if len(stops) != int(numStops) {
1174+ b.Fail(xturbo.ErrBadLength)
1175+ return 0
1176+ }
1177+ if len(colors) != int(numStops) {
1178+ b.Fail(xturbo.ErrBadLength)
1179+ return 0
1180+ }
1181+ size := 0
1182+ size += 4
1183+ size += 8
1184+ size += 8
1185+ size += 4
1186+ size += 4
1187+ size += 4
1188+ size += len(stops) * 4
1189+ size += len(colors) * 8
1190+ size = (size + 3) &^ 3
1191+ if b.Ext == nil {
1192+ b.Fail(xturbo.ErrExtMissing)
1193+ return 0
1194+ }
1195+ major := b.Ext.Major(xturbo.ExtRender)
1196+ if major == 0 {
1197+ b.Fail(xturbo.ErrExtMissing)
1198+ return 0
1199+ }
1200+ p, seq := b.Request(major, 35, size)
1201+ if p == nil {
1202+ return 0
1203+ }
1204+ off := 0
1205+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1206+ off += 4
1207+ inner.Put(p[off:])
1208+ off += 8
1209+ outer.Put(p[off:])
1210+ off += 8
1211+ binary.LittleEndian.PutUint32(p[off:], uint32(innerRadius))
1212+ off += 4
1213+ binary.LittleEndian.PutUint32(p[off:], uint32(outerRadius))
1214+ off += 4
1215+ binary.LittleEndian.PutUint32(p[off:], uint32(numStops))
1216+ off += 4
1217+ for i := range stops {
1218+ binary.LittleEndian.PutUint32(p[off:], uint32(stops[i]))
1219+ off += 4
1220+ }
1221+ for i := range colors {
1222+ colors[i].Put(p[off:])
1223+ off += 8
1224+ }
1225+ return seq
1226+}
1227+
1228+func CreateConicalGradient(b *xturbo.Buf, picture Picture, center Pointfix, angle Fixed, numStops uint32, stops []Fixed, colors []Color) xturbo.Seq {
1229+ if len(stops) != int(numStops) {
1230+ b.Fail(xturbo.ErrBadLength)
1231+ return 0
1232+ }
1233+ if len(colors) != int(numStops) {
1234+ b.Fail(xturbo.ErrBadLength)
1235+ return 0
1236+ }
1237+ size := 0
1238+ size += 4
1239+ size += 8
1240+ size += 4
1241+ size += 4
1242+ size += len(stops) * 4
1243+ size += len(colors) * 8
1244+ size = (size + 3) &^ 3
1245+ if b.Ext == nil {
1246+ b.Fail(xturbo.ErrExtMissing)
1247+ return 0
1248+ }
1249+ major := b.Ext.Major(xturbo.ExtRender)
1250+ if major == 0 {
1251+ b.Fail(xturbo.ErrExtMissing)
1252+ return 0
1253+ }
1254+ p, seq := b.Request(major, 36, size)
1255+ if p == nil {
1256+ return 0
1257+ }
1258+ off := 0
1259+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1260+ off += 4
1261+ center.Put(p[off:])
1262+ off += 8
1263+ binary.LittleEndian.PutUint32(p[off:], uint32(angle))
1264+ off += 4
1265+ binary.LittleEndian.PutUint32(p[off:], uint32(numStops))
1266+ off += 4
1267+ for i := range stops {
1268+ binary.LittleEndian.PutUint32(p[off:], uint32(stops[i]))
1269+ off += 4
1270+ }
1271+ for i := range colors {
1272+ colors[i].Put(p[off:])
1273+ off += 8
1274+ }
1275+ return seq
1276+}
@@ -0,0 +1,1276 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+import "math/bits"
7+import "bnema.dev/xturbo"
8+
9+func QueryVersion(b *xturbo.Buf, clientMajorVersion uint32, clientMinorVersion uint32) xturbo.Seq {
10+ if b.Ext == nil {
11+ b.Fail(xturbo.ErrExtMissing)
12+ return 0
13+ }
14+ major := b.Ext.Major(xturbo.ExtRender)
15+ if major == 0 {
16+ b.Fail(xturbo.ErrExtMissing)
17+ return 0
18+ }
19+ p, seq := b.Request(major, 0, 8)
20+ if p == nil {
21+ return 0
22+ }
23+ binary.LittleEndian.PutUint32(p[0:], uint32(clientMajorVersion))
24+ binary.LittleEndian.PutUint32(p[4:], uint32(clientMinorVersion))
25+ return seq
26+}
27+
28+func QueryPictFormats(b *xturbo.Buf) xturbo.Seq {
29+ if b.Ext == nil {
30+ b.Fail(xturbo.ErrExtMissing)
31+ return 0
32+ }
33+ major := b.Ext.Major(xturbo.ExtRender)
34+ if major == 0 {
35+ b.Fail(xturbo.ErrExtMissing)
36+ return 0
37+ }
38+ p, seq := b.Request(major, 1, 0)
39+ if p == nil {
40+ return 0
41+ }
42+ return seq
43+}
44+
45+func QueryPictIndexValues(b *xturbo.Buf, format Pictformat) xturbo.Seq {
46+ if b.Ext == nil {
47+ b.Fail(xturbo.ErrExtMissing)
48+ return 0
49+ }
50+ major := b.Ext.Major(xturbo.ExtRender)
51+ if major == 0 {
52+ b.Fail(xturbo.ErrExtMissing)
53+ return 0
54+ }
55+ p, seq := b.Request(major, 2, 4)
56+ if p == nil {
57+ return 0
58+ }
59+ binary.LittleEndian.PutUint32(p[0:], uint32(format))
60+ return seq
61+}
62+
63+type CreatePictureValues struct {
64+ Mask Cp
65+ Repeat Repeat
66+ Alphamap Picture
67+ Alphaxorigin int32
68+ Alphayorigin int32
69+ Clipxorigin int32
70+ Clipyorigin int32
71+ Clipmask xproto.Pixmap
72+ Graphicsexposure uint32
73+ Subwindowmode xproto.SubwindowMode
74+ Polyedge PolyEdge
75+ Polymode PolyMode
76+ Dither xproto.Atom
77+ Componentalpha uint32
78+}
79+
80+func (v *CreatePictureValues) SetRepeat(x Repeat) *CreatePictureValues {
81+ v.Mask |= CpRepeat
82+ v.Repeat = x
83+ return v
84+}
85+func (v *CreatePictureValues) SetAlphamap(x Picture) *CreatePictureValues {
86+ v.Mask |= CpAlphaMap
87+ v.Alphamap = x
88+ return v
89+}
90+func (v *CreatePictureValues) SetAlphaxorigin(x int32) *CreatePictureValues {
91+ v.Mask |= CpAlphaXOrigin
92+ v.Alphaxorigin = x
93+ return v
94+}
95+func (v *CreatePictureValues) SetAlphayorigin(x int32) *CreatePictureValues {
96+ v.Mask |= CpAlphaYOrigin
97+ v.Alphayorigin = x
98+ return v
99+}
100+func (v *CreatePictureValues) SetClipxorigin(x int32) *CreatePictureValues {
101+ v.Mask |= CpClipXOrigin
102+ v.Clipxorigin = x
103+ return v
104+}
105+func (v *CreatePictureValues) SetClipyorigin(x int32) *CreatePictureValues {
106+ v.Mask |= CpClipYOrigin
107+ v.Clipyorigin = x
108+ return v
109+}
110+func (v *CreatePictureValues) SetClipmask(x xproto.Pixmap) *CreatePictureValues {
111+ v.Mask |= CpClipMask
112+ v.Clipmask = x
113+ return v
114+}
115+func (v *CreatePictureValues) SetGraphicsexposure(x uint32) *CreatePictureValues {
116+ v.Mask |= CpGraphicsExposure
117+ v.Graphicsexposure = x
118+ return v
119+}
120+func (v *CreatePictureValues) SetSubwindowmode(x xproto.SubwindowMode) *CreatePictureValues {
121+ v.Mask |= CpSubwindowMode
122+ v.Subwindowmode = x
123+ return v
124+}
125+func (v *CreatePictureValues) SetPolyedge(x PolyEdge) *CreatePictureValues {
126+ v.Mask |= CpPolyEdge
127+ v.Polyedge = x
128+ return v
129+}
130+func (v *CreatePictureValues) SetPolymode(x PolyMode) *CreatePictureValues {
131+ v.Mask |= CpPolyMode
132+ v.Polymode = x
133+ return v
134+}
135+func (v *CreatePictureValues) SetDither(x xproto.Atom) *CreatePictureValues {
136+ v.Mask |= CpDither
137+ v.Dither = x
138+ return v
139+}
140+func (v *CreatePictureValues) SetComponentalpha(x uint32) *CreatePictureValues {
141+ v.Mask |= CpComponentAlpha
142+ v.Componentalpha = x
143+ return v
144+}
145+func CreatePicture(b *xturbo.Buf, pid Picture, drawable xproto.Drawable, format Pictformat, v *CreatePictureValues) xturbo.Seq {
146+ if b.Ext == nil {
147+ b.Fail(xturbo.ErrExtMissing)
148+ return 0
149+ }
150+ major := b.Ext.Major(xturbo.ExtRender)
151+ if major == 0 {
152+ b.Fail(xturbo.ErrExtMissing)
153+ return 0
154+ }
155+ p, seq := b.Request(major, 4, 16+4*bits.OnesCount32(uint32(v.Mask)))
156+ if p == nil {
157+ return 0
158+ }
159+ binary.LittleEndian.PutUint32(p[0:], uint32(pid))
160+ binary.LittleEndian.PutUint32(p[4:], uint32(drawable))
161+ binary.LittleEndian.PutUint32(p[8:], uint32(format))
162+ binary.LittleEndian.PutUint32(p[12:], uint32(v.Mask))
163+ off := 16
164+ if v.Mask&CpRepeat != 0 {
165+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Repeat))
166+ off += 4
167+ }
168+ if v.Mask&CpAlphaMap != 0 {
169+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphamap))
170+ off += 4
171+ }
172+ if v.Mask&CpAlphaXOrigin != 0 {
173+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphaxorigin))
174+ off += 4
175+ }
176+ if v.Mask&CpAlphaYOrigin != 0 {
177+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphayorigin))
178+ off += 4
179+ }
180+ if v.Mask&CpClipXOrigin != 0 {
181+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipxorigin))
182+ off += 4
183+ }
184+ if v.Mask&CpClipYOrigin != 0 {
185+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipyorigin))
186+ off += 4
187+ }
188+ if v.Mask&CpClipMask != 0 {
189+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipmask))
190+ off += 4
191+ }
192+ if v.Mask&CpGraphicsExposure != 0 {
193+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Graphicsexposure))
194+ off += 4
195+ }
196+ if v.Mask&CpSubwindowMode != 0 {
197+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Subwindowmode))
198+ off += 4
199+ }
200+ if v.Mask&CpPolyEdge != 0 {
201+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Polyedge))
202+ off += 4
203+ }
204+ if v.Mask&CpPolyMode != 0 {
205+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Polymode))
206+ off += 4
207+ }
208+ if v.Mask&CpDither != 0 {
209+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Dither))
210+ off += 4
211+ }
212+ if v.Mask&CpComponentAlpha != 0 {
213+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Componentalpha))
214+ off += 4
215+ }
216+ _ = off
217+ return seq
218+}
219+
220+type ChangePictureValues struct {
221+ Mask Cp
222+ Repeat Repeat
223+ Alphamap Picture
224+ Alphaxorigin int32
225+ Alphayorigin int32
226+ Clipxorigin int32
227+ Clipyorigin int32
228+ Clipmask xproto.Pixmap
229+ Graphicsexposure uint32
230+ Subwindowmode xproto.SubwindowMode
231+ Polyedge PolyEdge
232+ Polymode PolyMode
233+ Dither xproto.Atom
234+ Componentalpha uint32
235+}
236+
237+func (v *ChangePictureValues) SetRepeat(x Repeat) *ChangePictureValues {
238+ v.Mask |= CpRepeat
239+ v.Repeat = x
240+ return v
241+}
242+func (v *ChangePictureValues) SetAlphamap(x Picture) *ChangePictureValues {
243+ v.Mask |= CpAlphaMap
244+ v.Alphamap = x
245+ return v
246+}
247+func (v *ChangePictureValues) SetAlphaxorigin(x int32) *ChangePictureValues {
248+ v.Mask |= CpAlphaXOrigin
249+ v.Alphaxorigin = x
250+ return v
251+}
252+func (v *ChangePictureValues) SetAlphayorigin(x int32) *ChangePictureValues {
253+ v.Mask |= CpAlphaYOrigin
254+ v.Alphayorigin = x
255+ return v
256+}
257+func (v *ChangePictureValues) SetClipxorigin(x int32) *ChangePictureValues {
258+ v.Mask |= CpClipXOrigin
259+ v.Clipxorigin = x
260+ return v
261+}
262+func (v *ChangePictureValues) SetClipyorigin(x int32) *ChangePictureValues {
263+ v.Mask |= CpClipYOrigin
264+ v.Clipyorigin = x
265+ return v
266+}
267+func (v *ChangePictureValues) SetClipmask(x xproto.Pixmap) *ChangePictureValues {
268+ v.Mask |= CpClipMask
269+ v.Clipmask = x
270+ return v
271+}
272+func (v *ChangePictureValues) SetGraphicsexposure(x uint32) *ChangePictureValues {
273+ v.Mask |= CpGraphicsExposure
274+ v.Graphicsexposure = x
275+ return v
276+}
277+func (v *ChangePictureValues) SetSubwindowmode(x xproto.SubwindowMode) *ChangePictureValues {
278+ v.Mask |= CpSubwindowMode
279+ v.Subwindowmode = x
280+ return v
281+}
282+func (v *ChangePictureValues) SetPolyedge(x PolyEdge) *ChangePictureValues {
283+ v.Mask |= CpPolyEdge
284+ v.Polyedge = x
285+ return v
286+}
287+func (v *ChangePictureValues) SetPolymode(x PolyMode) *ChangePictureValues {
288+ v.Mask |= CpPolyMode
289+ v.Polymode = x
290+ return v
291+}
292+func (v *ChangePictureValues) SetDither(x xproto.Atom) *ChangePictureValues {
293+ v.Mask |= CpDither
294+ v.Dither = x
295+ return v
296+}
297+func (v *ChangePictureValues) SetComponentalpha(x uint32) *ChangePictureValues {
298+ v.Mask |= CpComponentAlpha
299+ v.Componentalpha = x
300+ return v
301+}
302+func ChangePicture(b *xturbo.Buf, picture Picture, v *ChangePictureValues) xturbo.Seq {
303+ if b.Ext == nil {
304+ b.Fail(xturbo.ErrExtMissing)
305+ return 0
306+ }
307+ major := b.Ext.Major(xturbo.ExtRender)
308+ if major == 0 {
309+ b.Fail(xturbo.ErrExtMissing)
310+ return 0
311+ }
312+ p, seq := b.Request(major, 5, 8+4*bits.OnesCount32(uint32(v.Mask)))
313+ if p == nil {
314+ return 0
315+ }
316+ binary.LittleEndian.PutUint32(p[0:], uint32(picture))
317+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Mask))
318+ off := 8
319+ if v.Mask&CpRepeat != 0 {
320+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Repeat))
321+ off += 4
322+ }
323+ if v.Mask&CpAlphaMap != 0 {
324+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphamap))
325+ off += 4
326+ }
327+ if v.Mask&CpAlphaXOrigin != 0 {
328+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphaxorigin))
329+ off += 4
330+ }
331+ if v.Mask&CpAlphaYOrigin != 0 {
332+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Alphayorigin))
333+ off += 4
334+ }
335+ if v.Mask&CpClipXOrigin != 0 {
336+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipxorigin))
337+ off += 4
338+ }
339+ if v.Mask&CpClipYOrigin != 0 {
340+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipyorigin))
341+ off += 4
342+ }
343+ if v.Mask&CpClipMask != 0 {
344+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Clipmask))
345+ off += 4
346+ }
347+ if v.Mask&CpGraphicsExposure != 0 {
348+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Graphicsexposure))
349+ off += 4
350+ }
351+ if v.Mask&CpSubwindowMode != 0 {
352+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Subwindowmode))
353+ off += 4
354+ }
355+ if v.Mask&CpPolyEdge != 0 {
356+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Polyedge))
357+ off += 4
358+ }
359+ if v.Mask&CpPolyMode != 0 {
360+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Polymode))
361+ off += 4
362+ }
363+ if v.Mask&CpDither != 0 {
364+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Dither))
365+ off += 4
366+ }
367+ if v.Mask&CpComponentAlpha != 0 {
368+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Componentalpha))
369+ off += 4
370+ }
371+ _ = off
372+ return seq
373+}
374+
375+func SetPictureClipRectangles(b *xturbo.Buf, picture Picture, clipXOrigin int16, clipYOrigin int16, rectangles []xproto.Rectangle) xturbo.Seq {
376+ size := 0
377+ size += 4
378+ size += 2
379+ size += 2
380+ size += len(rectangles) * 8
381+ size = (size + 3) &^ 3
382+ if b.Ext == nil {
383+ b.Fail(xturbo.ErrExtMissing)
384+ return 0
385+ }
386+ major := b.Ext.Major(xturbo.ExtRender)
387+ if major == 0 {
388+ b.Fail(xturbo.ErrExtMissing)
389+ return 0
390+ }
391+ p, seq := b.Request(major, 6, size)
392+ if p == nil {
393+ return 0
394+ }
395+ off := 0
396+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
397+ off += 4
398+ binary.LittleEndian.PutUint16(p[off:], uint16(clipXOrigin))
399+ off += 2
400+ binary.LittleEndian.PutUint16(p[off:], uint16(clipYOrigin))
401+ off += 2
402+ for i := range rectangles {
403+ rectangles[i].Put(p[off:])
404+ off += 8
405+ }
406+ return seq
407+}
408+
409+func FreePicture(b *xturbo.Buf, picture Picture) xturbo.Seq {
410+ if b.Ext == nil {
411+ b.Fail(xturbo.ErrExtMissing)
412+ return 0
413+ }
414+ major := b.Ext.Major(xturbo.ExtRender)
415+ if major == 0 {
416+ b.Fail(xturbo.ErrExtMissing)
417+ return 0
418+ }
419+ p, seq := b.Request(major, 7, 4)
420+ if p == nil {
421+ return 0
422+ }
423+ binary.LittleEndian.PutUint32(p[0:], uint32(picture))
424+ return seq
425+}
426+
427+func Composite(b *xturbo.Buf, op PictOp, src Picture, mask Picture, dst Picture, srcX int16, srcY int16, maskX int16, maskY int16, dstX int16, dstY int16, width uint16, height uint16) xturbo.Seq {
428+ if b.Ext == nil {
429+ b.Fail(xturbo.ErrExtMissing)
430+ return 0
431+ }
432+ major := b.Ext.Major(xturbo.ExtRender)
433+ if major == 0 {
434+ b.Fail(xturbo.ErrExtMissing)
435+ return 0
436+ }
437+ p, seq := b.Request(major, 8, 32)
438+ if p == nil {
439+ return 0
440+ }
441+ p[0:][0] = byte(op)
442+ binary.LittleEndian.PutUint32(p[4:], uint32(src))
443+ binary.LittleEndian.PutUint32(p[8:], uint32(mask))
444+ binary.LittleEndian.PutUint32(p[12:], uint32(dst))
445+ binary.LittleEndian.PutUint16(p[16:], uint16(srcX))
446+ binary.LittleEndian.PutUint16(p[18:], uint16(srcY))
447+ binary.LittleEndian.PutUint16(p[20:], uint16(maskX))
448+ binary.LittleEndian.PutUint16(p[22:], uint16(maskY))
449+ binary.LittleEndian.PutUint16(p[24:], uint16(dstX))
450+ binary.LittleEndian.PutUint16(p[26:], uint16(dstY))
451+ binary.LittleEndian.PutUint16(p[28:], uint16(width))
452+ binary.LittleEndian.PutUint16(p[30:], uint16(height))
453+ return seq
454+}
455+
456+func Trapezoids(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, srcX int16, srcY int16, traps []Trapezoid) xturbo.Seq {
457+ size := 0
458+ size += 1
459+ size += 3
460+ size += 4
461+ size += 4
462+ size += 4
463+ size += 2
464+ size += 2
465+ size += len(traps) * 40
466+ size = (size + 3) &^ 3
467+ if b.Ext == nil {
468+ b.Fail(xturbo.ErrExtMissing)
469+ return 0
470+ }
471+ major := b.Ext.Major(xturbo.ExtRender)
472+ if major == 0 {
473+ b.Fail(xturbo.ErrExtMissing)
474+ return 0
475+ }
476+ p, seq := b.Request(major, 10, size)
477+ if p == nil {
478+ return 0
479+ }
480+ off := 0
481+ p[off:][0] = byte(op)
482+ off += 1
483+ off += 3
484+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
485+ off += 4
486+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
487+ off += 4
488+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
489+ off += 4
490+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
491+ off += 2
492+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
493+ off += 2
494+ for i := range traps {
495+ traps[i].Put(p[off:])
496+ off += 40
497+ }
498+ return seq
499+}
500+
501+func Triangles(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, srcX int16, srcY int16, triangles []Triangle) xturbo.Seq {
502+ size := 0
503+ size += 1
504+ size += 3
505+ size += 4
506+ size += 4
507+ size += 4
508+ size += 2
509+ size += 2
510+ size += len(triangles) * 24
511+ size = (size + 3) &^ 3
512+ if b.Ext == nil {
513+ b.Fail(xturbo.ErrExtMissing)
514+ return 0
515+ }
516+ major := b.Ext.Major(xturbo.ExtRender)
517+ if major == 0 {
518+ b.Fail(xturbo.ErrExtMissing)
519+ return 0
520+ }
521+ p, seq := b.Request(major, 11, size)
522+ if p == nil {
523+ return 0
524+ }
525+ off := 0
526+ p[off:][0] = byte(op)
527+ off += 1
528+ off += 3
529+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
530+ off += 4
531+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
532+ off += 4
533+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
534+ off += 4
535+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
536+ off += 2
537+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
538+ off += 2
539+ for i := range triangles {
540+ triangles[i].Put(p[off:])
541+ off += 24
542+ }
543+ return seq
544+}
545+
546+func TriStrip(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, srcX int16, srcY int16, points []Pointfix) xturbo.Seq {
547+ size := 0
548+ size += 1
549+ size += 3
550+ size += 4
551+ size += 4
552+ size += 4
553+ size += 2
554+ size += 2
555+ size += len(points) * 8
556+ size = (size + 3) &^ 3
557+ if b.Ext == nil {
558+ b.Fail(xturbo.ErrExtMissing)
559+ return 0
560+ }
561+ major := b.Ext.Major(xturbo.ExtRender)
562+ if major == 0 {
563+ b.Fail(xturbo.ErrExtMissing)
564+ return 0
565+ }
566+ p, seq := b.Request(major, 12, size)
567+ if p == nil {
568+ return 0
569+ }
570+ off := 0
571+ p[off:][0] = byte(op)
572+ off += 1
573+ off += 3
574+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
575+ off += 4
576+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
577+ off += 4
578+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
579+ off += 4
580+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
581+ off += 2
582+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
583+ off += 2
584+ for i := range points {
585+ points[i].Put(p[off:])
586+ off += 8
587+ }
588+ return seq
589+}
590+
591+func TriFan(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, srcX int16, srcY int16, points []Pointfix) xturbo.Seq {
592+ size := 0
593+ size += 1
594+ size += 3
595+ size += 4
596+ size += 4
597+ size += 4
598+ size += 2
599+ size += 2
600+ size += len(points) * 8
601+ size = (size + 3) &^ 3
602+ if b.Ext == nil {
603+ b.Fail(xturbo.ErrExtMissing)
604+ return 0
605+ }
606+ major := b.Ext.Major(xturbo.ExtRender)
607+ if major == 0 {
608+ b.Fail(xturbo.ErrExtMissing)
609+ return 0
610+ }
611+ p, seq := b.Request(major, 13, size)
612+ if p == nil {
613+ return 0
614+ }
615+ off := 0
616+ p[off:][0] = byte(op)
617+ off += 1
618+ off += 3
619+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
620+ off += 4
621+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
622+ off += 4
623+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
624+ off += 4
625+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
626+ off += 2
627+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
628+ off += 2
629+ for i := range points {
630+ points[i].Put(p[off:])
631+ off += 8
632+ }
633+ return seq
634+}
635+
636+func CreateGlyphSet(b *xturbo.Buf, gsid Glyphset, format Pictformat) xturbo.Seq {
637+ if b.Ext == nil {
638+ b.Fail(xturbo.ErrExtMissing)
639+ return 0
640+ }
641+ major := b.Ext.Major(xturbo.ExtRender)
642+ if major == 0 {
643+ b.Fail(xturbo.ErrExtMissing)
644+ return 0
645+ }
646+ p, seq := b.Request(major, 17, 8)
647+ if p == nil {
648+ return 0
649+ }
650+ binary.LittleEndian.PutUint32(p[0:], uint32(gsid))
651+ binary.LittleEndian.PutUint32(p[4:], uint32(format))
652+ return seq
653+}
654+
655+func ReferenceGlyphSet(b *xturbo.Buf, gsid Glyphset, existing Glyphset) xturbo.Seq {
656+ if b.Ext == nil {
657+ b.Fail(xturbo.ErrExtMissing)
658+ return 0
659+ }
660+ major := b.Ext.Major(xturbo.ExtRender)
661+ if major == 0 {
662+ b.Fail(xturbo.ErrExtMissing)
663+ return 0
664+ }
665+ p, seq := b.Request(major, 18, 8)
666+ if p == nil {
667+ return 0
668+ }
669+ binary.LittleEndian.PutUint32(p[0:], uint32(gsid))
670+ binary.LittleEndian.PutUint32(p[4:], uint32(existing))
671+ return seq
672+}
673+
674+func FreeGlyphSet(b *xturbo.Buf, glyphset Glyphset) xturbo.Seq {
675+ if b.Ext == nil {
676+ b.Fail(xturbo.ErrExtMissing)
677+ return 0
678+ }
679+ major := b.Ext.Major(xturbo.ExtRender)
680+ if major == 0 {
681+ b.Fail(xturbo.ErrExtMissing)
682+ return 0
683+ }
684+ p, seq := b.Request(major, 19, 4)
685+ if p == nil {
686+ return 0
687+ }
688+ binary.LittleEndian.PutUint32(p[0:], uint32(glyphset))
689+ return seq
690+}
691+
692+func AddGlyphs(b *xturbo.Buf, glyphset Glyphset, glyphsLen uint32, glyphids []uint32, glyphs []Glyphinfo, data []byte) xturbo.Seq {
693+ if len(glyphids) != int(glyphsLen) {
694+ b.Fail(xturbo.ErrBadLength)
695+ return 0
696+ }
697+ if len(glyphs) != int(glyphsLen) {
698+ b.Fail(xturbo.ErrBadLength)
699+ return 0
700+ }
701+ size := 0
702+ size += 4
703+ size += 4
704+ size += len(glyphids) * 4
705+ size += len(glyphs) * 12
706+ size += len(data) * 1
707+ size = (size + 3) &^ 3
708+ if b.Ext == nil {
709+ b.Fail(xturbo.ErrExtMissing)
710+ return 0
711+ }
712+ major := b.Ext.Major(xturbo.ExtRender)
713+ if major == 0 {
714+ b.Fail(xturbo.ErrExtMissing)
715+ return 0
716+ }
717+ p, seq := b.Request(major, 20, size)
718+ if p == nil {
719+ return 0
720+ }
721+ off := 0
722+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
723+ off += 4
724+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphsLen))
725+ off += 4
726+ for i := range glyphids {
727+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphids[i]))
728+ off += 4
729+ }
730+ for i := range glyphs {
731+ glyphs[i].Put(p[off:])
732+ off += 12
733+ }
734+ copy(p[off:], data)
735+ off += len(data)
736+ return seq
737+}
738+
739+func FreeGlyphs(b *xturbo.Buf, glyphset Glyphset, glyphs []Glyph) xturbo.Seq {
740+ size := 0
741+ size += 4
742+ size += len(glyphs) * 4
743+ size = (size + 3) &^ 3
744+ if b.Ext == nil {
745+ b.Fail(xturbo.ErrExtMissing)
746+ return 0
747+ }
748+ major := b.Ext.Major(xturbo.ExtRender)
749+ if major == 0 {
750+ b.Fail(xturbo.ErrExtMissing)
751+ return 0
752+ }
753+ p, seq := b.Request(major, 22, size)
754+ if p == nil {
755+ return 0
756+ }
757+ off := 0
758+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
759+ off += 4
760+ for i := range glyphs {
761+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphs[i]))
762+ off += 4
763+ }
764+ return seq
765+}
766+
767+func CompositeGlyphs8(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, glyphset Glyphset, srcX int16, srcY int16, glyphcmds []byte) xturbo.Seq {
768+ size := 0
769+ size += 1
770+ size += 3
771+ size += 4
772+ size += 4
773+ size += 4
774+ size += 4
775+ size += 2
776+ size += 2
777+ size += len(glyphcmds) * 1
778+ size = (size + 3) &^ 3
779+ if b.Ext == nil {
780+ b.Fail(xturbo.ErrExtMissing)
781+ return 0
782+ }
783+ major := b.Ext.Major(xturbo.ExtRender)
784+ if major == 0 {
785+ b.Fail(xturbo.ErrExtMissing)
786+ return 0
787+ }
788+ p, seq := b.Request(major, 23, size)
789+ if p == nil {
790+ return 0
791+ }
792+ off := 0
793+ p[off:][0] = byte(op)
794+ off += 1
795+ off += 3
796+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
797+ off += 4
798+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
799+ off += 4
800+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
801+ off += 4
802+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
803+ off += 4
804+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
805+ off += 2
806+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
807+ off += 2
808+ copy(p[off:], glyphcmds)
809+ off += len(glyphcmds)
810+ return seq
811+}
812+
813+func CompositeGlyphs16(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, glyphset Glyphset, srcX int16, srcY int16, glyphcmds []byte) xturbo.Seq {
814+ size := 0
815+ size += 1
816+ size += 3
817+ size += 4
818+ size += 4
819+ size += 4
820+ size += 4
821+ size += 2
822+ size += 2
823+ size += len(glyphcmds) * 1
824+ size = (size + 3) &^ 3
825+ if b.Ext == nil {
826+ b.Fail(xturbo.ErrExtMissing)
827+ return 0
828+ }
829+ major := b.Ext.Major(xturbo.ExtRender)
830+ if major == 0 {
831+ b.Fail(xturbo.ErrExtMissing)
832+ return 0
833+ }
834+ p, seq := b.Request(major, 24, size)
835+ if p == nil {
836+ return 0
837+ }
838+ off := 0
839+ p[off:][0] = byte(op)
840+ off += 1
841+ off += 3
842+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
843+ off += 4
844+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
845+ off += 4
846+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
847+ off += 4
848+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
849+ off += 4
850+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
851+ off += 2
852+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
853+ off += 2
854+ copy(p[off:], glyphcmds)
855+ off += len(glyphcmds)
856+ return seq
857+}
858+
859+func CompositeGlyphs32(b *xturbo.Buf, op PictOp, src Picture, dst Picture, maskFormat Pictformat, glyphset Glyphset, srcX int16, srcY int16, glyphcmds []byte) xturbo.Seq {
860+ size := 0
861+ size += 1
862+ size += 3
863+ size += 4
864+ size += 4
865+ size += 4
866+ size += 4
867+ size += 2
868+ size += 2
869+ size += len(glyphcmds) * 1
870+ size = (size + 3) &^ 3
871+ if b.Ext == nil {
872+ b.Fail(xturbo.ErrExtMissing)
873+ return 0
874+ }
875+ major := b.Ext.Major(xturbo.ExtRender)
876+ if major == 0 {
877+ b.Fail(xturbo.ErrExtMissing)
878+ return 0
879+ }
880+ p, seq := b.Request(major, 25, size)
881+ if p == nil {
882+ return 0
883+ }
884+ off := 0
885+ p[off:][0] = byte(op)
886+ off += 1
887+ off += 3
888+ binary.LittleEndian.PutUint32(p[off:], uint32(src))
889+ off += 4
890+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
891+ off += 4
892+ binary.LittleEndian.PutUint32(p[off:], uint32(maskFormat))
893+ off += 4
894+ binary.LittleEndian.PutUint32(p[off:], uint32(glyphset))
895+ off += 4
896+ binary.LittleEndian.PutUint16(p[off:], uint16(srcX))
897+ off += 2
898+ binary.LittleEndian.PutUint16(p[off:], uint16(srcY))
899+ off += 2
900+ copy(p[off:], glyphcmds)
901+ off += len(glyphcmds)
902+ return seq
903+}
904+
905+func FillRectangles(b *xturbo.Buf, op PictOp, dst Picture, color Color, rects []xproto.Rectangle) xturbo.Seq {
906+ size := 0
907+ size += 1
908+ size += 3
909+ size += 4
910+ size += 8
911+ size += len(rects) * 8
912+ size = (size + 3) &^ 3
913+ if b.Ext == nil {
914+ b.Fail(xturbo.ErrExtMissing)
915+ return 0
916+ }
917+ major := b.Ext.Major(xturbo.ExtRender)
918+ if major == 0 {
919+ b.Fail(xturbo.ErrExtMissing)
920+ return 0
921+ }
922+ p, seq := b.Request(major, 26, size)
923+ if p == nil {
924+ return 0
925+ }
926+ off := 0
927+ p[off:][0] = byte(op)
928+ off += 1
929+ off += 3
930+ binary.LittleEndian.PutUint32(p[off:], uint32(dst))
931+ off += 4
932+ color.Put(p[off:])
933+ off += 8
934+ for i := range rects {
935+ rects[i].Put(p[off:])
936+ off += 8
937+ }
938+ return seq
939+}
940+
941+func CreateCursor(b *xturbo.Buf, cid xproto.Cursor, source Picture, x uint16, y uint16) xturbo.Seq {
942+ if b.Ext == nil {
943+ b.Fail(xturbo.ErrExtMissing)
944+ return 0
945+ }
946+ major := b.Ext.Major(xturbo.ExtRender)
947+ if major == 0 {
948+ b.Fail(xturbo.ErrExtMissing)
949+ return 0
950+ }
951+ p, seq := b.Request(major, 27, 12)
952+ if p == nil {
953+ return 0
954+ }
955+ binary.LittleEndian.PutUint32(p[0:], uint32(cid))
956+ binary.LittleEndian.PutUint32(p[4:], uint32(source))
957+ binary.LittleEndian.PutUint16(p[8:], uint16(x))
958+ binary.LittleEndian.PutUint16(p[10:], uint16(y))
959+ return seq
960+}
961+
962+func SetPictureTransform(b *xturbo.Buf, picture Picture, transform Transform) xturbo.Seq {
963+ if b.Ext == nil {
964+ b.Fail(xturbo.ErrExtMissing)
965+ return 0
966+ }
967+ major := b.Ext.Major(xturbo.ExtRender)
968+ if major == 0 {
969+ b.Fail(xturbo.ErrExtMissing)
970+ return 0
971+ }
972+ p, seq := b.Request(major, 28, 40)
973+ if p == nil {
974+ return 0
975+ }
976+ binary.LittleEndian.PutUint32(p[0:], uint32(picture))
977+ transform.Put(p[4:])
978+ return seq
979+}
980+
981+func QueryFilters(b *xturbo.Buf, drawable xproto.Drawable) xturbo.Seq {
982+ if b.Ext == nil {
983+ b.Fail(xturbo.ErrExtMissing)
984+ return 0
985+ }
986+ major := b.Ext.Major(xturbo.ExtRender)
987+ if major == 0 {
988+ b.Fail(xturbo.ErrExtMissing)
989+ return 0
990+ }
991+ p, seq := b.Request(major, 29, 4)
992+ if p == nil {
993+ return 0
994+ }
995+ binary.LittleEndian.PutUint32(p[0:], uint32(drawable))
996+ return seq
997+}
998+
999+func SetPictureFilter(b *xturbo.Buf, picture Picture, filter string, values []Fixed) xturbo.Seq {
1000+ if uint64(len(filter)) > 65535 {
1001+ b.Fail(xturbo.ErrBadLength)
1002+ return 0
1003+ }
1004+ size := 0
1005+ size += 4
1006+ size += 2
1007+ size += 2
1008+ size += len(filter) * 1
1009+ size = (size + 3) &^ 3
1010+ size += len(values) * 4
1011+ size = (size + 3) &^ 3
1012+ if b.Ext == nil {
1013+ b.Fail(xturbo.ErrExtMissing)
1014+ return 0
1015+ }
1016+ major := b.Ext.Major(xturbo.ExtRender)
1017+ if major == 0 {
1018+ b.Fail(xturbo.ErrExtMissing)
1019+ return 0
1020+ }
1021+ p, seq := b.Request(major, 30, size)
1022+ if p == nil {
1023+ return 0
1024+ }
1025+ off := 0
1026+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1027+ off += 4
1028+ binary.LittleEndian.PutUint16(p[off:], uint16(len(filter)))
1029+ off += 2
1030+ off += 2
1031+ copy(p[off:], filter)
1032+ off += len(filter)
1033+ off = (off + 3) &^ 3
1034+ for i := range values {
1035+ binary.LittleEndian.PutUint32(p[off:], uint32(values[i]))
1036+ off += 4
1037+ }
1038+ return seq
1039+}
1040+
1041+func CreateAnimCursor(b *xturbo.Buf, cid xproto.Cursor, cursors []Animcursorelt) xturbo.Seq {
1042+ size := 0
1043+ size += 4
1044+ size += len(cursors) * 8
1045+ size = (size + 3) &^ 3
1046+ if b.Ext == nil {
1047+ b.Fail(xturbo.ErrExtMissing)
1048+ return 0
1049+ }
1050+ major := b.Ext.Major(xturbo.ExtRender)
1051+ if major == 0 {
1052+ b.Fail(xturbo.ErrExtMissing)
1053+ return 0
1054+ }
1055+ p, seq := b.Request(major, 31, size)
1056+ if p == nil {
1057+ return 0
1058+ }
1059+ off := 0
1060+ binary.LittleEndian.PutUint32(p[off:], uint32(cid))
1061+ off += 4
1062+ for i := range cursors {
1063+ cursors[i].Put(p[off:])
1064+ off += 8
1065+ }
1066+ return seq
1067+}
1068+
1069+func AddTraps(b *xturbo.Buf, picture Picture, xOff int16, yOff int16, traps []Trap) xturbo.Seq {
1070+ size := 0
1071+ size += 4
1072+ size += 2
1073+ size += 2
1074+ size += len(traps) * 24
1075+ size = (size + 3) &^ 3
1076+ if b.Ext == nil {
1077+ b.Fail(xturbo.ErrExtMissing)
1078+ return 0
1079+ }
1080+ major := b.Ext.Major(xturbo.ExtRender)
1081+ if major == 0 {
1082+ b.Fail(xturbo.ErrExtMissing)
1083+ return 0
1084+ }
1085+ p, seq := b.Request(major, 32, size)
1086+ if p == nil {
1087+ return 0
1088+ }
1089+ off := 0
1090+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1091+ off += 4
1092+ binary.LittleEndian.PutUint16(p[off:], uint16(xOff))
1093+ off += 2
1094+ binary.LittleEndian.PutUint16(p[off:], uint16(yOff))
1095+ off += 2
1096+ for i := range traps {
1097+ traps[i].Put(p[off:])
1098+ off += 24
1099+ }
1100+ return seq
1101+}
1102+
1103+func CreateSolidFill(b *xturbo.Buf, picture Picture, color Color) xturbo.Seq {
1104+ if b.Ext == nil {
1105+ b.Fail(xturbo.ErrExtMissing)
1106+ return 0
1107+ }
1108+ major := b.Ext.Major(xturbo.ExtRender)
1109+ if major == 0 {
1110+ b.Fail(xturbo.ErrExtMissing)
1111+ return 0
1112+ }
1113+ p, seq := b.Request(major, 33, 12)
1114+ if p == nil {
1115+ return 0
1116+ }
1117+ binary.LittleEndian.PutUint32(p[0:], uint32(picture))
1118+ color.Put(p[4:])
1119+ return seq
1120+}
1121+
1122+func CreateLinearGradient(b *xturbo.Buf, picture Picture, p1 Pointfix, p2 Pointfix, numStops uint32, stops []Fixed, colors []Color) xturbo.Seq {
1123+ if len(stops) != int(numStops) {
1124+ b.Fail(xturbo.ErrBadLength)
1125+ return 0
1126+ }
1127+ if len(colors) != int(numStops) {
1128+ b.Fail(xturbo.ErrBadLength)
1129+ return 0
1130+ }
1131+ size := 0
1132+ size += 4
1133+ size += 8
1134+ size += 8
1135+ size += 4
1136+ size += len(stops) * 4
1137+ size += len(colors) * 8
1138+ size = (size + 3) &^ 3
1139+ if b.Ext == nil {
1140+ b.Fail(xturbo.ErrExtMissing)
1141+ return 0
1142+ }
1143+ major := b.Ext.Major(xturbo.ExtRender)
1144+ if major == 0 {
1145+ b.Fail(xturbo.ErrExtMissing)
1146+ return 0
1147+ }
1148+ p, seq := b.Request(major, 34, size)
1149+ if p == nil {
1150+ return 0
1151+ }
1152+ off := 0
1153+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1154+ off += 4
1155+ p1.Put(p[off:])
1156+ off += 8
1157+ p2.Put(p[off:])
1158+ off += 8
1159+ binary.LittleEndian.PutUint32(p[off:], uint32(numStops))
1160+ off += 4
1161+ for i := range stops {
1162+ binary.LittleEndian.PutUint32(p[off:], uint32(stops[i]))
1163+ off += 4
1164+ }
1165+ for i := range colors {
1166+ colors[i].Put(p[off:])
1167+ off += 8
1168+ }
1169+ return seq
1170+}
1171+
1172+func CreateRadialGradient(b *xturbo.Buf, picture Picture, inner Pointfix, outer Pointfix, innerRadius Fixed, outerRadius Fixed, numStops uint32, stops []Fixed, colors []Color) xturbo.Seq {
1173+ if len(stops) != int(numStops) {
1174+ b.Fail(xturbo.ErrBadLength)
1175+ return 0
1176+ }
1177+ if len(colors) != int(numStops) {
1178+ b.Fail(xturbo.ErrBadLength)
1179+ return 0
1180+ }
1181+ size := 0
1182+ size += 4
1183+ size += 8
1184+ size += 8
1185+ size += 4
1186+ size += 4
1187+ size += 4
1188+ size += len(stops) * 4
1189+ size += len(colors) * 8
1190+ size = (size + 3) &^ 3
1191+ if b.Ext == nil {
1192+ b.Fail(xturbo.ErrExtMissing)
1193+ return 0
1194+ }
1195+ major := b.Ext.Major(xturbo.ExtRender)
1196+ if major == 0 {
1197+ b.Fail(xturbo.ErrExtMissing)
1198+ return 0
1199+ }
1200+ p, seq := b.Request(major, 35, size)
1201+ if p == nil {
1202+ return 0
1203+ }
1204+ off := 0
1205+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1206+ off += 4
1207+ inner.Put(p[off:])
1208+ off += 8
1209+ outer.Put(p[off:])
1210+ off += 8
1211+ binary.LittleEndian.PutUint32(p[off:], uint32(innerRadius))
1212+ off += 4
1213+ binary.LittleEndian.PutUint32(p[off:], uint32(outerRadius))
1214+ off += 4
1215+ binary.LittleEndian.PutUint32(p[off:], uint32(numStops))
1216+ off += 4
1217+ for i := range stops {
1218+ binary.LittleEndian.PutUint32(p[off:], uint32(stops[i]))
1219+ off += 4
1220+ }
1221+ for i := range colors {
1222+ colors[i].Put(p[off:])
1223+ off += 8
1224+ }
1225+ return seq
1226+}
1227+
1228+func CreateConicalGradient(b *xturbo.Buf, picture Picture, center Pointfix, angle Fixed, numStops uint32, stops []Fixed, colors []Color) xturbo.Seq {
1229+ if len(stops) != int(numStops) {
1230+ b.Fail(xturbo.ErrBadLength)
1231+ return 0
1232+ }
1233+ if len(colors) != int(numStops) {
1234+ b.Fail(xturbo.ErrBadLength)
1235+ return 0
1236+ }
1237+ size := 0
1238+ size += 4
1239+ size += 8
1240+ size += 4
1241+ size += 4
1242+ size += len(stops) * 4
1243+ size += len(colors) * 8
1244+ size = (size + 3) &^ 3
1245+ if b.Ext == nil {
1246+ b.Fail(xturbo.ErrExtMissing)
1247+ return 0
1248+ }
1249+ major := b.Ext.Major(xturbo.ExtRender)
1250+ if major == 0 {
1251+ b.Fail(xturbo.ErrExtMissing)
1252+ return 0
1253+ }
1254+ p, seq := b.Request(major, 36, size)
1255+ if p == nil {
1256+ return 0
1257+ }
1258+ off := 0
1259+ binary.LittleEndian.PutUint32(p[off:], uint32(picture))
1260+ off += 4
1261+ center.Put(p[off:])
1262+ off += 8
1263+ binary.LittleEndian.PutUint32(p[off:], uint32(angle))
1264+ off += 4
1265+ binary.LittleEndian.PutUint32(p[off:], uint32(numStops))
1266+ off += 4
1267+ for i := range stops {
1268+ binary.LittleEndian.PutUint32(p[off:], uint32(stops[i]))
1269+ off += 4
1270+ }
1271+ for i := range colors {
1272+ colors[i].Put(p[off:])
1273+ off += 8
1274+ }
1275+ return seq
1276+}
A render/sizes_test.go +68 -0
@@ -0,0 +1,68 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "testing"
5+
6+func TestWireSizes(t *testing.T) {
7+ if DirectformatSize != 16 {
8+ t.Errorf("Directformat fixed prefix = %d", DirectformatSize)
9+ }
10+ if PictforminfoSize != 28 {
11+ t.Errorf("Pictforminfo fixed prefix = %d", PictforminfoSize)
12+ }
13+ if PictvisualSize != 8 {
14+ t.Errorf("Pictvisual fixed prefix = %d", PictvisualSize)
15+ }
16+ if PictdepthSize != 8 {
17+ t.Errorf("Pictdepth fixed prefix = %d", PictdepthSize)
18+ }
19+ if PictscreenSize != 8 {
20+ t.Errorf("Pictscreen fixed prefix = %d", PictscreenSize)
21+ }
22+ if IndexvalueSize != 12 {
23+ t.Errorf("Indexvalue fixed prefix = %d", IndexvalueSize)
24+ }
25+ if ColorSize != 8 {
26+ t.Errorf("Color fixed prefix = %d", ColorSize)
27+ }
28+ if PointfixSize != 8 {
29+ t.Errorf("Pointfix fixed prefix = %d", PointfixSize)
30+ }
31+ if LinefixSize != 16 {
32+ t.Errorf("Linefix fixed prefix = %d", LinefixSize)
33+ }
34+ if TriangleSize != 24 {
35+ t.Errorf("Triangle fixed prefix = %d", TriangleSize)
36+ }
37+ if TrapezoidSize != 40 {
38+ t.Errorf("Trapezoid fixed prefix = %d", TrapezoidSize)
39+ }
40+ if GlyphinfoSize != 12 {
41+ t.Errorf("Glyphinfo fixed prefix = %d", GlyphinfoSize)
42+ }
43+ if TransformSize != 36 {
44+ t.Errorf("Transform fixed prefix = %d", TransformSize)
45+ }
46+ if AnimcursoreltSize != 8 {
47+ t.Errorf("Animcursorelt fixed prefix = %d", AnimcursoreltSize)
48+ }
49+ if SpanfixSize != 12 {
50+ t.Errorf("Spanfix fixed prefix = %d", SpanfixSize)
51+ }
52+ if TrapSize != 24 {
53+ t.Errorf("Trap fixed prefix = %d", TrapSize)
54+ }
55+ if got := 32; got != 32 {
56+ t.Errorf("reply QueryVersion fixed size = %d", got)
57+ }
58+ if got := 32; got != 32 {
59+ t.Errorf("reply QueryPictFormats fixed size = %d", got)
60+ }
61+ if got := 32; got != 32 {
62+ t.Errorf("reply QueryPictIndexValues fixed size = %d", got)
63+ }
64+ if got := 32; got != 32 {
65+ t.Errorf("reply QueryFilters fixed size = %d", got)
66+ }
67+
68+}
@@ -0,0 +1,68 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "testing"
5+
6+func TestWireSizes(t *testing.T) {
7+ if DirectformatSize != 16 {
8+ t.Errorf("Directformat fixed prefix = %d", DirectformatSize)
9+ }
10+ if PictforminfoSize != 28 {
11+ t.Errorf("Pictforminfo fixed prefix = %d", PictforminfoSize)
12+ }
13+ if PictvisualSize != 8 {
14+ t.Errorf("Pictvisual fixed prefix = %d", PictvisualSize)
15+ }
16+ if PictdepthSize != 8 {
17+ t.Errorf("Pictdepth fixed prefix = %d", PictdepthSize)
18+ }
19+ if PictscreenSize != 8 {
20+ t.Errorf("Pictscreen fixed prefix = %d", PictscreenSize)
21+ }
22+ if IndexvalueSize != 12 {
23+ t.Errorf("Indexvalue fixed prefix = %d", IndexvalueSize)
24+ }
25+ if ColorSize != 8 {
26+ t.Errorf("Color fixed prefix = %d", ColorSize)
27+ }
28+ if PointfixSize != 8 {
29+ t.Errorf("Pointfix fixed prefix = %d", PointfixSize)
30+ }
31+ if LinefixSize != 16 {
32+ t.Errorf("Linefix fixed prefix = %d", LinefixSize)
33+ }
34+ if TriangleSize != 24 {
35+ t.Errorf("Triangle fixed prefix = %d", TriangleSize)
36+ }
37+ if TrapezoidSize != 40 {
38+ t.Errorf("Trapezoid fixed prefix = %d", TrapezoidSize)
39+ }
40+ if GlyphinfoSize != 12 {
41+ t.Errorf("Glyphinfo fixed prefix = %d", GlyphinfoSize)
42+ }
43+ if TransformSize != 36 {
44+ t.Errorf("Transform fixed prefix = %d", TransformSize)
45+ }
46+ if AnimcursoreltSize != 8 {
47+ t.Errorf("Animcursorelt fixed prefix = %d", AnimcursoreltSize)
48+ }
49+ if SpanfixSize != 12 {
50+ t.Errorf("Spanfix fixed prefix = %d", SpanfixSize)
51+ }
52+ if TrapSize != 24 {
53+ t.Errorf("Trap fixed prefix = %d", TrapSize)
54+ }
55+ if got := 32; got != 32 {
56+ t.Errorf("reply QueryVersion fixed size = %d", got)
57+ }
58+ if got := 32; got != 32 {
59+ t.Errorf("reply QueryPictFormats fixed size = %d", got)
60+ }
61+ if got := 32; got != 32 {
62+ t.Errorf("reply QueryPictIndexValues fixed size = %d", got)
63+ }
64+ if got := 32; got != 32 {
65+ t.Errorf("reply QueryFilters fixed size = %d", got)
66+ }
67+
68+}
A render/types_generated.go +522 -0
@@ -0,0 +1,522 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+type Glyph uint32
8+
9+type Glyphset uint32
10+
11+type Picture uint32
12+
13+type Pictformat uint32
14+
15+type Fixed int32
16+
17+type Directformat struct {
18+ RedShift uint16
19+ RedMask uint16
20+ GreenShift uint16
21+ GreenMask uint16
22+ BlueShift uint16
23+ BlueMask uint16
24+ AlphaShift uint16
25+ AlphaMask uint16
26+}
27+
28+const DirectformatSize = 16
29+
30+func (v Directformat) Put(p []byte) {
31+ clear(p[:16])
32+ binary.LittleEndian.PutUint16(p[0:], uint16(v.RedShift))
33+ binary.LittleEndian.PutUint16(p[2:], uint16(v.RedMask))
34+ binary.LittleEndian.PutUint16(p[4:], uint16(v.GreenShift))
35+ binary.LittleEndian.PutUint16(p[6:], uint16(v.GreenMask))
36+ binary.LittleEndian.PutUint16(p[8:], uint16(v.BlueShift))
37+ binary.LittleEndian.PutUint16(p[10:], uint16(v.BlueMask))
38+ binary.LittleEndian.PutUint16(p[12:], uint16(v.AlphaShift))
39+ binary.LittleEndian.PutUint16(p[14:], uint16(v.AlphaMask))
40+}
41+func GetDirectformat(p []byte) (v Directformat) {
42+ _ = p[15]
43+ v.RedShift = uint16(binary.LittleEndian.Uint16(p[0:]))
44+ v.RedMask = uint16(binary.LittleEndian.Uint16(p[2:]))
45+ v.GreenShift = uint16(binary.LittleEndian.Uint16(p[4:]))
46+ v.GreenMask = uint16(binary.LittleEndian.Uint16(p[6:]))
47+ v.BlueShift = uint16(binary.LittleEndian.Uint16(p[8:]))
48+ v.BlueMask = uint16(binary.LittleEndian.Uint16(p[10:]))
49+ v.AlphaShift = uint16(binary.LittleEndian.Uint16(p[12:]))
50+ v.AlphaMask = uint16(binary.LittleEndian.Uint16(p[14:]))
51+ return
52+}
53+
54+type Pictforminfo struct {
55+ Id Pictformat
56+ Type PictType
57+ Depth uint8
58+ Direct Directformat
59+ Colormap xproto.Colormap
60+}
61+
62+const PictforminfoSize = 28
63+
64+func (v Pictforminfo) Put(p []byte) {
65+ clear(p[:28])
66+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Id))
67+ p[4:][0] = byte(v.Type)
68+ p[5:][0] = byte(v.Depth)
69+ v.Direct.Put(p[8:])
70+ binary.LittleEndian.PutUint32(p[24:], uint32(v.Colormap))
71+}
72+func GetPictforminfo(p []byte) (v Pictforminfo) {
73+ _ = p[27]
74+ v.Id = Pictformat(binary.LittleEndian.Uint32(p[0:]))
75+ v.Type = PictType(p[4:][0])
76+ v.Depth = uint8(p[5:][0])
77+ v.Direct = GetDirectformat(p[8:])
78+ v.Colormap = xproto.Colormap(binary.LittleEndian.Uint32(p[24:]))
79+ return
80+}
81+
82+type Pictvisual struct {
83+ Visual xproto.Visualid
84+ Format Pictformat
85+}
86+
87+const PictvisualSize = 8
88+
89+func (v Pictvisual) Put(p []byte) {
90+ clear(p[:8])
91+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Visual))
92+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Format))
93+}
94+func GetPictvisual(p []byte) (v Pictvisual) {
95+ _ = p[7]
96+ v.Visual = xproto.Visualid(binary.LittleEndian.Uint32(p[0:]))
97+ v.Format = Pictformat(binary.LittleEndian.Uint32(p[4:]))
98+ return
99+}
100+
101+type Pictdepth struct {
102+ Depth uint8
103+ NumVisuals uint16
104+ Visuals []Pictvisual
105+}
106+
107+const PictdepthSize = 8
108+
109+func (v Pictdepth) Size() int {
110+ off := 0
111+ off += 1
112+ off += 1
113+ off += 2
114+ off += 4
115+ off += len(v.Visuals) * 8
116+ return off
117+}
118+func (v Pictdepth) Put(p []byte) {
119+ off := 0
120+ p[off:][0] = byte(v.Depth)
121+ off += 1
122+ clear(p[off : off+1])
123+ off += 1
124+ binary.LittleEndian.PutUint16(p[off:], uint16(v.NumVisuals))
125+ off += 2
126+ clear(p[off : off+4])
127+ off += 4
128+ for i := range v.Visuals {
129+ v.Visuals[i].Put(p[off+i*8:])
130+ }
131+ off += len(v.Visuals) * 8
132+}
133+
134+type Pictscreen struct {
135+ NumDepths uint32
136+ Fallback Pictformat
137+ Depths []Pictdepth
138+}
139+
140+const PictscreenSize = 8
141+
142+func (v Pictscreen) Size() int {
143+ off := 0
144+ off += 4
145+ off += 4
146+ for i := range v.Depths {
147+ off += v.Depths[i].Size()
148+ }
149+ return off
150+}
151+func (v Pictscreen) Put(p []byte) {
152+ off := 0
153+ binary.LittleEndian.PutUint32(p[off:], uint32(v.NumDepths))
154+ off += 4
155+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Fallback))
156+ off += 4
157+ for i := range v.Depths {
158+ v.Depths[i].Put(p[off:])
159+ off += v.Depths[i].Size()
160+ }
161+}
162+
163+type Indexvalue struct {
164+ Pixel uint32
165+ Red uint16
166+ Green uint16
167+ Blue uint16
168+ Alpha uint16
169+}
170+
171+const IndexvalueSize = 12
172+
173+func (v Indexvalue) Put(p []byte) {
174+ clear(p[:12])
175+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Pixel))
176+ binary.LittleEndian.PutUint16(p[4:], uint16(v.Red))
177+ binary.LittleEndian.PutUint16(p[6:], uint16(v.Green))
178+ binary.LittleEndian.PutUint16(p[8:], uint16(v.Blue))
179+ binary.LittleEndian.PutUint16(p[10:], uint16(v.Alpha))
180+}
181+func GetIndexvalue(p []byte) (v Indexvalue) {
182+ _ = p[11]
183+ v.Pixel = uint32(binary.LittleEndian.Uint32(p[0:]))
184+ v.Red = uint16(binary.LittleEndian.Uint16(p[4:]))
185+ v.Green = uint16(binary.LittleEndian.Uint16(p[6:]))
186+ v.Blue = uint16(binary.LittleEndian.Uint16(p[8:]))
187+ v.Alpha = uint16(binary.LittleEndian.Uint16(p[10:]))
188+ return
189+}
190+
191+type Color struct {
192+ Red uint16
193+ Green uint16
194+ Blue uint16
195+ Alpha uint16
196+}
197+
198+const ColorSize = 8
199+
200+func (v Color) Put(p []byte) {
201+ clear(p[:8])
202+ binary.LittleEndian.PutUint16(p[0:], uint16(v.Red))
203+ binary.LittleEndian.PutUint16(p[2:], uint16(v.Green))
204+ binary.LittleEndian.PutUint16(p[4:], uint16(v.Blue))
205+ binary.LittleEndian.PutUint16(p[6:], uint16(v.Alpha))
206+}
207+func GetColor(p []byte) (v Color) {
208+ _ = p[7]
209+ v.Red = uint16(binary.LittleEndian.Uint16(p[0:]))
210+ v.Green = uint16(binary.LittleEndian.Uint16(p[2:]))
211+ v.Blue = uint16(binary.LittleEndian.Uint16(p[4:]))
212+ v.Alpha = uint16(binary.LittleEndian.Uint16(p[6:]))
213+ return
214+}
215+
216+type Pointfix struct {
217+ X Fixed
218+ Y Fixed
219+}
220+
221+const PointfixSize = 8
222+
223+func (v Pointfix) Put(p []byte) {
224+ clear(p[:8])
225+ binary.LittleEndian.PutUint32(p[0:], uint32(v.X))
226+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Y))
227+}
228+func GetPointfix(p []byte) (v Pointfix) {
229+ _ = p[7]
230+ v.X = Fixed(binary.LittleEndian.Uint32(p[0:]))
231+ v.Y = Fixed(binary.LittleEndian.Uint32(p[4:]))
232+ return
233+}
234+
235+type Linefix struct {
236+ P1 Pointfix
237+ P2 Pointfix
238+}
239+
240+const LinefixSize = 16
241+
242+func (v Linefix) Put(p []byte) { clear(p[:16]); v.P1.Put(p[0:]); v.P2.Put(p[8:]) }
243+func GetLinefix(p []byte) (v Linefix) {
244+ _ = p[15]
245+ v.P1 = GetPointfix(p[0:])
246+ v.P2 = GetPointfix(p[8:])
247+ return
248+}
249+
250+type Triangle struct {
251+ P1 Pointfix
252+ P2 Pointfix
253+ P3 Pointfix
254+}
255+
256+const TriangleSize = 24
257+
258+func (v Triangle) Put(p []byte) { clear(p[:24]); v.P1.Put(p[0:]); v.P2.Put(p[8:]); v.P3.Put(p[16:]) }
259+func GetTriangle(p []byte) (v Triangle) {
260+ _ = p[23]
261+ v.P1 = GetPointfix(p[0:])
262+ v.P2 = GetPointfix(p[8:])
263+ v.P3 = GetPointfix(p[16:])
264+ return
265+}
266+
267+type Trapezoid struct {
268+ Top Fixed
269+ Bottom Fixed
270+ Left Linefix
271+ Right Linefix
272+}
273+
274+const TrapezoidSize = 40
275+
276+func (v Trapezoid) Put(p []byte) {
277+ clear(p[:40])
278+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Top))
279+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Bottom))
280+ v.Left.Put(p[8:])
281+ v.Right.Put(p[24:])
282+}
283+func GetTrapezoid(p []byte) (v Trapezoid) {
284+ _ = p[39]
285+ v.Top = Fixed(binary.LittleEndian.Uint32(p[0:]))
286+ v.Bottom = Fixed(binary.LittleEndian.Uint32(p[4:]))
287+ v.Left = GetLinefix(p[8:])
288+ v.Right = GetLinefix(p[24:])
289+ return
290+}
291+
292+type Glyphinfo struct {
293+ Width uint16
294+ Height uint16
295+ X int16
296+ Y int16
297+ XOff int16
298+ YOff int16
299+}
300+
301+const GlyphinfoSize = 12
302+
303+func (v Glyphinfo) Put(p []byte) {
304+ clear(p[:12])
305+ binary.LittleEndian.PutUint16(p[0:], uint16(v.Width))
306+ binary.LittleEndian.PutUint16(p[2:], uint16(v.Height))
307+ binary.LittleEndian.PutUint16(p[4:], uint16(v.X))
308+ binary.LittleEndian.PutUint16(p[6:], uint16(v.Y))
309+ binary.LittleEndian.PutUint16(p[8:], uint16(v.XOff))
310+ binary.LittleEndian.PutUint16(p[10:], uint16(v.YOff))
311+}
312+func GetGlyphinfo(p []byte) (v Glyphinfo) {
313+ _ = p[11]
314+ v.Width = uint16(binary.LittleEndian.Uint16(p[0:]))
315+ v.Height = uint16(binary.LittleEndian.Uint16(p[2:]))
316+ v.X = int16(binary.LittleEndian.Uint16(p[4:]))
317+ v.Y = int16(binary.LittleEndian.Uint16(p[6:]))
318+ v.XOff = int16(binary.LittleEndian.Uint16(p[8:]))
319+ v.YOff = int16(binary.LittleEndian.Uint16(p[10:]))
320+ return
321+}
322+
323+type Transform struct {
324+ Matrix11 Fixed
325+ Matrix12 Fixed
326+ Matrix13 Fixed
327+ Matrix21 Fixed
328+ Matrix22 Fixed
329+ Matrix23 Fixed
330+ Matrix31 Fixed
331+ Matrix32 Fixed
332+ Matrix33 Fixed
333+}
334+
335+const TransformSize = 36
336+
337+func (v Transform) Put(p []byte) {
338+ clear(p[:36])
339+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Matrix11))
340+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Matrix12))
341+ binary.LittleEndian.PutUint32(p[8:], uint32(v.Matrix13))
342+ binary.LittleEndian.PutUint32(p[12:], uint32(v.Matrix21))
343+ binary.LittleEndian.PutUint32(p[16:], uint32(v.Matrix22))
344+ binary.LittleEndian.PutUint32(p[20:], uint32(v.Matrix23))
345+ binary.LittleEndian.PutUint32(p[24:], uint32(v.Matrix31))
346+ binary.LittleEndian.PutUint32(p[28:], uint32(v.Matrix32))
347+ binary.LittleEndian.PutUint32(p[32:], uint32(v.Matrix33))
348+}
349+func GetTransform(p []byte) (v Transform) {
350+ _ = p[35]
351+ v.Matrix11 = Fixed(binary.LittleEndian.Uint32(p[0:]))
352+ v.Matrix12 = Fixed(binary.LittleEndian.Uint32(p[4:]))
353+ v.Matrix13 = Fixed(binary.LittleEndian.Uint32(p[8:]))
354+ v.Matrix21 = Fixed(binary.LittleEndian.Uint32(p[12:]))
355+ v.Matrix22 = Fixed(binary.LittleEndian.Uint32(p[16:]))
356+ v.Matrix23 = Fixed(binary.LittleEndian.Uint32(p[20:]))
357+ v.Matrix31 = Fixed(binary.LittleEndian.Uint32(p[24:]))
358+ v.Matrix32 = Fixed(binary.LittleEndian.Uint32(p[28:]))
359+ v.Matrix33 = Fixed(binary.LittleEndian.Uint32(p[32:]))
360+ return
361+}
362+
363+type Animcursorelt struct {
364+ Cursor xproto.Cursor
365+ Delay uint32
366+}
367+
368+const AnimcursoreltSize = 8
369+
370+func (v Animcursorelt) Put(p []byte) {
371+ clear(p[:8])
372+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Cursor))
373+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Delay))
374+}
375+func GetAnimcursorelt(p []byte) (v Animcursorelt) {
376+ _ = p[7]
377+ v.Cursor = xproto.Cursor(binary.LittleEndian.Uint32(p[0:]))
378+ v.Delay = uint32(binary.LittleEndian.Uint32(p[4:]))
379+ return
380+}
381+
382+type Spanfix struct {
383+ L Fixed
384+ R Fixed
385+ Y Fixed
386+}
387+
388+const SpanfixSize = 12
389+
390+func (v Spanfix) Put(p []byte) {
391+ clear(p[:12])
392+ binary.LittleEndian.PutUint32(p[0:], uint32(v.L))
393+ binary.LittleEndian.PutUint32(p[4:], uint32(v.R))
394+ binary.LittleEndian.PutUint32(p[8:], uint32(v.Y))
395+}
396+func GetSpanfix(p []byte) (v Spanfix) {
397+ _ = p[11]
398+ v.L = Fixed(binary.LittleEndian.Uint32(p[0:]))
399+ v.R = Fixed(binary.LittleEndian.Uint32(p[4:]))
400+ v.Y = Fixed(binary.LittleEndian.Uint32(p[8:]))
401+ return
402+}
403+
404+type Trap struct {
405+ Top Spanfix
406+ Bot Spanfix
407+}
408+
409+const TrapSize = 24
410+
411+func (v Trap) Put(p []byte) { clear(p[:24]); v.Top.Put(p[0:]); v.Bot.Put(p[12:]) }
412+func GetTrap(p []byte) (v Trap) {
413+ _ = p[23]
414+ v.Top = GetSpanfix(p[0:])
415+ v.Bot = GetSpanfix(p[12:])
416+ return
417+}
418+
419+type PictType uint8
420+
421+const PictTypeIndexed PictType = 0
422+const PictTypeDirect PictType = 1
423+
424+const PictureNone Picture = 0
425+
426+type PictOp uint8
427+
428+const PictOpClear PictOp = 0
429+const PictOpSrc PictOp = 1
430+const PictOpDst PictOp = 2
431+const PictOpOver PictOp = 3
432+const PictOpOverReverse PictOp = 4
433+const PictOpIn PictOp = 5
434+const PictOpInReverse PictOp = 6
435+const PictOpOut PictOp = 7
436+const PictOpOutReverse PictOp = 8
437+const PictOpAtop PictOp = 9
438+const PictOpAtopReverse PictOp = 10
439+const PictOpXor PictOp = 11
440+const PictOpAdd PictOp = 12
441+const PictOpSaturate PictOp = 13
442+const PictOpDisjointClear PictOp = 16
443+const PictOpDisjointSrc PictOp = 17
444+const PictOpDisjointDst PictOp = 18
445+const PictOpDisjointOver PictOp = 19
446+const PictOpDisjointOverReverse PictOp = 20
447+const PictOpDisjointIn PictOp = 21
448+const PictOpDisjointInReverse PictOp = 22
449+const PictOpDisjointOut PictOp = 23
450+const PictOpDisjointOutReverse PictOp = 24
451+const PictOpDisjointAtop PictOp = 25
452+const PictOpDisjointAtopReverse PictOp = 26
453+const PictOpDisjointXor PictOp = 27
454+const PictOpConjointClear PictOp = 32
455+const PictOpConjointSrc PictOp = 33
456+const PictOpConjointDst PictOp = 34
457+const PictOpConjointOver PictOp = 35
458+const PictOpConjointOverReverse PictOp = 36
459+const PictOpConjointIn PictOp = 37
460+const PictOpConjointInReverse PictOp = 38
461+const PictOpConjointOut PictOp = 39
462+const PictOpConjointOutReverse PictOp = 40
463+const PictOpConjointAtop PictOp = 41
464+const PictOpConjointAtopReverse PictOp = 42
465+const PictOpConjointXor PictOp = 43
466+const PictOpMultiply PictOp = 48
467+const PictOpScreen PictOp = 49
468+const PictOpOverlay PictOp = 50
469+const PictOpDarken PictOp = 51
470+const PictOpLighten PictOp = 52
471+const PictOpColorDodge PictOp = 53
472+const PictOpColorBurn PictOp = 54
473+const PictOpHardLight PictOp = 55
474+const PictOpSoftLight PictOp = 56
475+const PictOpDifference PictOp = 57
476+const PictOpExclusion PictOp = 58
477+const PictOpHSLHue PictOp = 59
478+const PictOpHSLSaturation PictOp = 60
479+const PictOpHSLColor PictOp = 61
480+const PictOpHSLLuminosity PictOp = 62
481+
482+type PolyEdge uint32
483+
484+const PolyEdgeSharp PolyEdge = 0
485+const PolyEdgeSmooth PolyEdge = 1
486+
487+type PolyMode uint32
488+
489+const PolyModePrecise PolyMode = 0
490+const PolyModeImprecise PolyMode = 1
491+
492+type Cp uint32
493+
494+const CpRepeat Cp = 1
495+const CpAlphaMap Cp = 2
496+const CpAlphaXOrigin Cp = 4
497+const CpAlphaYOrigin Cp = 8
498+const CpClipXOrigin Cp = 16
499+const CpClipYOrigin Cp = 32
500+const CpClipMask Cp = 64
501+const CpGraphicsExposure Cp = 128
502+const CpSubwindowMode Cp = 256
503+const CpPolyEdge Cp = 512
504+const CpPolyMode Cp = 1024
505+const CpDither Cp = 2048
506+const CpComponentAlpha Cp = 4096
507+
508+type SubPixel uint32
509+
510+const SubPixelUnknown SubPixel = 0
511+const SubPixelHorizontalRGB SubPixel = 1
512+const SubPixelHorizontalBGR SubPixel = 2
513+const SubPixelVerticalRGB SubPixel = 3
514+const SubPixelVerticalBGR SubPixel = 4
515+const SubPixelNone SubPixel = 5
516+
517+type Repeat uint32
518+
519+const RepeatNone Repeat = 0
520+const RepeatNormal Repeat = 1
521+const RepeatPad Repeat = 2
522+const RepeatReflect Repeat = 3
@@ -0,0 +1,522 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+type Glyph uint32
8+
9+type Glyphset uint32
10+
11+type Picture uint32
12+
13+type Pictformat uint32
14+
15+type Fixed int32
16+
17+type Directformat struct {
18+ RedShift uint16
19+ RedMask uint16
20+ GreenShift uint16
21+ GreenMask uint16
22+ BlueShift uint16
23+ BlueMask uint16
24+ AlphaShift uint16
25+ AlphaMask uint16
26+}
27+
28+const DirectformatSize = 16
29+
30+func (v Directformat) Put(p []byte) {
31+ clear(p[:16])
32+ binary.LittleEndian.PutUint16(p[0:], uint16(v.RedShift))
33+ binary.LittleEndian.PutUint16(p[2:], uint16(v.RedMask))
34+ binary.LittleEndian.PutUint16(p[4:], uint16(v.GreenShift))
35+ binary.LittleEndian.PutUint16(p[6:], uint16(v.GreenMask))
36+ binary.LittleEndian.PutUint16(p[8:], uint16(v.BlueShift))
37+ binary.LittleEndian.PutUint16(p[10:], uint16(v.BlueMask))
38+ binary.LittleEndian.PutUint16(p[12:], uint16(v.AlphaShift))
39+ binary.LittleEndian.PutUint16(p[14:], uint16(v.AlphaMask))
40+}
41+func GetDirectformat(p []byte) (v Directformat) {
42+ _ = p[15]
43+ v.RedShift = uint16(binary.LittleEndian.Uint16(p[0:]))
44+ v.RedMask = uint16(binary.LittleEndian.Uint16(p[2:]))
45+ v.GreenShift = uint16(binary.LittleEndian.Uint16(p[4:]))
46+ v.GreenMask = uint16(binary.LittleEndian.Uint16(p[6:]))
47+ v.BlueShift = uint16(binary.LittleEndian.Uint16(p[8:]))
48+ v.BlueMask = uint16(binary.LittleEndian.Uint16(p[10:]))
49+ v.AlphaShift = uint16(binary.LittleEndian.Uint16(p[12:]))
50+ v.AlphaMask = uint16(binary.LittleEndian.Uint16(p[14:]))
51+ return
52+}
53+
54+type Pictforminfo struct {
55+ Id Pictformat
56+ Type PictType
57+ Depth uint8
58+ Direct Directformat
59+ Colormap xproto.Colormap
60+}
61+
62+const PictforminfoSize = 28
63+
64+func (v Pictforminfo) Put(p []byte) {
65+ clear(p[:28])
66+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Id))
67+ p[4:][0] = byte(v.Type)
68+ p[5:][0] = byte(v.Depth)
69+ v.Direct.Put(p[8:])
70+ binary.LittleEndian.PutUint32(p[24:], uint32(v.Colormap))
71+}
72+func GetPictforminfo(p []byte) (v Pictforminfo) {
73+ _ = p[27]
74+ v.Id = Pictformat(binary.LittleEndian.Uint32(p[0:]))
75+ v.Type = PictType(p[4:][0])
76+ v.Depth = uint8(p[5:][0])
77+ v.Direct = GetDirectformat(p[8:])
78+ v.Colormap = xproto.Colormap(binary.LittleEndian.Uint32(p[24:]))
79+ return
80+}
81+
82+type Pictvisual struct {
83+ Visual xproto.Visualid
84+ Format Pictformat
85+}
86+
87+const PictvisualSize = 8
88+
89+func (v Pictvisual) Put(p []byte) {
90+ clear(p[:8])
91+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Visual))
92+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Format))
93+}
94+func GetPictvisual(p []byte) (v Pictvisual) {
95+ _ = p[7]
96+ v.Visual = xproto.Visualid(binary.LittleEndian.Uint32(p[0:]))
97+ v.Format = Pictformat(binary.LittleEndian.Uint32(p[4:]))
98+ return
99+}
100+
101+type Pictdepth struct {
102+ Depth uint8
103+ NumVisuals uint16
104+ Visuals []Pictvisual
105+}
106+
107+const PictdepthSize = 8
108+
109+func (v Pictdepth) Size() int {
110+ off := 0
111+ off += 1
112+ off += 1
113+ off += 2
114+ off += 4
115+ off += len(v.Visuals) * 8
116+ return off
117+}
118+func (v Pictdepth) Put(p []byte) {
119+ off := 0
120+ p[off:][0] = byte(v.Depth)
121+ off += 1
122+ clear(p[off : off+1])
123+ off += 1
124+ binary.LittleEndian.PutUint16(p[off:], uint16(v.NumVisuals))
125+ off += 2
126+ clear(p[off : off+4])
127+ off += 4
128+ for i := range v.Visuals {
129+ v.Visuals[i].Put(p[off+i*8:])
130+ }
131+ off += len(v.Visuals) * 8
132+}
133+
134+type Pictscreen struct {
135+ NumDepths uint32
136+ Fallback Pictformat
137+ Depths []Pictdepth
138+}
139+
140+const PictscreenSize = 8
141+
142+func (v Pictscreen) Size() int {
143+ off := 0
144+ off += 4
145+ off += 4
146+ for i := range v.Depths {
147+ off += v.Depths[i].Size()
148+ }
149+ return off
150+}
151+func (v Pictscreen) Put(p []byte) {
152+ off := 0
153+ binary.LittleEndian.PutUint32(p[off:], uint32(v.NumDepths))
154+ off += 4
155+ binary.LittleEndian.PutUint32(p[off:], uint32(v.Fallback))
156+ off += 4
157+ for i := range v.Depths {
158+ v.Depths[i].Put(p[off:])
159+ off += v.Depths[i].Size()
160+ }
161+}
162+
163+type Indexvalue struct {
164+ Pixel uint32
165+ Red uint16
166+ Green uint16
167+ Blue uint16
168+ Alpha uint16
169+}
170+
171+const IndexvalueSize = 12
172+
173+func (v Indexvalue) Put(p []byte) {
174+ clear(p[:12])
175+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Pixel))
176+ binary.LittleEndian.PutUint16(p[4:], uint16(v.Red))
177+ binary.LittleEndian.PutUint16(p[6:], uint16(v.Green))
178+ binary.LittleEndian.PutUint16(p[8:], uint16(v.Blue))
179+ binary.LittleEndian.PutUint16(p[10:], uint16(v.Alpha))
180+}
181+func GetIndexvalue(p []byte) (v Indexvalue) {
182+ _ = p[11]
183+ v.Pixel = uint32(binary.LittleEndian.Uint32(p[0:]))
184+ v.Red = uint16(binary.LittleEndian.Uint16(p[4:]))
185+ v.Green = uint16(binary.LittleEndian.Uint16(p[6:]))
186+ v.Blue = uint16(binary.LittleEndian.Uint16(p[8:]))
187+ v.Alpha = uint16(binary.LittleEndian.Uint16(p[10:]))
188+ return
189+}
190+
191+type Color struct {
192+ Red uint16
193+ Green uint16
194+ Blue uint16
195+ Alpha uint16
196+}
197+
198+const ColorSize = 8
199+
200+func (v Color) Put(p []byte) {
201+ clear(p[:8])
202+ binary.LittleEndian.PutUint16(p[0:], uint16(v.Red))
203+ binary.LittleEndian.PutUint16(p[2:], uint16(v.Green))
204+ binary.LittleEndian.PutUint16(p[4:], uint16(v.Blue))
205+ binary.LittleEndian.PutUint16(p[6:], uint16(v.Alpha))
206+}
207+func GetColor(p []byte) (v Color) {
208+ _ = p[7]
209+ v.Red = uint16(binary.LittleEndian.Uint16(p[0:]))
210+ v.Green = uint16(binary.LittleEndian.Uint16(p[2:]))
211+ v.Blue = uint16(binary.LittleEndian.Uint16(p[4:]))
212+ v.Alpha = uint16(binary.LittleEndian.Uint16(p[6:]))
213+ return
214+}
215+
216+type Pointfix struct {
217+ X Fixed
218+ Y Fixed
219+}
220+
221+const PointfixSize = 8
222+
223+func (v Pointfix) Put(p []byte) {
224+ clear(p[:8])
225+ binary.LittleEndian.PutUint32(p[0:], uint32(v.X))
226+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Y))
227+}
228+func GetPointfix(p []byte) (v Pointfix) {
229+ _ = p[7]
230+ v.X = Fixed(binary.LittleEndian.Uint32(p[0:]))
231+ v.Y = Fixed(binary.LittleEndian.Uint32(p[4:]))
232+ return
233+}
234+
235+type Linefix struct {
236+ P1 Pointfix
237+ P2 Pointfix
238+}
239+
240+const LinefixSize = 16
241+
242+func (v Linefix) Put(p []byte) { clear(p[:16]); v.P1.Put(p[0:]); v.P2.Put(p[8:]) }
243+func GetLinefix(p []byte) (v Linefix) {
244+ _ = p[15]
245+ v.P1 = GetPointfix(p[0:])
246+ v.P2 = GetPointfix(p[8:])
247+ return
248+}
249+
250+type Triangle struct {
251+ P1 Pointfix
252+ P2 Pointfix
253+ P3 Pointfix
254+}
255+
256+const TriangleSize = 24
257+
258+func (v Triangle) Put(p []byte) { clear(p[:24]); v.P1.Put(p[0:]); v.P2.Put(p[8:]); v.P3.Put(p[16:]) }
259+func GetTriangle(p []byte) (v Triangle) {
260+ _ = p[23]
261+ v.P1 = GetPointfix(p[0:])
262+ v.P2 = GetPointfix(p[8:])
263+ v.P3 = GetPointfix(p[16:])
264+ return
265+}
266+
267+type Trapezoid struct {
268+ Top Fixed
269+ Bottom Fixed
270+ Left Linefix
271+ Right Linefix
272+}
273+
274+const TrapezoidSize = 40
275+
276+func (v Trapezoid) Put(p []byte) {
277+ clear(p[:40])
278+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Top))
279+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Bottom))
280+ v.Left.Put(p[8:])
281+ v.Right.Put(p[24:])
282+}
283+func GetTrapezoid(p []byte) (v Trapezoid) {
284+ _ = p[39]
285+ v.Top = Fixed(binary.LittleEndian.Uint32(p[0:]))
286+ v.Bottom = Fixed(binary.LittleEndian.Uint32(p[4:]))
287+ v.Left = GetLinefix(p[8:])
288+ v.Right = GetLinefix(p[24:])
289+ return
290+}
291+
292+type Glyphinfo struct {
293+ Width uint16
294+ Height uint16
295+ X int16
296+ Y int16
297+ XOff int16
298+ YOff int16
299+}
300+
301+const GlyphinfoSize = 12
302+
303+func (v Glyphinfo) Put(p []byte) {
304+ clear(p[:12])
305+ binary.LittleEndian.PutUint16(p[0:], uint16(v.Width))
306+ binary.LittleEndian.PutUint16(p[2:], uint16(v.Height))
307+ binary.LittleEndian.PutUint16(p[4:], uint16(v.X))
308+ binary.LittleEndian.PutUint16(p[6:], uint16(v.Y))
309+ binary.LittleEndian.PutUint16(p[8:], uint16(v.XOff))
310+ binary.LittleEndian.PutUint16(p[10:], uint16(v.YOff))
311+}
312+func GetGlyphinfo(p []byte) (v Glyphinfo) {
313+ _ = p[11]
314+ v.Width = uint16(binary.LittleEndian.Uint16(p[0:]))
315+ v.Height = uint16(binary.LittleEndian.Uint16(p[2:]))
316+ v.X = int16(binary.LittleEndian.Uint16(p[4:]))
317+ v.Y = int16(binary.LittleEndian.Uint16(p[6:]))
318+ v.XOff = int16(binary.LittleEndian.Uint16(p[8:]))
319+ v.YOff = int16(binary.LittleEndian.Uint16(p[10:]))
320+ return
321+}
322+
323+type Transform struct {
324+ Matrix11 Fixed
325+ Matrix12 Fixed
326+ Matrix13 Fixed
327+ Matrix21 Fixed
328+ Matrix22 Fixed
329+ Matrix23 Fixed
330+ Matrix31 Fixed
331+ Matrix32 Fixed
332+ Matrix33 Fixed
333+}
334+
335+const TransformSize = 36
336+
337+func (v Transform) Put(p []byte) {
338+ clear(p[:36])
339+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Matrix11))
340+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Matrix12))
341+ binary.LittleEndian.PutUint32(p[8:], uint32(v.Matrix13))
342+ binary.LittleEndian.PutUint32(p[12:], uint32(v.Matrix21))
343+ binary.LittleEndian.PutUint32(p[16:], uint32(v.Matrix22))
344+ binary.LittleEndian.PutUint32(p[20:], uint32(v.Matrix23))
345+ binary.LittleEndian.PutUint32(p[24:], uint32(v.Matrix31))
346+ binary.LittleEndian.PutUint32(p[28:], uint32(v.Matrix32))
347+ binary.LittleEndian.PutUint32(p[32:], uint32(v.Matrix33))
348+}
349+func GetTransform(p []byte) (v Transform) {
350+ _ = p[35]
351+ v.Matrix11 = Fixed(binary.LittleEndian.Uint32(p[0:]))
352+ v.Matrix12 = Fixed(binary.LittleEndian.Uint32(p[4:]))
353+ v.Matrix13 = Fixed(binary.LittleEndian.Uint32(p[8:]))
354+ v.Matrix21 = Fixed(binary.LittleEndian.Uint32(p[12:]))
355+ v.Matrix22 = Fixed(binary.LittleEndian.Uint32(p[16:]))
356+ v.Matrix23 = Fixed(binary.LittleEndian.Uint32(p[20:]))
357+ v.Matrix31 = Fixed(binary.LittleEndian.Uint32(p[24:]))
358+ v.Matrix32 = Fixed(binary.LittleEndian.Uint32(p[28:]))
359+ v.Matrix33 = Fixed(binary.LittleEndian.Uint32(p[32:]))
360+ return
361+}
362+
363+type Animcursorelt struct {
364+ Cursor xproto.Cursor
365+ Delay uint32
366+}
367+
368+const AnimcursoreltSize = 8
369+
370+func (v Animcursorelt) Put(p []byte) {
371+ clear(p[:8])
372+ binary.LittleEndian.PutUint32(p[0:], uint32(v.Cursor))
373+ binary.LittleEndian.PutUint32(p[4:], uint32(v.Delay))
374+}
375+func GetAnimcursorelt(p []byte) (v Animcursorelt) {
376+ _ = p[7]
377+ v.Cursor = xproto.Cursor(binary.LittleEndian.Uint32(p[0:]))
378+ v.Delay = uint32(binary.LittleEndian.Uint32(p[4:]))
379+ return
380+}
381+
382+type Spanfix struct {
383+ L Fixed
384+ R Fixed
385+ Y Fixed
386+}
387+
388+const SpanfixSize = 12
389+
390+func (v Spanfix) Put(p []byte) {
391+ clear(p[:12])
392+ binary.LittleEndian.PutUint32(p[0:], uint32(v.L))
393+ binary.LittleEndian.PutUint32(p[4:], uint32(v.R))
394+ binary.LittleEndian.PutUint32(p[8:], uint32(v.Y))
395+}
396+func GetSpanfix(p []byte) (v Spanfix) {
397+ _ = p[11]
398+ v.L = Fixed(binary.LittleEndian.Uint32(p[0:]))
399+ v.R = Fixed(binary.LittleEndian.Uint32(p[4:]))
400+ v.Y = Fixed(binary.LittleEndian.Uint32(p[8:]))
401+ return
402+}
403+
404+type Trap struct {
405+ Top Spanfix
406+ Bot Spanfix
407+}
408+
409+const TrapSize = 24
410+
411+func (v Trap) Put(p []byte) { clear(p[:24]); v.Top.Put(p[0:]); v.Bot.Put(p[12:]) }
412+func GetTrap(p []byte) (v Trap) {
413+ _ = p[23]
414+ v.Top = GetSpanfix(p[0:])
415+ v.Bot = GetSpanfix(p[12:])
416+ return
417+}
418+
419+type PictType uint8
420+
421+const PictTypeIndexed PictType = 0
422+const PictTypeDirect PictType = 1
423+
424+const PictureNone Picture = 0
425+
426+type PictOp uint8
427+
428+const PictOpClear PictOp = 0
429+const PictOpSrc PictOp = 1
430+const PictOpDst PictOp = 2
431+const PictOpOver PictOp = 3
432+const PictOpOverReverse PictOp = 4
433+const PictOpIn PictOp = 5
434+const PictOpInReverse PictOp = 6
435+const PictOpOut PictOp = 7
436+const PictOpOutReverse PictOp = 8
437+const PictOpAtop PictOp = 9
438+const PictOpAtopReverse PictOp = 10
439+const PictOpXor PictOp = 11
440+const PictOpAdd PictOp = 12
441+const PictOpSaturate PictOp = 13
442+const PictOpDisjointClear PictOp = 16
443+const PictOpDisjointSrc PictOp = 17
444+const PictOpDisjointDst PictOp = 18
445+const PictOpDisjointOver PictOp = 19
446+const PictOpDisjointOverReverse PictOp = 20
447+const PictOpDisjointIn PictOp = 21
448+const PictOpDisjointInReverse PictOp = 22
449+const PictOpDisjointOut PictOp = 23
450+const PictOpDisjointOutReverse PictOp = 24
451+const PictOpDisjointAtop PictOp = 25
452+const PictOpDisjointAtopReverse PictOp = 26
453+const PictOpDisjointXor PictOp = 27
454+const PictOpConjointClear PictOp = 32
455+const PictOpConjointSrc PictOp = 33
456+const PictOpConjointDst PictOp = 34
457+const PictOpConjointOver PictOp = 35
458+const PictOpConjointOverReverse PictOp = 36
459+const PictOpConjointIn PictOp = 37
460+const PictOpConjointInReverse PictOp = 38
461+const PictOpConjointOut PictOp = 39
462+const PictOpConjointOutReverse PictOp = 40
463+const PictOpConjointAtop PictOp = 41
464+const PictOpConjointAtopReverse PictOp = 42
465+const PictOpConjointXor PictOp = 43
466+const PictOpMultiply PictOp = 48
467+const PictOpScreen PictOp = 49
468+const PictOpOverlay PictOp = 50
469+const PictOpDarken PictOp = 51
470+const PictOpLighten PictOp = 52
471+const PictOpColorDodge PictOp = 53
472+const PictOpColorBurn PictOp = 54
473+const PictOpHardLight PictOp = 55
474+const PictOpSoftLight PictOp = 56
475+const PictOpDifference PictOp = 57
476+const PictOpExclusion PictOp = 58
477+const PictOpHSLHue PictOp = 59
478+const PictOpHSLSaturation PictOp = 60
479+const PictOpHSLColor PictOp = 61
480+const PictOpHSLLuminosity PictOp = 62
481+
482+type PolyEdge uint32
483+
484+const PolyEdgeSharp PolyEdge = 0
485+const PolyEdgeSmooth PolyEdge = 1
486+
487+type PolyMode uint32
488+
489+const PolyModePrecise PolyMode = 0
490+const PolyModeImprecise PolyMode = 1
491+
492+type Cp uint32
493+
494+const CpRepeat Cp = 1
495+const CpAlphaMap Cp = 2
496+const CpAlphaXOrigin Cp = 4
497+const CpAlphaYOrigin Cp = 8
498+const CpClipXOrigin Cp = 16
499+const CpClipYOrigin Cp = 32
500+const CpClipMask Cp = 64
501+const CpGraphicsExposure Cp = 128
502+const CpSubwindowMode Cp = 256
503+const CpPolyEdge Cp = 512
504+const CpPolyMode Cp = 1024
505+const CpDither Cp = 2048
506+const CpComponentAlpha Cp = 4096
507+
508+type SubPixel uint32
509+
510+const SubPixelUnknown SubPixel = 0
511+const SubPixelHorizontalRGB SubPixel = 1
512+const SubPixelHorizontalBGR SubPixel = 2
513+const SubPixelVerticalRGB SubPixel = 3
514+const SubPixelVerticalBGR SubPixel = 4
515+const SubPixelNone SubPixel = 5
516+
517+type Repeat uint32
518+
519+const RepeatNone Repeat = 0
520+const RepeatNormal Repeat = 1
521+const RepeatPad Repeat = 2
522+const RepeatReflect Repeat = 3
A replies_test.go +304 -0
@@ -0,0 +1,304 @@
1+package xturbo_test
2+
3+import (
4+ "bytes"
5+ "encoding/binary"
6+ "testing"
7+
8+ "bnema.dev/xturbo/internal/xtest"
9+ "bnema.dev/xturbo/render"
10+ "bnema.dev/xturbo/res"
11+ "bnema.dev/xturbo/xfixes"
12+ "bnema.dev/xturbo/xproto"
13+)
14+
15+func loadReply(t testing.TB, name string) []byte {
16+ t.Helper()
17+ raw, err := xtest.LoadHex("testdata/golden/" + name + ".reply.hex")
18+ if err != nil {
19+ t.Fatal(err)
20+ }
21+ if raw[0] != 1 || len(raw) != 32+4*int(binary.LittleEndian.Uint32(raw[4:])) {
22+ t.Fatalf("%s: not a framed reply", name)
23+ }
24+ return raw
25+}
26+
27+// Expected values are independent of the generated code: they follow from the
28+// capture script (window geometry, written property, ConfigureWindow state)
29+// or are read from the raw golden at protocol-spec offsets.
30+func TestReplyGoldens(t *testing.T) {
31+ u32 := func(raw []byte, off int) uint32 { return binary.LittleEndian.Uint32(raw[off:]) }
32+ t.Run("xproto/GetGeometry", func(t *testing.T) {
33+ raw := loadReply(t, "xproto/GetGeometry")
34+ r, ok := xproto.ParseGetGeometryReply(raw)
35+ if !ok || r.Depth() != 24 || r.Root() != xproto.Window(u32(raw, 8)) || r.X() != 0 || r.Y() != 0 ||
36+ r.Width() != 800 || r.Height() != 600 || r.BorderWidth() != 0 || !bytes.Equal(r.Raw(), raw) {
37+ t.Fatalf("decoded %+v", r)
38+ }
39+ })
40+ t.Run("xproto/QueryTree", func(t *testing.T) {
41+ raw := loadReply(t, "xproto/QueryTree")
42+ r, ok := xproto.ParseQueryTreeReply(raw)
43+ if !ok || r.Root() != xproto.Window(u32(raw, 8)) || r.Parent() != xproto.WindowNone || r.ChildrenLen() != 2 {
44+ t.Fatal("fixed fields")
45+ }
46+ c := r.Children()
47+ // Stacking order bottom-to-top: the sibling was created before the child.
48+ if c.Len() != 2 || c.At(0)&0x1fffff != 2 || c.At(1)&0x1fffff != 1 || c.At(0)&^0x1fffff != c.At(1)&^0x1fffff {
49+ t.Fatalf("children %x %x", c.At(0), c.At(1))
50+ }
51+ })
52+ t.Run("xproto/GetWindowAttributes", func(t *testing.T) {
53+ raw := loadReply(t, "xproto/GetWindowAttributes")
54+ r, ok := xproto.ParseGetWindowAttributesReply(raw)
55+ if !ok || r.BackingStore() != xproto.BackingStoreNotUseful || r.Visual() != xproto.Visualid(u32(raw, 8)) ||
56+ r.Class() != xproto.WindowClassInputOutput || r.BitGravity() != xproto.GravityBitForget ||
57+ r.WinGravity() != xproto.GravityNorthWest || r.BackingPlanes() != 0xffffffff || r.BackingPixel() != 0 ||
58+ !r.SaveUnder() || !r.MapIsInstalled() || r.MapState() != xproto.MapStateUnmapped || r.OverrideRedirect() ||
59+ r.Colormap() != xproto.Colormap(u32(raw, 28)) || r.AllEventMasks() != 0 || r.YourEventMask() != 0 ||
60+ r.DoNotPropagateMask() != 0 {
61+ t.Fatal("decoded attributes")
62+ }
63+ })
64+ t.Run("xproto/InternAtom", func(t *testing.T) {
65+ raw := loadReply(t, "xproto/InternAtom")
66+ r, ok := xproto.ParseInternAtomReply(raw)
67+ if !ok || r.Atom() == xproto.AtomNone || r.Atom() != xproto.Atom(u32(raw, 8)) {
68+ t.Fatal("atom")
69+ }
70+ })
71+ t.Run("xproto/GetProperty", func(t *testing.T) {
72+ raw := loadReply(t, "xproto/GetProperty")
73+ r, ok := xproto.ParseGetPropertyReply(raw)
74+ if !ok || r.Format() != 8 || r.Type() != xproto.AtomString || r.BytesAfter() != 0 || r.ValueLen() != 3 ||
75+ string(r.Value()) != "abc" || cap(r.Value()) != 3 {
76+ t.Fatalf("value %q", r.Value())
77+ }
78+ })
79+ t.Run("xproto/GetPropertyEmpty", func(t *testing.T) {
80+ raw := loadReply(t, "xproto/GetPropertyEmpty")
81+ r, ok := xproto.ParseGetPropertyReply(raw)
82+ if !ok || r.Format() != 0 || r.Type() != xproto.AtomNone || r.ValueLen() != 0 || len(r.Value()) != 0 {
83+ t.Fatal("empty property")
84+ }
85+ })
86+ t.Run("xproto/QueryExtension", func(t *testing.T) {
87+ raw := loadReply(t, "xproto/QueryExtension")
88+ r, ok := xproto.ParseQueryExtensionReply(raw)
89+ if !ok || !r.Present() || r.MajorOpcode() < 128 || r.FirstEvent() != raw[10] || r.FirstError() != raw[11] {
90+ t.Fatal("XFIXES query")
91+ }
92+ })
93+ t.Run("xproto/ListExtensions", func(t *testing.T) {
94+ raw := loadReply(t, "xproto/ListExtensions")
95+ r, ok := xproto.ParseListExtensionsReply(raw)
96+ if !ok {
97+ t.Fatal("parse")
98+ }
99+ it := r.Names()
100+ var names []string
101+ total := 32
102+ for it.Next() {
103+ v := it.Value()
104+ if int(v.NameLen()) != len(v.Name()) || v.Size() != 1+len(v.Name()) {
105+ t.Fatal("STR view")
106+ }
107+ names = append(names, string(v.Name()))
108+ total += v.Size()
109+ }
110+ if it.Err() || len(names) != int(r.NamesLen()) || total > len(raw) || len(raw)-total > 3 {
111+ t.Fatalf("iterated %d of %d, %d/%d bytes", len(names), r.NamesLen(), total, len(raw))
112+ }
113+ found := map[string]bool{}
114+ for _, n := range names {
115+ found[n] = true
116+ }
117+ for _, want := range []string{"XFIXES", "RENDER", "BIG-REQUESTS", "Composite", "X-Resource"} {
118+ if !found[want] {
119+ t.Fatalf("missing %s in %q", want, names)
120+ }
121+ }
122+ })
123+ t.Run("xfixes/QueryVersion", func(t *testing.T) {
124+ r, ok := xfixes.ParseQueryVersionReply(loadReply(t, "xfixes/QueryVersion"))
125+ if !ok || r.MajorVersion() != 6 || r.MinorVersion() != 0 {
126+ t.Fatal("version")
127+ }
128+ })
129+ t.Run("res/QueryClientIds", func(t *testing.T) {
130+ r, ok := res.ParseQueryClientIdsReply(loadReply(t, "res/QueryClientIds"))
131+ if !ok || r.NumIds() != 2 {
132+ t.Fatal("parse")
133+ }
134+ // Spec client 0 asks for every client's XID: the server client and ours.
135+ it := r.Ids()
136+ var clients []uint32
137+ for it.Next() {
138+ v := it.Value()
139+ if v.Spec().Mask != res.ClientIdMaskClientXID || v.Length() != 0 || v.Value().Len() != 0 || v.Size() != 12 {
140+ t.Fatal("ClientIdValue view")
141+ }
142+ clients = append(clients, v.Spec().Client)
143+ }
144+ if it.Err() || len(clients) != 2 || clients[0] != 0 || clients[1] == 0 {
145+ t.Fatalf("clients %x", clients)
146+ }
147+ })
148+ t.Run("render/QueryPictFormats", func(t *testing.T) {
149+ raw := loadReply(t, "render/QueryPictFormats")
150+ r, ok := render.ParseQueryPictFormatsReply(raw)
151+ if !ok || r.NumScreens() != 1 || r.Formats().Len() != int(r.NumFormats()) || r.Subpixels().Len() != int(r.NumSubpixel()) {
152+ t.Fatal("counts")
153+ }
154+ formats := map[render.Pictformat]bool{}
155+ for i := range r.Formats().Len() {
156+ f := r.Formats().At(i)
157+ formats[f.Id] = true
158+ }
159+ screens := r.Screens()
160+ visuals := 0
161+ for screens.Next() {
162+ depths := screens.Value().Depths()
163+ for depths.Next() {
164+ vs := depths.Value().Visuals()
165+ for i := range vs.Len() {
166+ if !formats[vs.At(i).Format] {
167+ t.Fatal("visual references an unknown format")
168+ }
169+ visuals++
170+ }
171+ }
172+ if depths.Err() {
173+ t.Fatal("depth iterator")
174+ }
175+ }
176+ if screens.Err() || visuals == 0 {
177+ t.Fatal("screen iterator")
178+ }
179+ for i := range r.Subpixels().Len() {
180+ if r.Subpixels().At(i) != render.SubPixelUnknown {
181+ t.Fatal("Xvfb subpixel order")
182+ }
183+ }
184+ })
185+}
186+
187+func TestReplyParseRejects(t *testing.T) {
188+ raw := loadReply(t, "xproto/QueryTree")
189+ if _, ok := xproto.ParseQueryTreeReply(raw[:31]); ok {
190+ t.Fatal("short fixed prefix accepted")
191+ }
192+ if _, ok := xproto.ParseQueryTreeReply(raw[:len(raw)-1]); ok {
193+ t.Fatal("truncated list accepted")
194+ }
195+ raw = loadReply(t, "xproto/GetWindowAttributes")
196+ if _, ok := xproto.ParseGetWindowAttributesReply(raw[:43]); ok {
197+ t.Fatal("44-byte reply prefix not enforced")
198+ }
199+ raw = bytes.Clone(loadReply(t, "xproto/ListExtensions"))
200+ raw[1] = 255 // names_len: far more STRs than the reply holds
201+ if _, ok := xproto.ParseListExtensionsReply(raw); ok {
202+ t.Fatal("STR count beyond the reply accepted")
203+ }
204+ prop := bytes.Clone(loadReply(t, "xproto/GetProperty"))
205+ prop[1] = 32 // 3 items of 4 bytes overrun the 36-byte reply
206+ if _, ok := xproto.ParseGetPropertyReply(prop); ok {
207+ t.Fatal("format*value_len overrun accepted")
208+ }
209+}
210+
211+func TestReplyAllocs(t *testing.T) {
212+ prop := loadReply(t, "xproto/GetProperty")
213+ tree := loadReply(t, "xproto/QueryTree")
214+ exts := loadReply(t, "xproto/ListExtensions")
215+ formats := loadReply(t, "render/QueryPictFormats")
216+ ids := loadReply(t, "res/QueryClientIds")
217+ var sink uint64
218+ n := testing.AllocsPerRun(1000, func() {
219+ p, _ := xproto.ParseGetPropertyReply(prop)
220+ v := p.Value()
221+ sink += uint64(len(v)) + uint64(p.Type()) + uint64(p.Format())
222+ q, _ := xproto.ParseQueryTreeReply(tree)
223+ c := q.Children()
224+ for i := range c.Len() {
225+ sink += uint64(c.At(i))
226+ }
227+ sink += uint64(q.Root()) + uint64(q.Parent())
228+ l, _ := xproto.ParseListExtensionsReply(exts)
229+ it := l.Names()
230+ for it.Next() {
231+ sink += uint64(len(it.Value().Name()))
232+ }
233+ f, _ := render.ParseQueryPictFormatsReply(formats)
234+ fl := f.Formats()
235+ for i := range fl.Len() {
236+ sink += uint64(fl.At(i).Id)
237+ }
238+ s := f.Screens()
239+ for s.Next() {
240+ d := s.Value().Depths()
241+ for d.Next() {
242+ vs := d.Value().Visuals()
243+ for i := range vs.Len() {
244+ sink += uint64(vs.At(i).Visual)
245+ }
246+ }
247+ }
248+ sp := f.Subpixels()
249+ for i := range sp.Len() {
250+ sink += uint64(sp.At(i))
251+ }
252+ r, _ := res.ParseQueryClientIdsReply(ids)
253+ ri := r.Ids()
254+ for ri.Next() {
255+ sink += uint64(ri.Value().Value().Len())
256+ }
257+ })
258+ if n != 0 {
259+ t.Fatalf("reply parse/getters/iteration allocate %v times", n)
260+ }
261+ _ = sink
262+}
263+
264+func BenchmarkGetPropertyReply(b *testing.B) {
265+ raw := loadReply(b, "xproto/GetProperty")
266+ var sink int
267+ b.ReportAllocs()
268+ for b.Loop() {
269+ r, ok := xproto.ParseGetPropertyReply(raw)
270+ if !ok {
271+ b.Fatal("parse")
272+ }
273+ sink += len(r.Value()) + int(r.Type())
274+ }
275+ _ = sink
276+}
277+
278+func BenchmarkQueryTreeReply(b *testing.B) {
279+ raw := loadReply(b, "xproto/QueryTree")
280+ var sink xproto.Window
281+ b.ReportAllocs()
282+ for b.Loop() {
283+ r, _ := xproto.ParseQueryTreeReply(raw)
284+ c := r.Children()
285+ for i := range c.Len() {
286+ sink ^= c.At(i)
287+ }
288+ }
289+ _ = sink
290+}
291+
292+func BenchmarkListExtensionsReply(b *testing.B) {
293+ raw := loadReply(b, "xproto/ListExtensions")
294+ var sink int
295+ b.ReportAllocs()
296+ for b.Loop() {
297+ r, _ := xproto.ParseListExtensionsReply(raw)
298+ it := r.Names()
299+ for it.Next() {
300+ sink += len(it.Value().Name())
301+ }
302+ }
303+ _ = sink
304+}
@@ -0,0 +1,304 @@
1+package xturbo_test
2+
3+import (
4+ "bytes"
5+ "encoding/binary"
6+ "testing"
7+
8+ "bnema.dev/xturbo/internal/xtest"
9+ "bnema.dev/xturbo/render"
10+ "bnema.dev/xturbo/res"
11+ "bnema.dev/xturbo/xfixes"
12+ "bnema.dev/xturbo/xproto"
13+)
14+
15+func loadReply(t testing.TB, name string) []byte {
16+ t.Helper()
17+ raw, err := xtest.LoadHex("testdata/golden/" + name + ".reply.hex")
18+ if err != nil {
19+ t.Fatal(err)
20+ }
21+ if raw[0] != 1 || len(raw) != 32+4*int(binary.LittleEndian.Uint32(raw[4:])) {
22+ t.Fatalf("%s: not a framed reply", name)
23+ }
24+ return raw
25+}
26+
27+// Expected values are independent of the generated code: they follow from the
28+// capture script (window geometry, written property, ConfigureWindow state)
29+// or are read from the raw golden at protocol-spec offsets.
30+func TestReplyGoldens(t *testing.T) {
31+ u32 := func(raw []byte, off int) uint32 { return binary.LittleEndian.Uint32(raw[off:]) }
32+ t.Run("xproto/GetGeometry", func(t *testing.T) {
33+ raw := loadReply(t, "xproto/GetGeometry")
34+ r, ok := xproto.ParseGetGeometryReply(raw)
35+ if !ok || r.Depth() != 24 || r.Root() != xproto.Window(u32(raw, 8)) || r.X() != 0 || r.Y() != 0 ||
36+ r.Width() != 800 || r.Height() != 600 || r.BorderWidth() != 0 || !bytes.Equal(r.Raw(), raw) {
37+ t.Fatalf("decoded %+v", r)
38+ }
39+ })
40+ t.Run("xproto/QueryTree", func(t *testing.T) {
41+ raw := loadReply(t, "xproto/QueryTree")
42+ r, ok := xproto.ParseQueryTreeReply(raw)
43+ if !ok || r.Root() != xproto.Window(u32(raw, 8)) || r.Parent() != xproto.WindowNone || r.ChildrenLen() != 2 {
44+ t.Fatal("fixed fields")
45+ }
46+ c := r.Children()
47+ // Stacking order bottom-to-top: the sibling was created before the child.
48+ if c.Len() != 2 || c.At(0)&0x1fffff != 2 || c.At(1)&0x1fffff != 1 || c.At(0)&^0x1fffff != c.At(1)&^0x1fffff {
49+ t.Fatalf("children %x %x", c.At(0), c.At(1))
50+ }
51+ })
52+ t.Run("xproto/GetWindowAttributes", func(t *testing.T) {
53+ raw := loadReply(t, "xproto/GetWindowAttributes")
54+ r, ok := xproto.ParseGetWindowAttributesReply(raw)
55+ if !ok || r.BackingStore() != xproto.BackingStoreNotUseful || r.Visual() != xproto.Visualid(u32(raw, 8)) ||
56+ r.Class() != xproto.WindowClassInputOutput || r.BitGravity() != xproto.GravityBitForget ||
57+ r.WinGravity() != xproto.GravityNorthWest || r.BackingPlanes() != 0xffffffff || r.BackingPixel() != 0 ||
58+ !r.SaveUnder() || !r.MapIsInstalled() || r.MapState() != xproto.MapStateUnmapped || r.OverrideRedirect() ||
59+ r.Colormap() != xproto.Colormap(u32(raw, 28)) || r.AllEventMasks() != 0 || r.YourEventMask() != 0 ||
60+ r.DoNotPropagateMask() != 0 {
61+ t.Fatal("decoded attributes")
62+ }
63+ })
64+ t.Run("xproto/InternAtom", func(t *testing.T) {
65+ raw := loadReply(t, "xproto/InternAtom")
66+ r, ok := xproto.ParseInternAtomReply(raw)
67+ if !ok || r.Atom() == xproto.AtomNone || r.Atom() != xproto.Atom(u32(raw, 8)) {
68+ t.Fatal("atom")
69+ }
70+ })
71+ t.Run("xproto/GetProperty", func(t *testing.T) {
72+ raw := loadReply(t, "xproto/GetProperty")
73+ r, ok := xproto.ParseGetPropertyReply(raw)
74+ if !ok || r.Format() != 8 || r.Type() != xproto.AtomString || r.BytesAfter() != 0 || r.ValueLen() != 3 ||
75+ string(r.Value()) != "abc" || cap(r.Value()) != 3 {
76+ t.Fatalf("value %q", r.Value())
77+ }
78+ })
79+ t.Run("xproto/GetPropertyEmpty", func(t *testing.T) {
80+ raw := loadReply(t, "xproto/GetPropertyEmpty")
81+ r, ok := xproto.ParseGetPropertyReply(raw)
82+ if !ok || r.Format() != 0 || r.Type() != xproto.AtomNone || r.ValueLen() != 0 || len(r.Value()) != 0 {
83+ t.Fatal("empty property")
84+ }
85+ })
86+ t.Run("xproto/QueryExtension", func(t *testing.T) {
87+ raw := loadReply(t, "xproto/QueryExtension")
88+ r, ok := xproto.ParseQueryExtensionReply(raw)
89+ if !ok || !r.Present() || r.MajorOpcode() < 128 || r.FirstEvent() != raw[10] || r.FirstError() != raw[11] {
90+ t.Fatal("XFIXES query")
91+ }
92+ })
93+ t.Run("xproto/ListExtensions", func(t *testing.T) {
94+ raw := loadReply(t, "xproto/ListExtensions")
95+ r, ok := xproto.ParseListExtensionsReply(raw)
96+ if !ok {
97+ t.Fatal("parse")
98+ }
99+ it := r.Names()
100+ var names []string
101+ total := 32
102+ for it.Next() {
103+ v := it.Value()
104+ if int(v.NameLen()) != len(v.Name()) || v.Size() != 1+len(v.Name()) {
105+ t.Fatal("STR view")
106+ }
107+ names = append(names, string(v.Name()))
108+ total += v.Size()
109+ }
110+ if it.Err() || len(names) != int(r.NamesLen()) || total > len(raw) || len(raw)-total > 3 {
111+ t.Fatalf("iterated %d of %d, %d/%d bytes", len(names), r.NamesLen(), total, len(raw))
112+ }
113+ found := map[string]bool{}
114+ for _, n := range names {
115+ found[n] = true
116+ }
117+ for _, want := range []string{"XFIXES", "RENDER", "BIG-REQUESTS", "Composite", "X-Resource"} {
118+ if !found[want] {
119+ t.Fatalf("missing %s in %q", want, names)
120+ }
121+ }
122+ })
123+ t.Run("xfixes/QueryVersion", func(t *testing.T) {
124+ r, ok := xfixes.ParseQueryVersionReply(loadReply(t, "xfixes/QueryVersion"))
125+ if !ok || r.MajorVersion() != 6 || r.MinorVersion() != 0 {
126+ t.Fatal("version")
127+ }
128+ })
129+ t.Run("res/QueryClientIds", func(t *testing.T) {
130+ r, ok := res.ParseQueryClientIdsReply(loadReply(t, "res/QueryClientIds"))
131+ if !ok || r.NumIds() != 2 {
132+ t.Fatal("parse")
133+ }
134+ // Spec client 0 asks for every client's XID: the server client and ours.
135+ it := r.Ids()
136+ var clients []uint32
137+ for it.Next() {
138+ v := it.Value()
139+ if v.Spec().Mask != res.ClientIdMaskClientXID || v.Length() != 0 || v.Value().Len() != 0 || v.Size() != 12 {
140+ t.Fatal("ClientIdValue view")
141+ }
142+ clients = append(clients, v.Spec().Client)
143+ }
144+ if it.Err() || len(clients) != 2 || clients[0] != 0 || clients[1] == 0 {
145+ t.Fatalf("clients %x", clients)
146+ }
147+ })
148+ t.Run("render/QueryPictFormats", func(t *testing.T) {
149+ raw := loadReply(t, "render/QueryPictFormats")
150+ r, ok := render.ParseQueryPictFormatsReply(raw)
151+ if !ok || r.NumScreens() != 1 || r.Formats().Len() != int(r.NumFormats()) || r.Subpixels().Len() != int(r.NumSubpixel()) {
152+ t.Fatal("counts")
153+ }
154+ formats := map[render.Pictformat]bool{}
155+ for i := range r.Formats().Len() {
156+ f := r.Formats().At(i)
157+ formats[f.Id] = true
158+ }
159+ screens := r.Screens()
160+ visuals := 0
161+ for screens.Next() {
162+ depths := screens.Value().Depths()
163+ for depths.Next() {
164+ vs := depths.Value().Visuals()
165+ for i := range vs.Len() {
166+ if !formats[vs.At(i).Format] {
167+ t.Fatal("visual references an unknown format")
168+ }
169+ visuals++
170+ }
171+ }
172+ if depths.Err() {
173+ t.Fatal("depth iterator")
174+ }
175+ }
176+ if screens.Err() || visuals == 0 {
177+ t.Fatal("screen iterator")
178+ }
179+ for i := range r.Subpixels().Len() {
180+ if r.Subpixels().At(i) != render.SubPixelUnknown {
181+ t.Fatal("Xvfb subpixel order")
182+ }
183+ }
184+ })
185+}
186+
187+func TestReplyParseRejects(t *testing.T) {
188+ raw := loadReply(t, "xproto/QueryTree")
189+ if _, ok := xproto.ParseQueryTreeReply(raw[:31]); ok {
190+ t.Fatal("short fixed prefix accepted")
191+ }
192+ if _, ok := xproto.ParseQueryTreeReply(raw[:len(raw)-1]); ok {
193+ t.Fatal("truncated list accepted")
194+ }
195+ raw = loadReply(t, "xproto/GetWindowAttributes")
196+ if _, ok := xproto.ParseGetWindowAttributesReply(raw[:43]); ok {
197+ t.Fatal("44-byte reply prefix not enforced")
198+ }
199+ raw = bytes.Clone(loadReply(t, "xproto/ListExtensions"))
200+ raw[1] = 255 // names_len: far more STRs than the reply holds
201+ if _, ok := xproto.ParseListExtensionsReply(raw); ok {
202+ t.Fatal("STR count beyond the reply accepted")
203+ }
204+ prop := bytes.Clone(loadReply(t, "xproto/GetProperty"))
205+ prop[1] = 32 // 3 items of 4 bytes overrun the 36-byte reply
206+ if _, ok := xproto.ParseGetPropertyReply(prop); ok {
207+ t.Fatal("format*value_len overrun accepted")
208+ }
209+}
210+
211+func TestReplyAllocs(t *testing.T) {
212+ prop := loadReply(t, "xproto/GetProperty")
213+ tree := loadReply(t, "xproto/QueryTree")
214+ exts := loadReply(t, "xproto/ListExtensions")
215+ formats := loadReply(t, "render/QueryPictFormats")
216+ ids := loadReply(t, "res/QueryClientIds")
217+ var sink uint64
218+ n := testing.AllocsPerRun(1000, func() {
219+ p, _ := xproto.ParseGetPropertyReply(prop)
220+ v := p.Value()
221+ sink += uint64(len(v)) + uint64(p.Type()) + uint64(p.Format())
222+ q, _ := xproto.ParseQueryTreeReply(tree)
223+ c := q.Children()
224+ for i := range c.Len() {
225+ sink += uint64(c.At(i))
226+ }
227+ sink += uint64(q.Root()) + uint64(q.Parent())
228+ l, _ := xproto.ParseListExtensionsReply(exts)
229+ it := l.Names()
230+ for it.Next() {
231+ sink += uint64(len(it.Value().Name()))
232+ }
233+ f, _ := render.ParseQueryPictFormatsReply(formats)
234+ fl := f.Formats()
235+ for i := range fl.Len() {
236+ sink += uint64(fl.At(i).Id)
237+ }
238+ s := f.Screens()
239+ for s.Next() {
240+ d := s.Value().Depths()
241+ for d.Next() {
242+ vs := d.Value().Visuals()
243+ for i := range vs.Len() {
244+ sink += uint64(vs.At(i).Visual)
245+ }
246+ }
247+ }
248+ sp := f.Subpixels()
249+ for i := range sp.Len() {
250+ sink += uint64(sp.At(i))
251+ }
252+ r, _ := res.ParseQueryClientIdsReply(ids)
253+ ri := r.Ids()
254+ for ri.Next() {
255+ sink += uint64(ri.Value().Value().Len())
256+ }
257+ })
258+ if n != 0 {
259+ t.Fatalf("reply parse/getters/iteration allocate %v times", n)
260+ }
261+ _ = sink
262+}
263+
264+func BenchmarkGetPropertyReply(b *testing.B) {
265+ raw := loadReply(b, "xproto/GetProperty")
266+ var sink int
267+ b.ReportAllocs()
268+ for b.Loop() {
269+ r, ok := xproto.ParseGetPropertyReply(raw)
270+ if !ok {
271+ b.Fatal("parse")
272+ }
273+ sink += len(r.Value()) + int(r.Type())
274+ }
275+ _ = sink
276+}
277+
278+func BenchmarkQueryTreeReply(b *testing.B) {
279+ raw := loadReply(b, "xproto/QueryTree")
280+ var sink xproto.Window
281+ b.ReportAllocs()
282+ for b.Loop() {
283+ r, _ := xproto.ParseQueryTreeReply(raw)
284+ c := r.Children()
285+ for i := range c.Len() {
286+ sink ^= c.At(i)
287+ }
288+ }
289+ _ = sink
290+}
291+
292+func BenchmarkListExtensionsReply(b *testing.B) {
293+ raw := loadReply(b, "xproto/ListExtensions")
294+ var sink int
295+ b.ReportAllocs()
296+ for b.Loop() {
297+ r, _ := xproto.ParseListExtensionsReply(raw)
298+ it := r.Names()
299+ for it.Next() {
300+ sink += len(it.Value().Name())
301+ }
302+ }
303+ _ = sink
304+}
A requests_test.go +62 -0
A res/replies_fuzz_test.go +146 -0
A res/replies_generated.go +348 -0
A res/requests_generated.go +144 -0
A res/sizes_test.go +47 -0
A res/types_generated.go +159 -0
A shape/replies_fuzz_test.go +86 -0
A shape/replies_generated.go +123 -0
A shape/requests_generated.go +214 -0
A shape/sizes_test.go +23 -0
A shape/types_generated.go +20 -0
A testdata/golden/render/CreatePicture.req.hex +3 -0
A testdata/golden/render/QueryPictFormats.reply.hex +3 -0
A testdata/golden/res/QueryClientIds.reply.hex +3 -0
A testdata/golden/res/QueryClientIds.req.hex +3 -0
A testdata/golden/xfixes/QueryVersion.reply.hex +3 -0
A testdata/golden/xfixes/QueryVersion.req.hex +3 -0
A testdata/golden/xproto/ChangeProperty.req.hex +3 -0
A testdata/golden/xproto/ChangeWindowAttributes.req.hex +3 -0
A testdata/golden/xproto/ConfigureWindow.req.hex +3 -0
A testdata/golden/xproto/ConfigureWindowAll.req.hex +3 -0
A testdata/golden/xproto/ConfigureWindowSparse.req.hex +3 -0
A testdata/golden/xproto/CreateWindow.req.hex +3 -0
A testdata/golden/xproto/GetGeometry.reply.hex +3 -0
A testdata/golden/xproto/GetGeometry.req.hex +3 -0
A testdata/golden/xproto/GetProperty.reply.hex +3 -0
A testdata/golden/xproto/GetPropertyEmpty.reply.hex +3 -0
A testdata/golden/xproto/GetWindowAttributes.reply.hex +3 -0
A testdata/golden/xproto/InternAtom.reply.hex +3 -0
A testdata/golden/xproto/InternAtom.req.hex +3 -0
A testdata/golden/xproto/ListExtensions.reply.hex +3 -0
A testdata/golden/xproto/MapWindow.req.hex +3 -0
A testdata/golden/xproto/QueryExtension.reply.hex +3 -0
A testdata/golden/xproto/QueryTree.reply.hex +3 -0
A values_test.go +103 -0
A xcmisc/replies_fuzz_test.go +60 -0
A xcmisc/replies_generated.go +80 -0
A xcmisc/requests_generated.go +59 -0
A xcmisc/sizes_test.go +17 -0
A xcmisc/types_generated.go +2 -0
A xfixes/replies_fuzz_test.go +136 -0
A xfixes/replies_generated.go +242 -0
A xfixes/requests_generated.go +766 -0
A xfixes/sizes_test.go +32 -0
A xfixes/types_generated.go +55 -0
A xproto/replies_fuzz_test.go +895 -0
A xproto/replies_generated.go +1964 -0
A xproto/requests_generated.go +2780 -0
A xproto/requests_test.go +83 -0
A xproto/sizes_test.go +293 -0
A xproto/types_generated.go +1353 -0
A xproto/types_test.go +54 -0

Comments4 open / 4

bnema outdated

T2.1 implemented: strict source-located XML AST for all 9 pinned modules, rejection tests, parser fuzz seed. Local make check and CGO_ENABLED=0 go build ./... pass; 3-second parser fuzz run passed (416270 executions). T1.1 is on main (ba4ad52), CI run 221 green, repo public. Stopped before T2.2/T2.3 for a public naming-contract decision: xproto WINDOW xidtype and Window enum both map to Go Window under the specified rules (likewise ATOM/Atom, CURSOR/Cursor, PIXMAP/Pixmap, FONT/Font; render PICTURE/Picture). Should these enums reuse the XID type for constants, or receive a distinct name (please specify the naming rule)? No workaround chosen. T2.4 must wait for T2.2/T2.3; AST preserves expressions, imports, attributes and XML field order. No goldens captured yet.

bnema outdated

T2.2 (85a51dd) and T2.3 are implemented. Applied the decided enum/XID type reuse rule; other declaration collisions are fatal. All nine packages compile; manifest-ordered root ExtIDs, type/struct/union encoders and size checks are generated. Fixed requests include core data-byte handling, negotiated extension major opcodes, padding and sticky-error guards. Full make check passed independently after each task (unfiltered output), CGO-disabled build passed, request/Buf allocation tests passed, and benchmarks show 0 B/op, 0 allocs/op. Local xtrace is glibc execution tracing, so the permitted raw socketpair tee fallback captures independent reference encodings accepted by Xvfb. Three request goldens committed; capture update also passes with race enabled. Decisions: variable structs have owned encoding forms now (borrowed View/Iter APIs remain with list/reply tasks); generation wiring brought forward so gen-check verifies this work. xfixes signatures use render/shape types where the XML imports require them (matching actual wire types). T2.4: extend resolveRequests beyond its explicit list/switch gate, lower AST length expressions with renderExpr, add derived-length and mismatch handling, extend independent capture encoder for InternAtom/ChangeProperty/res.QueryClientIds. Buf already supports aligned reservation, zeroing, sticky errors and growth; transport/ack updates remain Phase 3. CI 222 and 223 green; checking latest run next. Keep this MR draft; do not merge.

bnema outdated

Final verification: CI 224 logs show make check and make xserver passed, as did gen-check. Additional local 10-second parser fuzz run passed (~3.57 million executions); clean CGO_ENABLED=0 go test -count=1 ./... and go build ./... passed. Both signed task commits verified with GPG. Working trees clean, origin is ermite only; MR remains WIP/unmerged.

bnema outdated

T2.4 completed in signed commit 582be88. Full make check and make xserver passed; CGO_ENABLED=0 build passed; independent raw socketpair/Xvfb tee captured InternAtom, ChangeProperty (GetProperty read-back), and XRes QueryClientIds (negotiated XRes 1.2). Race-enabled capture update passed. Allocation tests cover InternAtom, 4 KiB ChangeProperty, and fixed struct lists; all zero. Benchmarks: InternAtom 7.714 ns/op, ChangeProperty4KiB 73.58 ns/op, both 0 B/op and 0 allocs/op. Decisions: support implicit list-name_len refs used by QueryTextExtents only when no explicit field exists; reject derived length truncation with ErrBadLength; bring SendEvent fixed-array signature forward from the explicit public contract. T2.5 should remove the switch gate in resolveRequests and implement typed Values structs/setters; request list sizes/offsets now use running body offsets, with final four-byte rounding. Shared lengths stay explicit (e.g. render AddGlyphs); variable structs use Size/Put. T2.5–T2.8 and T3.1–T3.5 not started; stopping at this clean boundary due to long context. Same branch/worktree, MR draft, no tag.

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
l
merge request: the list of files (j k move, enter opens, esc back)
M
merge request: the merge menu; there 1–3 pick the mode, D deletes the branch, enter merges
R
merge request: mark the head read
esc
close a dialog, leave a field

Branches 1

Tags 1

↑↓ moveenter openesc close