detached 16af5b8

Merge 722264ac773fc5df4f73671ee4fcce79537e93a6 into 3b9882544e8bb329ad3b1fae11af7e1cbaf76323

commit
16af5b818cf5f80e99b092d20007d6c6d2f9df22
author
ermite · · 2026-10-22 08:19 UTC
merge of
3b98825 + 722264a · diff against the first parent
signature
unsigned
43 files · +4569 -34
M cmd/xtgen/emit.go +5 -0
@@ -241,6 +241,11 @@ func emitTypes(modules []*Module) error {
241 var extensions []*Module241 var extensions []*Module
242 for _, m := range modules {242 for _, m := range modules {
243 if m.ExtName != "" {243 if m.ExtName != "" {
244+ count, err := eventCount(m)
245+ if err != nil {
246+ return err
247+ }
248+ m.NumEvents, m.NumErrors = count, errorCount(m)
244 extensions = append(extensions, m)249 extensions = append(extensions, m)
245 }250 }
246 }251 }
A cmd/xtgen/events.go +434 -0
@@ -0,0 +1,434 @@
1+package main
2+
3+import (
4+ "fmt"
5+ "path/filepath"
6+ "sort"
7+ "strconv"
8+ "strings"
9+)
10+
11+// eventIR is one <event> or <eventcopy>. Copies point at their source IR and
12+// share its container.
13+type eventIR struct {
14+ Name string // Go base name, e.g. MapRequest
15+ Code int // core: wire code; extension: relative code; XGE: evtype
16+ XGE bool
17+ Copy *eventIR
18+ C *container
19+ Node *Node
20+ KindExpr string // "" when the event has no Kind (core XGE envelope)
21+}
22+
23+const eventLifetime = "Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from."
24+
25+func eventCode(n *Node) (int, error) {
26+ code, err := strconv.Atoi(n.Attr["number"])
27+ if err != nil || code < 0 || code > 255 {
28+ return 0, n.fail("invalid event number %q", n.Attr["number"])
29+ }
30+ return code, nil
31+}
32+
33+func kindExpr(m *Module, e *eventIR) (string, error) {
34+ switch {
35+ case m.ExtName == "" && e.XGE:
36+ // The core XGE envelope is not an event type: DecodeEvent resolves
37+ // every XGE packet to its extension's kind or KindUnknown.
38+ return "", nil
39+ case m.ExtName == "":
40+ if e.Code < 2 || e.Code > 34 {
41+ return "", e.Node.fail("core event code %d outside 2..34", e.Code)
42+ }
43+ return fmt.Sprintf("xturbo.EventKind(%d)", e.Code), nil
44+ case e.XGE:
45+ return fmt.Sprintf("xturbo.EventKind(xturbo.Ext%s)<<8 | 0x80 | %d", m.ExtName, e.Code), nil
46+ }
47+ if e.Code > 127 {
48+ return "", e.Node.fail("extension event number %d above 127", e.Code)
49+ }
50+ if e.Code == 0 {
51+ // "| 0" is a no-op that staticcheck rejects (SA4016).
52+ return fmt.Sprintf("xturbo.EventKind(xturbo.Ext%s) << 8", m.ExtName), nil
53+ }
54+ return fmt.Sprintf("xturbo.EventKind(xturbo.Ext%s)<<8 | %d", m.ExtName, e.Code), nil
55+}
56+
57+// xgeReserved are the implicit XGE header getters.
58+const xgeReserved = "Raw Extension Length EventType Tail"
59+
60+func resolveEvents(m *Module) ([]*eventIR, error) {
61+ var events []*eventIR
62+ byXName := map[string]*eventIR{}
63+ for _, n := range m.Root.Children {
64+ if n.Kind != "event" && n.Kind != "eventcopy" {
65+ continue
66+ }
67+ code, err := eventCode(n)
68+ if err != nil {
69+ return nil, err
70+ }
71+ e := &eventIR{Name: goName(n.Attr["name"]), Code: code, Node: n}
72+ if n.Kind == "eventcopy" {
73+ src := byXName[n.Attr["ref"]]
74+ if src == nil {
75+ return nil, n.fail("eventcopy of unknown event %s", n.Attr["ref"])
76+ }
77+ if src.Copy != nil {
78+ src = src.Copy
79+ }
80+ e.Copy, e.C, e.XGE = src, src.C, src.XGE
81+ } else {
82+ e.XGE = n.Attr["xge"] == "true"
83+ lay, reserved := layoutEvent, "Raw"
84+ switch {
85+ case e.XGE:
86+ lay, reserved = layoutXGE, xgeReserved
87+ if code > 127 {
88+ return nil, n.fail("xge evtype %d >= 128", code)
89+ }
90+ case n.Attr["no-sequence-number"] == "true":
91+ lay = layoutEventNoSeq
92+ }
93+ c, err := buildContainer(m, e.Name+"Event", "e", n.Children, lay, reserved)
94+ if err != nil {
95+ return nil, err
96+ }
97+ if c.Dynamic >= 0 || c.Fixed > 32 {
98+ return nil, n.fail("event members must lie within the first 32 bytes")
99+ }
100+ for _, mem := range c.Members {
101+ if mem.Kind == "list" && mem.ListKind == "iter" {
102+ return nil, n.fail("variable-size struct list in event")
103+ }
104+ }
105+ e.C = c
106+ }
107+ if e.KindExpr, err = kindExpr(m, e); err != nil {
108+ return nil, err
109+ }
110+ names := []string{e.Name + "Event", e.Name + "Of"}
111+ if e.KindExpr != "" {
112+ names = append(names, "Kind"+e.Name)
113+ }
114+ if m.ExtName == "" && !e.XGE {
115+ names = append(names, e.Name+"EventData")
116+ }
117+ for _, s := range names {
118+ if err := m.claim(s, n); err != nil {
119+ return nil, err
120+ }
121+ }
122+ byXName[n.Attr["name"]] = e
123+ events = append(events, e)
124+ }
125+ return events, nil
126+}
127+
128+// dataField renders one EventData struct field and its Put statement.
129+func dataField(c *container, mem member) (field, put string, err error) {
130+ name := mem.Method
131+ if name == "Put" {
132+ name += "_"
133+ }
134+ src := "d." + name
135+ at := fmt.Sprintf("tmp[%d:]", mem.Static)
136+ switch {
137+ case mem.Kind == "field" && isScalar(mem.Type):
138+ return name + " " + mem.GoType, putScalar(mem.Type, src, at), nil
139+ case mem.Kind == "field":
140+ return name + " " + mem.GoType, fmt.Sprintf("%s.Put(%s);", src, at), nil
141+ case mem.Kind == "list" && mem.Count.Kind == "value":
142+ count := mem.Size / mem.Type.Size
143+ elem := mem.Elem
144+ var b strings.Builder
145+ fmt.Fprintf(&b, "for i := range %s {", src)
146+ target := fmt.Sprintf("tmp[%d+i*%d:]", mem.Static, mem.Type.Size)
147+ if isScalar(mem.Type) {
148+ b.WriteString(putScalar(mem.Type, src+"[i]", target))
149+ } else {
150+ fmt.Fprintf(&b, "%s[i].Put(%s);", src, target)
151+ }
152+ b.WriteString("};")
153+ return fmt.Sprintf("%s [%d]%s", name, count, elem), b.String(), nil
154+ }
155+ return "", "", c.Module.Root.fail("unsupported event member %s for EventData", mem.XName)
156+}
157+
158+func eventDecl(m *Module, e *eventIR, lists map[string]string) (string, error) {
159+ var b strings.Builder
160+ if e.KindExpr != "" {
161+ fmt.Fprintf(&b, "// Kind%s identifies %s events in xturbo.Event.Kind.\nconst Kind%s = %s;\n", e.Name, e.Name, e.Name, e.KindExpr)
162+ }
163+ view := e.Name + "Event"
164+ if e.Copy != nil {
165+ src := e.Copy.Name
166+ fmt.Fprintf(&b, "// %s shares %s's layout (eventcopy).\ntype %s = %sEvent;\n", view, src, view, src)
167+ fmt.Fprintf(&b, "// %sOf views ev as %s without checking ev.Kind.\nfunc %sOf(ev *xturbo.Event) %s { return %sOf(ev) };\n", e.Name, e.Name, e.Name, view, src)
168+ if m.ExtName == "" && !e.XGE {
169+ fmt.Fprintf(&b, "// %sEventData encodes a %s event for SendEvent with %s's fields.\ntype %sEventData %sEventData;\n", e.Name, e.Name, src, e.Name, src)
170+ fmt.Fprintf(&b, "// Put writes the event (code %d, pads and sequence zeroed) into dst.\nfunc (d *%sEventData) Put(dst *[32]byte) { (*%sEventData)(d).Put(dst); dst[0] = %d };\n", e.Code, e.Name, src, e.Code)
171+ }
172+ return b.String(), nil
173+ }
174+ c := e.C
175+ if e.XGE {
176+ fmt.Fprintf(&b, "// %s is a view over a %s generic event: the copied Event.Raw plus the borrowed Event.Tail.\n// Tail is valid only until the Handler callback returns; copy what you keep.\n", view, e.Name)
177+ fmt.Fprintf(&b, "type %s struct { raw *[32]byte; tail []byte };\n", view)
178+ fmt.Fprintf(&b, "// %sOf views ev as %s; false when ev is not a generic event or Tail does not match its length.\n", e.Name, e.Name)
179+ fmt.Fprintf(&b, "func %sOf(ev *xturbo.Event) (%s, bool) { if ev.Raw[0]&0x7f != 35 || len(ev.Tail) != 4*int(binary.LittleEndian.Uint32(ev.Raw[4:])) { return %s{}, false }; return %s{raw: &ev.Raw, tail: ev.Tail}, true };\n", e.Name, view, view, view)
180+ fmt.Fprintf(&b, "func (e %s) Extension() uint8 { return e.raw[1] };\n", view)
181+ fmt.Fprintf(&b, "func (e %s) Length() uint32 { return binary.LittleEndian.Uint32(e.raw[4:]) };\n", view)
182+ fmt.Fprintf(&b, "func (e %s) EventType() uint16 { return binary.LittleEndian.Uint16(e.raw[8:]) };\n", view)
183+ fmt.Fprintf(&b, "func (e %s) Tail() []byte { return e.tail };\n", view)
184+ } else {
185+ fmt.Fprintf(&b, "// %s is a view over a %s event's 32 bytes. %s\n", view, e.Name, eventLifetime)
186+ fmt.Fprintf(&b, "type %s struct { raw *[32]byte };\n", view)
187+ fmt.Fprintf(&b, "// %sOf views ev as %s without checking ev.Kind: a wrong kind yields garbage, never a panic.\n", e.Name, e.Name)
188+ fmt.Fprintf(&b, "func %sOf(ev *xturbo.Event) %s { return %s{raw: &ev.Raw} };\n", e.Name, view, view)
189+ }
190+ fmt.Fprintf(&b, "func (e %s) Raw() *[32]byte { return e.raw };\n", view)
191+ if err := c.getters(&b, view, lists); err != nil {
192+ return "", err
193+ }
194+ if m.ExtName != "" || e.XGE {
195+ return b.String(), nil
196+ }
197+ var fields, puts []string
198+ for _, mem := range c.Members {
199+ if mem.Kind == "pad" {
200+ continue
201+ }
202+ f, p, err := dataField(c, mem)
203+ if err != nil {
204+ return "", err
205+ }
206+ fields = append(fields, f)
207+ puts = append(puts, p)
208+ }
209+ fmt.Fprintf(&b, "// %sEventData encodes a %s event for SendEvent.\ntype %sEventData struct {\n%s\n};\n", e.Name, e.Name, e.Name, strings.Join(fields, "\n"))
210+ // Encode into a local array and store it last: borrowed fields (union
211+ // views such as ClientMessageData) may alias dst, e.g. d.Put(&ev.Raw).
212+ fmt.Fprintf(&b, "// Put writes the event (code %d, pads and sequence zeroed) into dst. dst may alias\n// memory that d's borrowed fields view, such as the Event.Raw they were read from.\nfunc (d *%sEventData) Put(dst *[32]byte) { var tmp [32]byte; tmp[0] = %d; %s; *dst = tmp };\n", e.Code, e.Name, e.Code, strings.Join(puts, ""))
213+ return b.String(), nil
214+}
215+
216+// errorIR is one <error> or <errorcopy>.
217+type errorIR struct {
218+ Const, Name string
219+ Code int
220+}
221+
222+func errorConst(xname string) string {
223+ return "Bad" + goName(strings.TrimPrefix(xname, "Bad"))
224+}
225+
226+func resolveErrors(m *Module) ([]errorIR, error) {
227+ var errs []errorIR
228+ seen := map[int]bool{}
229+ for _, n := range m.Root.Children {
230+ if n.Kind != "error" && n.Kind != "errorcopy" {
231+ continue
232+ }
233+ code, err := strconv.Atoi(n.Attr["number"])
234+ if err != nil || code < 0 || code > 255 || seen[code] {
235+ return nil, n.fail("invalid or duplicate error number %q", n.Attr["number"])
236+ }
237+ seen[code] = true
238+ e := errorIR{Const: errorConst(n.Attr["name"]), Code: code}
239+ e.Name = e.Const
240+ if err := m.claim(e.Const, n); err != nil {
241+ return nil, err
242+ }
243+ errs = append(errs, e)
244+ }
245+ return errs, nil
246+}
247+
248+// errorCount is one past the highest error number.
249+func errorCount(m *Module) int {
250+ count := 0
251+ for _, n := range m.Root.Children {
252+ if n.Kind == "error" || n.Kind == "errorcopy" {
253+ code, _ := strconv.Atoi(n.Attr["number"])
254+ count = max(count, code+1)
255+ }
256+ }
257+ return count
258+}
259+
260+// eventCount is one past the highest non-XGE event number.
261+func eventCount(m *Module) (int, error) {
262+ count := 0
263+ for _, n := range m.Root.Children {
264+ if (n.Kind == "event" || n.Kind == "eventcopy") && n.Attr["xge"] != "true" {
265+ code, err := eventCode(n)
266+ if err != nil {
267+ return 0, err
268+ }
269+ count = max(count, code+1)
270+ }
271+ }
272+ return count, nil
273+}
274+
275+func errorsDecl(m *Module, errs []errorIR) string {
276+ var b strings.Builder
277+ what := "wire error codes"
278+ if m.ExtName != "" {
279+ what = "error codes relative to the extension's FirstError (XError.Rel)"
280+ }
281+ fmt.Fprintf(&b, "// Error codes: %s.\nconst (\n", what)
282+ for _, e := range errs {
283+ fmt.Fprintf(&b, "%s uint8 = %d\n", e.Const, e.Code)
284+ }
285+ b.WriteString(");\n")
286+ names := make([]string, errorCount(m))
287+ for _, e := range errs {
288+ names[e.Code] = e.Name
289+ }
290+ b.WriteString("var errorNames = [...]string{")
291+ for _, n := range names {
292+ fmt.Fprintf(&b, "%q,", n)
293+ }
294+ b.WriteString("};\n")
295+ fmt.Fprintf(&b, "// ErrorName returns the name of an error code (%s), or \"\" when unknown.\n", what)
296+ b.WriteString("func ErrorName(code uint8) string { if int(code) < len(errorNames) { return errorNames[code] }; return \"\" };\n")
297+ return b.String()
298+}
299+
300+func emitEvents(modules []*Module) error {
301+ for _, m := range modules {
302+ events, err := resolveEvents(m)
303+ if err != nil {
304+ return err
305+ }
306+ lists := map[string]string{}
307+ var decls []string
308+ for _, e := range events {
309+ d, err := eventDecl(m, e, lists)
310+ if err != nil {
311+ return err
312+ }
313+ decls = append(decls, d)
314+ }
315+ var names []string
316+ for name := range lists {
317+ // Reuse a list view already declared by replies_generated.go.
318+ if old := m.Names[name]; old != nil && old == m.Root {
319+ continue
320+ }
321+ if err := m.claim(name, m.Root); err != nil {
322+ return err
323+ }
324+ names = append(names, name)
325+ }
326+ sort.Strings(names)
327+ for _, name := range names {
328+ decls = append(decls, lists[name])
329+ }
330+ errs, err := resolveErrors(m)
331+ if err != nil {
332+ return err
333+ }
334+ if len(errs) > 0 {
335+ data := struct {
336+ Pkg string
337+ Decls []string
338+ }{Pkg: m.Pkg, Decls: []string{errorsDecl(m, errs)}}
339+ if err := writeTemplate(filepath.Join(m.Pkg, "errors_generated.go"), "errors", data); err != nil {
340+ return err
341+ }
342+ }
343+ if len(decls) == 0 {
344+ continue
345+ }
346+ imports := importsFor(m, strings.Join(decls, "\n")) + "\nimport \"bnema.dev/xturbo\""
347+ decls = append([]string{imports}, decls...)
348+ data := struct {
349+ Pkg string
350+ Decls []string
351+ }{Pkg: m.Pkg, Decls: decls}
352+ if err := writeTemplate(filepath.Join(m.Pkg, "events_generated.go"), "events", data); err != nil {
353+ return err
354+ }
355+ if err := emitEventFuzz(m, events); err != nil {
356+ return err
357+ }
358+ }
359+ return nil
360+}
361+
362+// emitEventFuzz writes FuzzEventViews: every view getter runs on arbitrary
363+// bytes, and for core events EventData built from the getters survives a
364+// Put/view round trip. Seeds are the package's event goldens.
365+func emitEventFuzz(m *Module, events []*eventIR) error {
366+ var b strings.Builder
367+ b.WriteString("func FuzzEventViews(f *testing.F) {\nf.Add(make([]byte, 32));\n")
368+ goldens, _ := filepath.Glob(filepath.Join("testdata/golden", m.Pkg, "*.event.hex"))
369+ sort.Strings(goldens)
370+ for _, g := range goldens {
371+ fmt.Fprintf(&b, "if raw, err := xtest.LoadHex(%q); err != nil { f.Fatal(err) } else { f.Add(raw) };\n", "../"+filepath.ToSlash(g))
372+ }
373+ b.WriteString("f.Fuzz(func(t *testing.T, raw []byte) {\nif len(raw) < 32 { return };\nvar ev xturbo.Event;\ncopy(ev.Raw[:], raw);\nev.Tail = raw[32:];\n")
374+ for _, e := range events {
375+ if e.Copy != nil {
376+ fmt.Fprintf(&b, "_ = %sOf(&ev);\n", e.Name)
377+ continue
378+ }
379+ v := "v" + e.Name
380+ if e.XGE {
381+ fmt.Fprintf(&b, "if %s, ok := %sOf(&ev); ok { _ = %s.Extension(); _ = %s.Length(); _ = %s.EventType(); _ = len(%s.Tail());", v, e.Name, v, v, v, v)
382+ } else {
383+ fmt.Fprintf(&b, "{ %s := %sOf(&ev);", v, e.Name)
384+ }
385+ touch(&b, e.C, v, 0, nil, `t.Fatal("iterator error")`)
386+ if m.ExtName == "" && !e.XGE {
387+ var fields, checks []string
388+ for _, mem := range e.C.Members {
389+ if mem.Kind == "pad" {
390+ continue
391+ }
392+ name := mem.Method
393+ if name == "Put" {
394+ name += "_"
395+ }
396+ switch {
397+ case mem.Kind == "list" && mem.ListKind == "bytes":
398+ fields = append(fields, fmt.Sprintf("%s: [%d]%s(%s.%s())", name, mem.Size/mem.Type.Size, mem.Elem, v, mem.Method))
399+ checks = append(checks, fmt.Sprintf("if !bytes.Equal(w.%s(), %s.%s()) { t.Fatal(%q) };", mem.Method, v, mem.Method, e.Name+"."+mem.Method))
400+ case mem.Kind == "field" && isScalar(mem.Type):
401+ fields = append(fields, fmt.Sprintf("%s: %s.%s()", name, v, mem.Method))
402+ checks = append(checks, fmt.Sprintf("if w.%s() != %s.%s() { t.Fatal(%q) };", mem.Method, v, mem.Method, e.Name+"."+mem.Method))
403+ case mem.Kind == "field":
404+ fields = append(fields, fmt.Sprintf("%s: %s.%s()", name, v, mem.Method))
405+ checks = append(checks, fmt.Sprintf("if !bytes.Equal(out[%d:%d], ev.Raw[%d:%d]) { t.Fatal(%q) };", mem.Static, mem.Static+mem.Size, mem.Static, mem.Static+mem.Size, e.Name+"."+mem.Method))
406+ default:
407+ return e.Node.fail("event member %s has no EventData round trip", mem.XName)
408+ }
409+ }
410+ fmt.Fprintf(&b, "d := %sEventData{%s}; var out [32]byte; d.Put(&out);", e.Name, strings.Join(fields, ", "))
411+ fmt.Fprintf(&b, "if out[0] != %d { t.Fatal(\"%s code\") };", e.Code, e.Name)
412+ b.WriteString("back := xturbo.Event{Raw: out};")
413+ fmt.Fprintf(&b, "w := %sOf(&back); %s", e.Name, strings.Join(checks, ""))
414+ // In place: dst aliases the Raw that d's borrowed fields view.
415+ fmt.Fprintf(&b, "in := xturbo.Event{Raw: ev.Raw}; vi := %sOf(&in); di := %sEventData{%s}; di.Put(&in.Raw); if in.Raw != out { t.Fatal(%q) };",
416+ e.Name, e.Name, strings.ReplaceAll(strings.Join(fields, ", "), v+".", "vi."), e.Name+" in-place Put")
417+ }
418+ b.WriteString("};\n")
419+ }
420+ b.WriteString("})\n}\n")
421+ body := b.String()
422+ imports := "import \"testing\"\nimport \"bnema.dev/xturbo\""
423+ if len(goldens) > 0 {
424+ imports += "\nimport \"bnema.dev/xturbo/internal/xtest\""
425+ }
426+ if strings.Contains(body, "bytes.") {
427+ imports += "\nimport \"bytes\""
428+ }
429+ data := struct {
430+ Pkg string
431+ Decls []string
432+ }{Pkg: m.Pkg, Decls: []string{imports, body}}
433+ return writeTemplate(filepath.Join(m.Pkg, "events_fuzz_test.go"), "events", data)
434+}
A cmd/xtgen/events_test.go +82 -0
@@ -0,0 +1,82 @@
1+package main
2+
3+import (
4+ "strings"
5+ "testing"
6+)
7+
8+// TestEventLayouts pins wire offsets that the X11 protocol defines, checked
9+// against the event containers (independent of generated code).
10+func TestEventLayouts(t *testing.T) {
11+ want := map[string]map[string]int{
12+ "xproto.MapRequest": {"Parent": 4, "Window": 8},
13+ "xproto.ConfigureRequest": {"StackMode": 1, "Parent": 4, "Window": 8, "Sibling": 12, "X": 16, "Y": 18, "Width": 20, "Height": 22, "BorderWidth": 24, "ValueMask": 26},
14+ "xproto.KeymapNotify": {"Keys": 1},
15+ "xproto.ClientMessage": {"Format": 1, "Window": 4, "Type": 8, "Data": 12},
16+ "xproto.PropertyNotify": {"Window": 4, "Atom": 8, "Time": 12, "State": 16},
17+ "xfixes.SelectionNotify": {"Subtype": 1, "Window": 4, "Owner": 8, "Selection": 12, "Timestamp": 16, "SelectionTimestamp": 20},
18+ "shape.Notify": {"ShapeKind": 1, "AffectedWindow": 4, "ServerTime": 16, "Shaped": 20},
19+ }
20+ copies := 0
21+ for _, m := range testModules(t) {
22+ events, err := resolveEvents(m)
23+ if err != nil {
24+ t.Fatal(err)
25+ }
26+ for _, e := range events {
27+ if e.Copy != nil {
28+ copies++
29+ if e.C != e.Copy.C {
30+ t.Errorf("%s: copy does not share its source layout", e.Name)
31+ }
32+ continue
33+ }
34+ exp := want[m.Pkg+"."+e.Name]
35+ for _, mem := range e.C.Members {
36+ if off, ok := exp[mem.Method]; ok {
37+ if mem.Static != off {
38+ t.Errorf("%s.%s.%s at %d want %d", m.Pkg, e.Name, mem.Method, mem.Static, off)
39+ }
40+ delete(exp, mem.Method)
41+ }
42+ }
43+ if len(exp) != 0 {
44+ t.Errorf("%s.%s: missing %v", m.Pkg, e.Name, exp)
45+ }
46+ }
47+ }
48+ if copies != 5 {
49+ t.Errorf("xproto has 5 eventcopy, got %d", copies)
50+ }
51+}
52+
53+func TestEventGeneratorRejects(t *testing.T) {
54+ for _, tc := range []struct{ body, want string }{
55+ {`<event name="E" number="200" xge="true"><pad bytes="22"/></event>`, "xge evtype"},
56+ {`<event name="E" number="1"><field type="CARD32" name="a"/></event>`, "header data byte"},
57+ {`<eventcopy name="C" number="3" ref="Missing"/>`, "unknown event"},
58+ {`<event name="E" number="2"><pad bytes="1"/><list type="CARD8" name="l"><fieldref>n</fieldref></list></event>`, "first 32 bytes"},
59+ } {
60+ // Some constructs are already rejected by the parser; either stage must
61+ // fail with a source location.
62+ n, err := parseXML("ev.xml", strings.NewReader(`<xcb header="test" extension-name="Test" extension-xname="TEST">`+tc.body+`</xcb>`))
63+ if err == nil {
64+ var ms []*Module
65+ if ms, err = resolveModules([]*Node{n}); err != nil {
66+ t.Fatal(err)
67+ }
68+ _, err = resolveEvents(ms[0])
69+ }
70+ if err == nil || !strings.Contains(err.Error(), tc.want) || !strings.Contains(err.Error(), "ev.xml:1:") {
71+ t.Errorf("%s: got %v", tc.want, err)
72+ }
73+ }
74+}
75+
76+func TestErrorConstNames(t *testing.T) {
77+ for in, want := range map[string]string{"Window": "BadWindow", "BadRegion": "BadRegion", "PictFormat": "BadPictFormat", "IDChoice": "BadIDChoice"} {
78+ if got := errorConst(in); got != want {
79+ t.Errorf("%s => %s want %s", in, got, want)
80+ }
81+ }
82+}
M cmd/xtgen/ir.go +1 -0
@@ -36,4 +36,5 @@ type Module struct {
36 Symbols map[string]*Type36 Symbols map[string]*Type
37 Names map[string]*Node37 Names map[string]*Node
38 Decls, SizeChecks []string38 Decls, SizeChecks []string
39+ NumEvents, NumErrors int // extension event/error code ranges
39 }40 }
M cmd/xtgen/main.go +3 -0
@@ -35,6 +35,9 @@ func main() {
35 if err == nil {35 if err == nil {
36 err = emitReplies(modules)36 err = emitReplies(modules)
37 }37 }
38+ if err == nil {
39+ err = emitEvents(modules)
40+ }
38 if err != nil {41 if err != nil {
39 fmt.Fprintln(os.Stderr, err)42 fmt.Fprintln(os.Stderr, err)
40 os.Exit(1)43 os.Exit(1)
M cmd/xtgen/replies.go +39 -10
@@ -58,14 +58,43 @@ func elemName(goType string) string {
58 return strings.ToUpper(goType[:1]) + goType[1:]58 return strings.ToUpper(goType[:1]) + goType[1:]
59 }59 }
60 60
61-// buildContainer lowers XML members. Replies start at byte 1 (the first XML61+// layout selects where a container's XML members land on the wire.
62-// member occupies the header data byte) and continue at byte 8.62+type layout int
63-func buildContainer(m *Module, name, recv string, nodes []*Node, reply bool, reserved string) (*container, error) {63+
64+const (
65+ layoutStruct layout = iota // members from byte 0
66+ layoutReply // byte 1, then byte 8 (after seq and length)
67+ layoutEvent // byte 1, then byte 4 (after seq)
68+ layoutEventNoSeq // contiguous from byte 1
69+ layoutXGE // from byte 10 (after extension, seq, length, evtype)
70+)
71+
72+// headerJump returns the offset following the header data byte, or 0 when the
73+// layout has no data byte.
74+func (l layout) headerJump() int {
75+ switch l {
76+ case layoutReply:
77+ return 8
78+ case layoutEvent:
79+ return 4
80+ }
81+ return 0
82+}
83+
84+// buildContainer lowers XML members. Replies and events start at byte 1 (the
85+// first XML member occupies the header data byte) and continue after the
86+// sequence (and, for replies, length) header.
87+func buildContainer(m *Module, name, recv string, nodes []*Node, lay layout, reserved string) (*container, error) {
88+ reply := lay == layoutReply
64 c := &container{Name: name, Recv: recv, Reply: reply, Dynamic: -1, Module: m}89 c := &container{Name: name, Recv: recv, Reply: reply, Dynamic: -1, Module: m}
65 off := 090 off := 0
66- if reply {91+ switch lay {
92+ case layoutReply, layoutEvent, layoutEventNoSeq:
67 off = 193 off = 1
94+ case layoutXGE:
95+ off = 10
68 }96 }
97+ what := map[layout]string{layoutStruct: "struct", layoutReply: "reply", layoutEvent: "event", layoutEventNoSeq: "event", layoutXGE: "event"}[lay]
69 first := true98 first := true
70 methods := map[string]bool{}99 methods := map[string]bool{}
71 for _, n := range nodes {100 for _, n := range nodes {
@@ -138,13 +167,13 @@ func buildContainer(m *Module, name, recv string, nodes []*Node, reply bool, res
138 c.Dynamic = len(c.Members)167 c.Dynamic = len(c.Members)
139 }168 }
140 default:169 default:
141- return nil, n.fail("unsupported %s member %s", map[bool]string{true: "reply", false: "struct"}[reply], n.Kind)170+ return nil, n.fail("unsupported %s member %s", what, n.Kind)
142 }171 }
143- if reply && first {172+ if jump := lay.headerJump(); jump > 0 && first {
144 if mem.Size != 1 {173 if mem.Size != 1 {
145- return nil, n.fail("first reply member must occupy the header data byte")174+ return nil, n.fail("first %s member must occupy the header data byte", what)
146 }175 }
147- off = 8176+ off = jump
148 first = false177 first = false
149 c.Members = append(c.Members, mem)178 c.Members = append(c.Members, mem)
150 continue179 continue
@@ -340,7 +369,7 @@ func resolveReplies(m *Module) ([]replyIR, []*container, error) {
340 return nil, nil, err369 return nil, nil, err
341 }370 }
342 }371 }
343- c, err := buildContainer(m, name+"Reply", "r", r.Children, true, "Raw")372+ c, err := buildContainer(m, name+"Reply", "r", r.Children, layoutReply, "Raw")
344 if err != nil {373 if err != nil {
345 return nil, nil, err374 return nil, nil, err
346 }375 }
@@ -357,7 +386,7 @@ func resolveReplies(m *Module) ([]replyIR, []*container, error) {
357 return nil, nil, err386 return nil, nil, err
358 }387 }
359 }388 }
360- c, err := buildContainer(m, t.GoType+"View", "v", t.Node.Children, false, "Raw Size")389+ c, err := buildContainer(m, t.GoType+"View", "v", t.Node.Children, layoutStruct, "Raw Size")
361 if err != nil {390 if err != nil {
362 return nil, nil, err391 return nil, nil, err
363 }392 }
M cmd/xtgen/replies_test.go +1 -1
@@ -60,7 +60,7 @@ func TestReplyReservedGetter(t *testing.T) {
60 {Kind: "pad", Attr: map[string]string{"bytes": "1"}},60 {Kind: "pad", Attr: map[string]string{"bytes": "1"}},
61 {Kind: "field", Attr: map[string]string{"name": "raw", "type": "CARD32"}},61 {Kind: "field", Attr: map[string]string{"name": "raw", "type": "CARD32"}},
62 }62 }
63- c, err := buildContainer(m, "XReply", "r", nodes, true, "Raw")63+ c, err := buildContainer(m, "XReply", "r", nodes, layoutReply, "Raw")
64 if err != nil {64 if err != nil {
65 t.Fatal(err)65 t.Fatal(err)
66 }66 }
A cmd/xtgen/templates/errors.tmpl +5 -0
@@ -0,0 +1,5 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package {{.Pkg}}
3+{{range .Decls}}
4+{{.}}
5+{{end}}
A cmd/xtgen/templates/events.tmpl +5 -0
@@ -0,0 +1,5 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package {{.Pkg}}
3+{{range .Decls}}
4+{{.}}
5+{{end}}
M cmd/xtgen/templates/ext.tmpl +4 -0
@@ -8,3 +8,7 @@ const (
8 {{end}} extCount8 {{end}} extCount
9 )9 )
10 var ExtXName = [extCount]string{"", {{range .}}{{printf "%q" .XName}},{{end}}}10 var ExtXName = [extCount]string{"", {{range .}}{{printf "%q" .XName}},{{end}}}
11+// extEvents and extErrors are the event and error code ranges each extension
12+// declares in the pinned XML; ExtTable.Set uses them to fill reverse maps.
13+var extEvents = [extCount]uint8{0, {{range .}}{{.NumEvents}},{{end}}}
14+var extErrors = [extCount]uint8{0, {{range .}}{{.NumErrors}},{{end}}}
A event.go +77 -0
@@ -0,0 +1,77 @@
1+package xturbo
2+
3+import "encoding/binary"
4+
5+// EventKind identifies a decoded event: uint16(ExtID)<<8 | code. Core events
6+// use the wire code (2..34); extension events the code relative to the
7+// extension's first event; XGE events 0x80|evtype.
8+type EventKind uint16
9+
10+// KindUnknown marks an event whose code or XGE extension is not known.
11+const KindUnknown EventKind = 0xFFFF
12+
13+const (
14+ codeGenericEvent = 35
15+ codeKeymapNotify = 11
16+)
17+
18+// Event is a decoded event value. Raw is a copy and survives Dispatch; Tail
19+// borrows the read buffer (XGE bytes beyond 32) and is valid only until the
20+// Handler callback returns. Copy what you keep.
21+type Event struct {
22+ Kind EventKind
23+ SendEvent bool
24+ Ext ExtID
25+ Seq Seq // wire sequence; Conn widens it. 0 for KeymapNotify.
26+ Raw [32]byte
27+ Tail []byte
28+}
29+
30+// DecodeEvent decodes one event packet at the start of raw. n is the number
31+// of bytes consumed (32, or 32+4*length for XGE). ok is false for error and
32+// reply packets and for short input. Unknown codes decode with
33+// Kind == KindUnknown and ok == true. A nil ext decodes core events only.
34+// The returned Seq is the 16-bit wire value; Conn widens it.
35+func DecodeEvent(raw []byte, ext *ExtTable) (ev Event, n int, ok bool) {
36+ if len(raw) < 32 {
37+ return Event{}, 0, false
38+ }
39+ code := raw[0] & 0x7f
40+ if code < 2 {
41+ return Event{}, 0, false
42+ }
43+ n = 32
44+ if code == codeGenericEvent {
45+ length := uint64(binary.LittleEndian.Uint32(raw[4:]))
46+ if uint64(len(raw)-32)/4 < length {
47+ return Event{}, 0, false
48+ }
49+ n = 32 + int(length)*4
50+ ev.Tail = raw[32:n:n]
51+ }
52+ copy(ev.Raw[:], raw[:32])
53+ ev.SendEvent = raw[0]&0x80 != 0
54+ if code != codeKeymapNotify {
55+ ev.Seq = Seq(binary.LittleEndian.Uint16(raw[2:]))
56+ }
57+ ev.Kind = KindUnknown
58+ switch {
59+ case code < codeGenericEvent:
60+ ev.Kind = EventKind(code)
61+ case code == codeGenericEvent:
62+ if ext == nil {
63+ break
64+ }
65+ evtype := binary.LittleEndian.Uint16(raw[8:])
66+ if id, found := ext.ByMajor(raw[1]); found && evtype < 0x80 {
67+ ev.Ext = id
68+ ev.Kind = EventKind(id)<<8 | 0x80 | EventKind(evtype)
69+ }
70+ case code >= 64 && ext != nil:
71+ if id, rel, found := ext.ByEvent(code); found {
72+ ev.Ext = id
73+ ev.Kind = EventKind(id)<<8 | EventKind(rel)
74+ }
75+ }
76+ return ev, n, true
77+}
A events_test.go +499 -0
@@ -0,0 +1,499 @@
1+package xturbo_test
2+
3+import (
4+ "bytes"
5+ "encoding/binary"
6+ "testing"
7+
8+ "bnema.dev/xturbo"
9+ "bnema.dev/xturbo/internal/xtest"
10+ "bnema.dev/xturbo/render"
11+ "bnema.dev/xturbo/shape"
12+ "bnema.dev/xturbo/xfixes"
13+ "bnema.dev/xturbo/xproto"
14+)
15+
16+// Xvfb negotiated XFIXES as major 138, first event 87, first error 140 when the
17+// goldens were captured; the BadRegion and SelectionNotify goldens confirm it.
18+const (
19+ xfixesMajor, xfixesEvent, xfixesError = 138, 87, 140
20+ geMajor = 0x83 // hand-written XGE golden
21+)
22+
23+func loadEvent(t testing.TB, name string) []byte {
24+ t.Helper()
25+ raw, err := xtest.LoadHex("testdata/golden/" + name + ".event.hex")
26+ if err != nil {
27+ t.Fatal(err)
28+ }
29+ return raw
30+}
31+
32+func goldenExt() *xturbo.ExtTable {
33+ var ext xturbo.ExtTable
34+ ext.Set(xturbo.ExtXFixes, xfixesMajor, xfixesEvent, xfixesError)
35+ ext.Set(xturbo.ExtGenericEvent, geMajor, 0, 0)
36+ ext.Set(xturbo.ExtShape, 129, 64, 0)
37+ return &ext
38+}
39+
40+func decode(t testing.TB, raw []byte, ext *xturbo.ExtTable) xturbo.Event {
41+ t.Helper()
42+ ev, n, ok := xturbo.DecodeEvent(raw, ext)
43+ if !ok || n != len(raw) || !bytes.Equal(ev.Raw[:], raw[:32]) {
44+ t.Fatalf("DecodeEvent(%x) = n %d ok %v", raw, n, ok)
45+ }
46+ return ev
47+}
48+
49+// Expected values follow from the capture script (second client's window at
50+// 10,20 300x200, ConfigureWindow x=-5 y=40 width=640, a format-32 ClientMessage
51+// with words 0x01020304*(i+1), SetSelectionOwner PRIMARY) or from raw golden
52+// offsets defined by the X11 protocol, not from generated code.
53+func TestEventGoldens(t *testing.T) {
54+ u32 := func(raw []byte, off int) uint32 { return binary.LittleEndian.Uint32(raw[off:]) }
55+ ext := goldenExt()
56+ core := func(name string, kind xturbo.EventKind) ([]byte, xturbo.Event) {
57+ raw := loadEvent(t, "xproto/"+name)
58+ ev := decode(t, raw, ext)
59+ if ev.Kind != kind || ev.Ext != xturbo.ExtCore || ev.Seq != xturbo.Seq(binary.LittleEndian.Uint16(raw[2:])) || ev.Tail != nil {
60+ t.Fatalf("%s: kind %d ext %d seq %d", name, ev.Kind, ev.Ext, ev.Seq)
61+ }
62+ return raw, ev
63+ }
64+ t.Run("MapRequest", func(t *testing.T) {
65+ raw, ev := core("MapRequest", xproto.KindMapRequest)
66+ e := xproto.MapRequestOf(&ev)
67+ if ev.SendEvent || e.Parent() != xproto.Window(u32(raw, 4)) || e.Window() != xproto.Window(u32(raw, 8)) || e.Window()&0xffff != 1 {
68+ t.Fatal("fields")
69+ }
70+ d := xproto.MapRequestEventData{Parent: e.Parent(), Window: e.Window()}
71+ var out [32]byte
72+ d.Put(&out)
73+ want := [32]byte(raw)
74+ want[2], want[3] = 0, 0 // Put leaves the sequence to the server
75+ if out != want {
76+ t.Fatalf("Put %x want %x", out, want)
77+ }
78+ })
79+ t.Run("ConfigureRequest", func(t *testing.T) {
80+ raw, ev := core("ConfigureRequest", xproto.KindConfigureRequest)
81+ e := xproto.ConfigureRequestOf(&ev)
82+ if e.StackMode() != xproto.StackModeAbove || e.Parent() != xproto.Window(u32(raw, 4)) || e.Window()&0xffff != 1 ||
83+ e.Sibling() != xproto.WindowNone || e.X() != -5 || e.Y() != 40 || e.Width() != 640 || e.Height() != 200 ||
84+ e.BorderWidth() != 0 || e.ValueMask() != xproto.ConfigWindowX|xproto.ConfigWindowY|xproto.ConfigWindowWidth {
85+ t.Fatalf("fields x=%d y=%d w=%d h=%d mask=%d", e.X(), e.Y(), e.Width(), e.Height(), e.ValueMask())
86+ }
87+ d := xproto.ConfigureRequestEventData{StackMode: e.StackMode(), Parent: e.Parent(), Window: e.Window(), Sibling: e.Sibling(),
88+ X: e.X(), Y: e.Y(), Width: e.Width(), Height: e.Height(), BorderWidth: e.BorderWidth(), ValueMask: e.ValueMask()}
89+ var out [32]byte
90+ d.Put(&out)
91+ if !bytes.Equal(out[4:], raw[4:]) || out[0] != 23 || out[1] != raw[1] {
92+ t.Fatalf("Put %x", out)
93+ }
94+ })
95+ t.Run("PropertyNotify", func(t *testing.T) {
96+ raw, ev := core("PropertyNotify", xproto.KindPropertyNotify)
97+ e := xproto.PropertyNotifyOf(&ev)
98+ if e.Window() != xproto.Window(u32(raw, 4)) || e.Atom() != xproto.Atom(u32(raw, 8)) || e.Atom() < 69 ||
99+ e.Time() != xproto.Timestamp(u32(raw, 12)) || e.State() != xproto.PropertyNewValue {
100+ t.Fatal("fields")
101+ }
102+ })
103+ t.Run("ClientMessage", func(t *testing.T) {
104+ raw, ev := core("ClientMessage", xproto.KindClientMessage)
105+ e := xproto.ClientMessageOf(&ev)
106+ if !ev.SendEvent || e.Format() != 32 || e.Window()&0xffff != 1 || e.Type() != xproto.Atom(u32(raw, 8)) {
107+ t.Fatal("fields")
108+ }
109+ words := e.Data().Data32()
110+ for i, w := range words {
111+ if w != uint32(0x01020304*(i+1)) {
112+ t.Fatalf("data32[%d] = %x", i, w)
113+ }
114+ }
115+ d := xproto.ClientMessageEventData{Format: 32, Window: e.Window(), Type: e.Type(), Data: e.Data()}
116+ var out [32]byte
117+ d.Put(&out)
118+ want := [32]byte(raw)
119+ want[0] &^= 0x80
120+ want[2], want[3] = 0, 0
121+ if out != want {
122+ t.Fatalf("Put %x want %x", out, want)
123+ }
124+ })
125+ t.Run("CreateNotify", func(t *testing.T) {
126+ _, ev := core("CreateNotify", xproto.KindCreateNotify)
127+ e := xproto.CreateNotifyOf(&ev)
128+ if e.X() != 10 || e.Y() != 20 || e.Width() != 300 || e.Height() != 200 || e.OverrideRedirect() {
129+ t.Fatal("fields")
130+ }
131+ })
132+ t.Run("Map/Unmap/DestroyNotify", func(t *testing.T) {
133+ _, ev := core("MapNotify", xproto.KindMapNotify)
134+ if m := xproto.MapNotifyOf(&ev); m.Window()&0xffff != 1 || m.OverrideRedirect() {
135+ t.Fatal("MapNotify")
136+ }
137+ _, ev = core("UnmapNotify", xproto.KindUnmapNotify)
138+ if u := xproto.UnmapNotifyOf(&ev); u.Window()&0xffff != 1 || u.FromConfigure() {
139+ t.Fatal("UnmapNotify")
140+ }
141+ _, ev = core("DestroyNotify", xproto.KindDestroyNotify)
142+ if d := xproto.DestroyNotifyOf(&ev); d.Window()&0xffff != 1 {
143+ t.Fatal("DestroyNotify")
144+ }
145+ })
146+ t.Run("SelectionNotify", func(t *testing.T) {
147+ _, ev := core("SelectionNotify", xproto.KindSelectionNotify)
148+ e := xproto.SelectionNotifyOf(&ev)
149+ if e.Requestor()&0xffff != 1 || e.Selection() != xproto.AtomSecondary || e.Target() != xproto.AtomString || e.Property() != xproto.AtomNone {
150+ t.Fatal("fields")
151+ }
152+ })
153+ t.Run("xfixes/SelectionNotify", func(t *testing.T) {
154+ raw := loadEvent(t, "xfixes/SelectionNotify")
155+ ev := decode(t, raw, ext)
156+ if xfixes.KindSelectionNotify != xturbo.EventKind(xturbo.ExtXFixes)<<8 {
157+ t.Fatal("KindSelectionNotify value")
158+ }
159+ if ev.Kind != xfixes.KindSelectionNotify || ev.Ext != xturbo.ExtXFixes {
160+ t.Fatalf("kind %x", ev.Kind)
161+ }
162+ e := xfixes.SelectionNotifyOf(&ev)
163+ if e.Subtype() != xfixes.SelectionEventSetSelectionOwner || e.Window() != xproto.Window(u32(raw, 4)) ||
164+ e.Owner()&0xffff != 1 || e.Selection() != xproto.AtomPrimary || e.Timestamp() != e.SelectionTimestamp() {
165+ t.Fatal("fields")
166+ }
167+ // Without XFIXES negotiated the same bytes are an unknown event.
168+ if ev, _, ok := xturbo.DecodeEvent(raw, nil); !ok || ev.Kind != xturbo.KindUnknown {
169+ t.Fatal("nil ext must decode as unknown")
170+ }
171+ })
172+ t.Run("KeymapNotify", func(t *testing.T) {
173+ raw := loadEvent(t, "xproto/KeymapNotify")
174+ ev := decode(t, raw, ext)
175+ if ev.Kind != xproto.KindKeymapNotify || ev.Seq != 0 {
176+ t.Fatalf("kind %d seq %d", ev.Kind, ev.Seq)
177+ }
178+ e := xproto.KeymapNotifyOf(&ev)
179+ if !bytes.Equal(e.Keys(), raw[1:]) {
180+ t.Fatal("keys")
181+ }
182+ d := xproto.KeymapNotifyEventData{Keys: [31]uint8(raw[1:])}
183+ var out [32]byte
184+ d.Put(&out)
185+ if !bytes.Equal(out[:], raw) {
186+ t.Fatalf("Put %x", out)
187+ }
188+ })
189+ t.Run("GeGeneric", func(t *testing.T) {
190+ raw := loadEvent(t, "xproto/GeGeneric")
191+ ev := decode(t, raw, ext)
192+ if ev.Kind != xturbo.EventKind(xturbo.ExtGenericEvent)<<8|0x80|1 || ev.Ext != xturbo.ExtGenericEvent || ev.Seq != 5 ||
193+ !bytes.Equal(ev.Tail, raw[32:]) {
194+ t.Fatalf("kind %x tail %x", ev.Kind, ev.Tail)
195+ }
196+ g, ok := xproto.GeGenericOf(&ev)
197+ if !ok || g.Extension() != geMajor || g.Length() != 2 || g.EventType() != 1 || !bytes.Equal(g.Tail(), raw[32:]) {
198+ t.Fatal("view")
199+ }
200+ // Unknown major: still framed so Dispatch skips the tail.
201+ ev, n, ok := xturbo.DecodeEvent(raw, nil)
202+ if !ok || n != 40 || ev.Kind != xturbo.KindUnknown || len(ev.Tail) != 8 {
203+ t.Fatal("unknown XGE framing")
204+ }
205+ ev.Tail = ev.Tail[:4]
206+ if _, ok := xproto.GeGenericOf(&ev); ok {
207+ t.Fatal("short Tail accepted")
208+ }
209+ })
210+}
211+
212+// TestEventDataPutInPlace re-encodes events into the Event.Raw their fields
213+// were read from. ClientMessage's Data is a union view over Raw; KeymapNotify's
214+// Keys is a slice view over Raw. Put must read every field before writing dst.
215+func TestEventDataPutInPlace(t *testing.T) {
216+ raw := loadEvent(t, "xproto/ClientMessage")
217+ ev := decode(t, raw, nil)
218+ e := xproto.ClientMessageOf(&ev)
219+ d := xproto.ClientMessageEventData{Format: e.Format(), Window: e.Window(), Type: e.Type(), Data: e.Data()}
220+ d.Put(&ev.Raw)
221+ want := [32]byte(raw)
222+ want[0] &^= 0x80
223+ want[2], want[3] = 0, 0
224+ if ev.Raw != want {
225+ t.Fatalf("ClientMessage in place %x want %x", ev.Raw, want)
226+ }
227+ if n := testing.AllocsPerRun(1000, func() { d.Data = e.Data(); d.Put(&ev.Raw) }); n != 0 {
228+ t.Fatalf("in-place Put allocates %v times", n)
229+ }
230+ raw = loadEvent(t, "xproto/KeymapNotify")
231+ ev = decode(t, raw, nil)
232+ keys := xproto.KeymapNotifyOf(&ev).Keys()
233+ k := xproto.KeymapNotifyEventData{Keys: [31]uint8(keys)}
234+ k.Put(&ev.Raw)
235+ if ev.Raw != [32]byte(raw) || !bytes.Equal(keys, raw[1:]) {
236+ t.Fatalf("KeymapNotify in place %x", ev.Raw)
237+ }
238+}
239+
240+func TestEventCopyAliases(t *testing.T) {
241+ if xproto.KindButtonRelease != 5 || xproto.KindKeyRelease != 3 || xproto.KindLeaveNotify != 8 || xproto.KindFocusOut != 10 || xproto.KindCirculateRequest != 27 {
242+ t.Fatal("eventcopy kinds")
243+ }
244+ var raw [32]byte
245+ raw[0], raw[1] = 5, 3 // ButtonRelease, button 3
246+ binary.LittleEndian.PutUint16(raw[24:], 0x100)
247+ ev := decode(t, raw[:], nil)
248+ var r xproto.ButtonReleaseEvent = xproto.ButtonReleaseOf(&ev)
249+ var p xproto.ButtonPressEvent = r // alias: same type
250+ if p.Detail() != 3 || r.EventX() != 0x100 {
251+ t.Fatal("fields")
252+ }
253+ d := xproto.ButtonReleaseEventData{Detail: 3, EventX: 0x100}
254+ var out [32]byte
255+ d.Put(&out)
256+ raw[2], raw[3] = 0, 0
257+ if out != raw {
258+ t.Fatalf("Put %x", out)
259+ }
260+}
261+
262+func TestShapeEvent(t *testing.T) {
263+ var raw [32]byte
264+ raw[0], raw[1] = 64, 2 // first event 64, SK Input
265+ binary.LittleEndian.PutUint32(raw[4:], 0x400001)
266+ binary.LittleEndian.PutUint16(raw[8:], 0xfffe)
267+ raw[20] = 1 // shaped: after window(4) extents(8) server_time(4)
268+ ev := decode(t, raw[:], goldenExt())
269+ if ev.Kind != shape.KindNotify || ev.Ext != xturbo.ExtShape {
270+ t.Fatalf("kind %x", ev.Kind)
271+ }
272+ e := shape.NotifyOf(&ev)
273+ if e.ShapeKind() != shape.SkInput || e.AffectedWindow() != 0x400001 || e.ExtentsX() != -2 || !e.Shaped() {
274+ t.Fatal("fields")
275+ }
276+}
277+
278+func TestDecodeEventRejects(t *testing.T) {
279+ raw := make([]byte, 64)
280+ for _, code := range []byte{0, 1, 0x80, 0x81} {
281+ raw[0] = code
282+ if _, _, ok := xturbo.DecodeEvent(raw, nil); ok {
283+ t.Fatalf("code %#x accepted", code)
284+ }
285+ }
286+ raw[0] = 2
287+ if _, _, ok := xturbo.DecodeEvent(raw[:31], nil); ok {
288+ t.Fatal("short event accepted")
289+ }
290+ raw[0] = 35
291+ binary.LittleEndian.PutUint32(raw[4:], 9) // 36 tail bytes > 32 available
292+ if _, _, ok := xturbo.DecodeEvent(raw, nil); ok {
293+ t.Fatal("truncated XGE accepted")
294+ }
295+ binary.LittleEndian.PutUint32(raw[4:], 0xffffffff)
296+ if _, _, ok := xturbo.DecodeEvent(raw, nil); ok {
297+ t.Fatal("huge XGE length accepted")
298+ }
299+ raw[0] = 100 // extension range, nothing negotiated
300+ if ev, n, ok := xturbo.DecodeEvent(raw, goldenExt()); !ok || n != 32 || ev.Kind != xturbo.KindUnknown {
301+ t.Fatal("unknown extension event")
302+ }
303+ raw[0] = 40 // core range gap 36..63
304+ if ev, _, ok := xturbo.DecodeEvent(raw, goldenExt()); !ok || ev.Kind != xturbo.KindUnknown {
305+ t.Fatal("unassigned core code")
306+ }
307+ raw[0] = 35 // XGE evtype >= 128 is never a known kind
308+ binary.LittleEndian.PutUint32(raw[4:], 0)
309+ raw[1] = geMajor
310+ binary.LittleEndian.PutUint16(raw[8:], 0x80)
311+ if ev, _, ok := xturbo.DecodeEvent(raw, goldenExt()); !ok || ev.Kind != xturbo.KindUnknown {
312+ t.Fatal("XGE evtype >= 128")
313+ }
314+}
315+
316+func TestExtTableReverseMaps(t *testing.T) {
317+ ext := goldenExt()
318+ if id, rel, ok := ext.ByEvent(xfixesEvent + 1); !ok || id != xturbo.ExtXFixes || rel != 1 {
319+ t.Fatal("ByEvent CursorNotify")
320+ }
321+ if _, _, ok := ext.ByEvent(xfixesEvent + 2); ok {
322+ t.Fatal("XFIXES declares two events")
323+ }
324+ if id, rel, ok := ext.ByError(xfixesError); !ok || id != xturbo.ExtXFixes || rel != xfixes.BadRegion {
325+ t.Fatal("ByError")
326+ }
327+ if id, ok := ext.ByMajor(xfixesMajor); !ok || id != xturbo.ExtXFixes {
328+ t.Fatal("ByMajor")
329+ }
330+ ext.Set(xturbo.ExtXFixes, 140, 90, 150) // re-negotiation drops stale entries
331+ if _, ok := ext.ByMajor(xfixesMajor); ok {
332+ t.Fatal("stale major")
333+ }
334+ if _, _, ok := ext.ByEvent(xfixesEvent); ok {
335+ t.Fatal("stale event")
336+ }
337+ if id, _, ok := ext.ByError(150); !ok || id != xturbo.ExtXFixes {
338+ t.Fatal("new error base")
339+ }
340+ var render xturbo.ExtTable
341+ render.Set(xturbo.ExtRender, 139, 0, 142)
342+ for code := uint8(142); code < 147; code++ {
343+ if id, rel, ok := render.ByError(code); !ok || id != xturbo.ExtRender || render.Info(id).FirstError+rel != code {
344+ t.Fatalf("render error %d", code)
345+ }
346+ }
347+ if _, _, ok := render.ByError(147); ok {
348+ t.Fatal("RENDER declares five errors")
349+ }
350+}
351+
352+func TestErrorNames(t *testing.T) {
353+ if xproto.BadWindow != 3 || xproto.BadAtom != 5 || xproto.BadImplementation != 17 ||
354+ xproto.ErrorName(xproto.BadWindow) != "BadWindow" || xproto.ErrorName(0) != "" || xproto.ErrorName(200) != "" {
355+ t.Fatal("xproto errors")
356+ }
357+ if xfixes.BadRegion != 0 || xfixes.ErrorName(0) != "BadRegion" {
358+ t.Fatal("xfixes errors")
359+ }
360+ for code, name := range []string{"BadPictFormat", "BadPicture", "BadPictOp", "BadGlyphSet", "BadGlyph"} {
361+ if render.ErrorName(uint8(code)) != name {
362+ t.Fatalf("render %d", code)
363+ }
364+ }
365+}
366+
367+// mixedEvents is 10k events: core, xfixes, XGE with a tail and unknown codes.
368+func mixedEvents(t testing.TB) []byte {
369+ var golden [][]byte
370+ for _, name := range []string{"xproto/MapRequest", "xproto/ConfigureRequest", "xproto/PropertyNotify", "xproto/ClientMessage",
371+ "xproto/KeymapNotify", "xfixes/SelectionNotify", "xproto/GeGeneric"} {
372+ golden = append(golden, loadEvent(t, name))
373+ }
374+ unknown := make([]byte, 32)
375+ unknown[0] = 100
376+ golden = append(golden, unknown)
377+ var stream []byte
378+ for i := range 10000 {
379+ stream = append(stream, golden[i%len(golden)]...)
380+ }
381+ return stream
382+}
383+
384+// eventSink touches the per-kind view getters a window manager reads.
385+func eventSink(ev *xturbo.Event) uint64 {
386+ switch ev.Kind {
387+ case xproto.KindMapRequest:
388+ e := xproto.MapRequestOf(ev)
389+ return uint64(e.Window()) + uint64(e.Parent())
390+ case xproto.KindConfigureRequest:
391+ e := xproto.ConfigureRequestOf(ev)
392+ return uint64(e.Window()) + uint64(e.Width()) + uint64(e.ValueMask()) + uint64(int64(e.X()))
393+ case xproto.KindPropertyNotify:
394+ e := xproto.PropertyNotifyOf(ev)
395+ return uint64(e.Atom()) + uint64(e.State())
396+ case xproto.KindClientMessage:
397+ e := xproto.ClientMessageOf(ev)
398+ return uint64(e.Type()) + uint64(e.Data().Data32()[0])
399+ case xproto.KindKeymapNotify:
400+ return uint64(len(xproto.KeymapNotifyOf(ev).Keys()))
401+ case xfixes.KindSelectionNotify:
402+ e := xfixes.SelectionNotifyOf(ev)
403+ return uint64(e.Owner()) + uint64(e.Selection())
404+ case xturbo.EventKind(xturbo.ExtGenericEvent)<<8 | 0x80 | 1:
405+ if g, ok := xproto.GeGenericOf(ev); ok {
406+ return uint64(len(g.Tail()))
407+ }
408+ }
409+ return 0
410+}
411+
412+func decodeAll(t testing.TB, stream []byte, ext *xturbo.ExtTable) (sum uint64) {
413+ for len(stream) > 0 {
414+ ev, n, ok := xturbo.DecodeEvent(stream, ext)
415+ if !ok {
416+ t.Fatal("decode")
417+ }
418+ sum += eventSink(&ev)
419+ stream = stream[n:]
420+ }
421+ return sum
422+}
423+
424+func TestAllocEvents(t *testing.T) {
425+ stream := mixedEvents(t)
426+ ext := goldenExt()
427+ var sink uint64
428+ if n := testing.AllocsPerRun(10, func() { sink += decodeAll(t, stream, ext) }); n != 0 {
429+ t.Fatalf("DecodeEvent x10k + views allocate %v times", n)
430+ }
431+ cm := xproto.ClientMessageEventData{Format: 32, Window: 1, Type: 2}
432+ cfg := xproto.ConfigureNotifyEventData{Window: 1, Width: 640, Height: 480}
433+ var out [32]byte
434+ if n := testing.AllocsPerRun(1000, func() { cm.Put(&out); cfg.Put(&out) }); n != 0 {
435+ t.Fatalf("EventData.Put allocates %v times", n)
436+ }
437+ b := xturbo.NewBuf(0)
438+ if n := testing.AllocsPerRun(1000, func() {
439+ b.Reset()
440+ cm.Put(&out)
441+ xproto.SendEvent(b, false, 1, xproto.EventMaskSubstructureRedirect, &out)
442+ }); n != 0 {
443+ t.Fatalf("SendEvent allocates %v times", n)
444+ }
445+ _ = sink
446+}
447+
448+func BenchmarkDecodeEvents10k(b *testing.B) {
449+ stream := mixedEvents(b)
450+ ext := goldenExt()
451+ var sink uint64
452+ b.SetBytes(int64(len(stream)))
453+ b.ReportAllocs()
454+ for b.Loop() {
455+ sink += decodeAll(b, stream, ext)
456+ }
457+ _ = sink
458+}
459+
460+// FuzzDecodeEvent: no panic; n is 32 or the XGE length; Raw copies the first
461+// 32 bytes; Tail stays within raw; Kind is consistent with Ext. extSeed picks
462+// which extensions are negotiated and where their bases lie.
463+func FuzzDecodeEvent(f *testing.F) {
464+ for _, name := range []string{"xproto/MapRequest", "xproto/ClientMessage", "xproto/KeymapNotify", "xfixes/SelectionNotify", "xproto/GeGeneric"} {
465+ f.Add(loadEvent(f, name), uint32(0x8a578c83))
466+ }
467+ f.Add(make([]byte, 32), uint32(0))
468+ f.Fuzz(func(t *testing.T, raw []byte, extSeed uint32) {
469+ var ext xturbo.ExtTable
470+ ext.Set(xturbo.ExtXFixes, uint8(extSeed>>24), uint8(extSeed>>16), uint8(extSeed>>8))
471+ ext.Set(xturbo.ExtGenericEvent, uint8(extSeed), 0, 0)
472+ ext.Set(xturbo.ExtShape, uint8(extSeed>>4), uint8(extSeed>>12)|64, 0)
473+ table := &ext
474+ if extSeed&1 != 0 {
475+ table = nil
476+ }
477+ ev, n, ok := xturbo.DecodeEvent(raw, table)
478+ if !ok {
479+ if n != 0 {
480+ t.Fatal("n on failure")
481+ }
482+ return
483+ }
484+ if len(raw) < 32 || raw[0]&0x7f < 2 || n > len(raw) || !bytes.Equal(ev.Raw[:], raw[:32]) || ev.SendEvent != (raw[0]&0x80 != 0) {
485+ t.Fatalf("n %d", n)
486+ }
487+ if raw[0]&0x7f == 35 {
488+ if n != 32+4*int(binary.LittleEndian.Uint32(raw[4:])) || len(ev.Tail) != n-32 {
489+ t.Fatal("XGE framing")
490+ }
491+ } else if n != 32 || ev.Tail != nil {
492+ t.Fatal("fixed framing")
493+ }
494+ if ev.Kind != xturbo.KindUnknown && ev.Kind>>8 != xturbo.EventKind(ev.Ext) {
495+ t.Fatal("Kind/Ext mismatch")
496+ }
497+ _ = eventSink(&ev)
498+ })
499+}
M ext.go +66 -4
@@ -7,12 +7,47 @@ type ExtInfo struct {
7 }7 }
8 8
9 // ExtTable is populated during setup. Fixed arrays avoid lookup allocations.9 // ExtTable is populated during setup. Fixed arrays avoid lookup allocations.
10-// Reverse mapping is completed with the read-side runtime in Phase 3.10+// Reverse maps store ExtID+1 so the zero value means "none".
11-type ExtTable struct{ info [extCount]ExtInfo }11+type ExtTable struct {
12+ info [extCount]ExtInfo
13+ byEvent [128]uint8
14+ byError [256]uint8
15+ byMajor [256]uint8
16+}
12 17
18+// Set records an extension's negotiated opcodes and fills the reverse maps
19+// for the event and error codes the extension declares in the pinned XML.
13 func (t *ExtTable) Set(id ExtID, major, firstEvent, firstError uint8) {20 func (t *ExtTable) Set(id ExtID, major, firstEvent, firstError uint8) {
14- if id < extCount {21+ if id == ExtCore || id >= extCount {
15- t.info[id] = ExtInfo{true, major, firstEvent, firstError}22+ return
23+ }
24+ tag := uint8(id) + 1
25+ for i := range t.byEvent {
26+ if t.byEvent[i] == tag {
27+ t.byEvent[i] = 0
28+ }
29+ }
30+ for i := range t.byError {
31+ if t.byError[i] == tag {
32+ t.byError[i] = 0
33+ }
34+ if t.byMajor[i] == tag {
35+ t.byMajor[i] = 0
36+ }
37+ }
38+ t.info[id] = ExtInfo{true, major, firstEvent, firstError}
39+ if major != 0 {
40+ t.byMajor[major] = tag
41+ }
42+ if firstEvent != 0 {
43+ for i := 0; i < int(extEvents[id]) && int(firstEvent)+i < len(t.byEvent); i++ {
44+ t.byEvent[int(firstEvent)+i] = tag
45+ }
46+ }
47+ if firstError != 0 {
48+ for i := 0; i < int(extErrors[id]) && int(firstError)+i < len(t.byError); i++ {
49+ t.byError[int(firstError)+i] = tag
50+ }
16 }51 }
17 }52 }
18 func (t *ExtTable) Info(id ExtID) ExtInfo {53 func (t *ExtTable) Info(id ExtID) ExtInfo {
@@ -25,3 +60,30 @@ func (t *ExtTable) Info(id ExtID) ExtInfo {
25 return t.info[id]60 return t.info[id]
26 }61 }
27 func (t *ExtTable) Major(id ExtID) uint8 { return t.Info(id).Major }62 func (t *ExtTable) Major(id ExtID) uint8 { return t.Info(id).Major }
63+
64+// ByEvent maps an event code (raw[0]&0x7f) to its extension and the code
65+// relative to the extension's first event.
66+func (t *ExtTable) ByEvent(code uint8) (id ExtID, rel uint8, ok bool) {
67+ if code >= 128 || t.byEvent[code] == 0 {
68+ return ExtCore, 0, false
69+ }
70+ id = ExtID(t.byEvent[code] - 1)
71+ return id, code - t.info[id].FirstEvent, true
72+}
73+
74+// ByError maps a wire error code to its extension and relative error code.
75+func (t *ExtTable) ByError(code uint8) (id ExtID, rel uint8, ok bool) {
76+ if t.byError[code] == 0 {
77+ return ExtCore, 0, false
78+ }
79+ id = ExtID(t.byError[code] - 1)
80+ return id, code - t.info[id].FirstError, true
81+}
82+
83+// ByMajor maps a major opcode to its extension.
84+func (t *ExtTable) ByMajor(major uint8) (id ExtID, ok bool) {
85+ if t.byMajor[major] == 0 {
86+ return ExtCore, false
87+ }
88+ return ExtID(t.byMajor[major] - 1), true
89+}
M ext_generated.go +5 -0
@@ -17,3 +17,8 @@ const (
17 )17 )
18 18
19 var ExtXName = [extCount]string{"", "BIG-REQUESTS", "Generic Event Extension", "XC-MISC", "Composite", "XFIXES", "X-Resource", "RENDER", "SHAPE"}19 var ExtXName = [extCount]string{"", "BIG-REQUESTS", "Generic Event Extension", "XC-MISC", "Composite", "XFIXES", "X-Resource", "RENDER", "SHAPE"}
20+
21+// extEvents and extErrors are the event and error code ranges each extension
22+// declares in the pinned XML; ExtTable.Set uses them to fill reverse maps.
23+var extEvents = [extCount]uint8{0, 0, 0, 0, 0, 2, 0, 0, 1}
24+var extErrors = [extCount]uint8{0, 0, 0, 0, 0, 1, 0, 5, 0}
M internal/xtest/README.md +33 -1
@@ -1,6 +1,27 @@
1 # Wire goldens1 # Wire goldens
2 2
3-Refresh the request fixtures from the repository root:3+`make xserver` captures from a fresh Xvfb and compares every packet with the
4+committed golden. Comparison is independent of the server build:
5+
6+- Server-chosen values (root window, visual, colormap, interned atom,
7+ resource IDs from the client's resource base, extension opcodes and
8+ event/error bases) are listed per golden by field offset in
9+ `serverFields`. `-update` records their capture-time values in
10+ `testdata/golden/xvfb-roles.json`. Compare mode checks that the golden holds
11+ the recorded value and the live packet holds the live server's value, masks
12+ both, then compares the remaining bytes exactly. Server timestamps are
13+ masked without a value check.
14+- `ListExtensions` and Render `QueryPictFormats` replies depend on how the
15+ server was built (extension list, visuals, formats), so live compare
16+ checks invariants: framing, counts matching list lengths, the bound
17+ extensions present, the root visual mapped to the format CreatePicture uses.
18+
19+Unit tests still decode the committed goldens byte-exactly. The test is
20+skipped when Xvfb is not installed. `XTURBO_XVFB_ARGS` adds Xvfb options to
21+simulate another server layout, e.g. `XTURBO_XVFB_ARGS="-extension GLX"`.
22+Screen size and depth (800x600x24) and the bound extensions are capture
23+inputs and must stay as captured. Rewrite the goldens from the repository
24+root with:
4 25
5 ```26 ```
6 PATH="$PATH:/usr/sbin" go test -tags xserver ./internal/xtest -update27 PATH="$PATH:/usr/sbin" go test -tags xserver ./internal/xtest -update
@@ -38,3 +59,14 @@ adds GetWindowAttributes (child window), QueryTree (root), an unset WM_NAME
38 GetProperty (zero-length value) and ListExtensions (variable STR list). These59 GetProperty (zero-length value) and ListExtensions (variable STR list). These
39 reply-only requests use opcodes outside the request-golden classifier.60 reply-only requests use opcodes outside the request-golden classifier.
40 Generated `FuzzParse*Reply` targets seed from every matching reply golden.61 Generated `FuzzParse*Reply` targets seed from every matching reply golden.
62+
63+T2.7 adds event and error goldens (`<Name>.event.hex`, `<Name>.error.hex`).
64+The tee client selects SubstructureRedirect/Notify and PropertyChange on the
65+root plus XFIXES selection input; a second raw client (not teed) creates, maps,
66+configures, unmaps and destroys a window, writes a root property, sends a
67+format-32 ClientMessage and takes PRIMARY. ConvertSelection of an unowned
68+SECONDARY yields a core SelectionNotify. GetAtomName, GetWindowAttributes and
69+XFIXES DestroyRegion on bad IDs yield BadAtom, BadWindow and BadRegion. Each
70+batch is fenced by a GetInputFocus reply; unexpected packets fail capture.
71+`KeymapNotify` and `GeGeneric` (XGE framing with a tail) are hand-written
72+(`# hand:`): Xvfb produces neither for these clients.
M internal/xtest/capture_test.go +453 -18
@@ -6,13 +6,16 @@ import (
6 "bytes"6 "bytes"
7 "encoding/binary"7 "encoding/binary"
8 "encoding/hex"8 "encoding/hex"
9+ "encoding/json"
9 "flag"10 "flag"
10 "fmt"11 "fmt"
11 "io"12 "io"
13+ "maps"
12 "net"14 "net"
13 "os"15 "os"
14 "os/exec"16 "os/exec"
15 "path/filepath"17 "path/filepath"
18+ "slices"
16 "strconv"19 "strconv"
17 "strings"20 "strings"
18 "testing"21 "testing"
@@ -21,11 +24,77 @@ import (
21 "golang.org/x/sys/unix"24 "golang.org/x/sys/unix"
22 )25 )
23 26
24-var update = flag.Bool("update", false, "refresh request goldens using Xvfb and a raw socketpair tee")27+var update = flag.Bool("update", false, "rewrite wire goldens from Xvfb instead of comparing against them")
25 28
29+// field is a server-dependent value inside a golden: the size-byte
30+// little-endian integer at Off must equal the server's value for Role.
31+type field struct {
32+ Off, Size int
33+ Role string
34+}
35+
36+// serverFields lists, per golden, every value the X server chooses: resource
37+// IDs derived from a server-assigned base, the root window and its visual and
38+// colormap, interned atoms, extension opcodes and bases, and timestamps.
39+// Compare mode checks that the golden holds the value recorded at capture
40+// (xvfb-roles.json) and the live packet holds the live server's value, then
41+// masks both and compares the remaining bytes exactly. Role "time" is a
42+// per-run server timestamp and is masked without a value check.
43+var serverFields = map[string][]field{
44+ "xproto/MapWindow.req": {{4, 4, "root"}},
45+ "xproto/GetGeometry.req": {{4, 4, "root"}},
46+ "xproto/ChangeProperty.req": {{4, 4, "root"}, {8, 4, "atom"}},
47+ "xproto/CreateWindow.req": {{4, 4, "child"}, {8, 4, "root"}},
48+ "xproto/ChangeWindowAttributes.req": {{4, 4, "child"}},
49+ "xproto/ConfigureWindow.req": {{4, 4, "child"}},
50+ "xproto/ConfigureWindowSparse.req": {{4, 4, "child"}},
51+ "xproto/ConfigureWindowAll.req": {{4, 4, "child"}, {32, 4, "sibling"}},
52+ "render/CreatePicture.req": {{0, 1, "renderMajor"}, {4, 4, "picture"}, {8, 4, "child"}, {12, 4, "rootFormat"}},
53+ "res/QueryClientIds.req": {{0, 1, "resMajor"}},
54+ "xfixes/QueryVersion.req": {{0, 1, "xfixesMajor"}},
55+ "xproto/QueryExtension.reply": {{9, 1, "xfixesMajor"}, {10, 1, "xfixesEvent"}, {11, 1, "xfixesError"}},
56+ "xproto/GetGeometry.reply": {{8, 4, "root"}},
57+ "xproto/InternAtom.reply": {{8, 4, "atom"}},
58+ "xproto/GetWindowAttributes.reply": {{8, 4, "rootVisual"}, {28, 4, "rootColormap"}},
59+ "xproto/QueryTree.reply": {{8, 4, "root"}, {32, 4, "sibling"}, {36, 4, "child"}},
60+ "res/QueryClientIds.reply": {{44, 4, "base"}},
61+ "xproto/CreateNotify.event": {{4, 4, "root"}, {8, 4, "client2"}},
62+ "xproto/MapRequest.event": {{4, 4, "root"}, {8, 4, "client2"}},
63+ "xproto/ConfigureRequest.event": {{4, 4, "root"}, {8, 4, "client2"}},
64+ "xproto/MapNotify.event": {{4, 4, "root"}, {8, 4, "client2"}},
65+ "xproto/UnmapNotify.event": {{4, 4, "root"}, {8, 4, "client2"}},
66+ "xproto/DestroyNotify.event": {{4, 4, "root"}, {8, 4, "client2"}},
67+ "xproto/PropertyNotify.event": {{4, 4, "root"}, {8, 4, "atom"}, {12, 4, "time"}},
68+ "xproto/ClientMessage.event": {{4, 4, "client2"}, {8, 4, "atom"}},
69+ "xproto/SelectionNotify.event": {{8, 4, "child"}},
70+ "xfixes/SelectionNotify.event": {{0, 1, "xfixesEvent"}, {4, 4, "root"}, {8, 4, "client2"}, {16, 4, "time"}, {20, 4, "time"}},
71+ "xproto/BadWindow.error": {{4, 4, "badWindow"}},
72+ "xfixes/BadRegion.error": {{1, 1, "xfixesError"}, {4, 4, "badRegion"}, {10, 1, "xfixesMajor"}},
73+}
74+
75+// structural replies depend on the server build itself (extension list,
76+// visuals and picture formats), so live compare checks invariants instead of
77+// bytes; unit tests still decode the committed golden byte-exactly.
78+var structural = map[string]func(p []byte, roles map[string]uint32) error{
79+ "xproto/ListExtensions.reply": checkListExtensions,
80+ "render/QueryPictFormats.reply": checkQueryPictFormats,
81+}
82+
83+const rolesPath = "../../testdata/golden/xvfb-roles.json"
84+
85+// xvfbArgs lets a run simulate a different server layout, e.g.
86+// XTURBO_XVFB_ARGS="-extension GLX" shifts resource IDs, atoms and visuals.
87+func xvfbArgs() []string {
88+ args := []string{"-displayfd", "3", "-screen", "0", "800x600x24", "-nolisten", "tcp", "-ac"}
89+ return append(args, strings.Fields(os.Getenv("XTURBO_XVFB_ARGS"))...)
90+}
91+
92+// TestCaptureRequests drives a fresh Xvfb through a raw socketpair tee. By
93+// default it compares every captured packet with the committed golden and
94+// fails on a difference; -update rewrites the goldens instead.
26 func TestCaptureRequests(t *testing.T) {95 func TestCaptureRequests(t *testing.T) {
27- if !*update {96+ if _, err := exec.LookPath("Xvfb"); err != nil {
28- t.Skip("pass -update to capture request goldens")97+ t.Skip("Xvfb not installed")
29 }98 }
30 // displayfd avoids display-number races and tells us when Xvfb is ready.99 // displayfd avoids display-number races and tells us when Xvfb is ready.
31 r, w, err := os.Pipe()100 r, w, err := os.Pipe()
@@ -33,7 +102,7 @@ func TestCaptureRequests(t *testing.T) {
33 t.Fatal(err)102 t.Fatal(err)
34 }103 }
35 defer r.Close()104 defer r.Close()
36- cmd := exec.Command("Xvfb", "-displayfd", "3", "-screen", "0", "800x600x24", "-nolisten", "tcp", "-ac")105+ cmd := exec.Command("Xvfb", xvfbArgs()...)
37 cmd.ExtraFiles = []*os.File{w}106 cmd.ExtraFiles = []*os.File{w}
38 var log bytes.Buffer107 var log bytes.Buffer
39 cmd.Stderr = &log108 cmd.Stderr = &log
@@ -327,6 +396,181 @@ func TestCaptureRequests(t *testing.T) {
327 keep("xproto/GetPropertyEmpty", packet())396 keep("xproto/GetPropertyEmpty", packet())
328 write([]byte{99, 0, 1, 0}) // ListExtensions: variable STR iterator397 write([]byte{99, 0, 1, 0}) // ListExtensions: variable STR iterator
329 keep("xproto/ListExtensions", packet())398 keep("xproto/ListExtensions", packet())
399+ // Event and error captures. The tee client acts as a window manager on the
400+ // root; a second raw client (direct socket, not teed) triggers the events.
401+ // Requests sent here on the tee use window/minor values the request-golden
402+ // classifier below ignores.
403+ xfixesEvent, xfixesError := ext[10], ext[11]
404+ events := map[string][]byte{}
405+ xerrors := map[string][]byte{}
406+ // collect reads events and errors until the reply fencing them.
407+ collect := func(names map[byte]string) {
408+ t.Helper()
409+ write([]byte{43, 0, 1, 0}) // GetInputFocus
410+ for {
411+ p := packet()
412+ switch {
413+ case p[0] == 1:
414+ return
415+ case p[0] == 0:
416+ name := names[p[1]]
417+ if name == "" {
418+ t.Fatalf("unexpected error %x", p)
419+ }
420+ xerrors[name] = p
421+ default:
422+ name := names[p[0]]
423+ if name == "" {
424+ t.Fatalf("unexpected event %x", p)
425+ }
426+ events[name] = p
427+ }
428+ }
429+ }
430+ const (
431+ maskSubstructureNotify = 1 << 19
432+ maskSubstructureRedirect = 1 << 20
433+ maskPropertyChange = 1 << 22
434+ atomPrimary, atomSecondary, atomString = 1, 2, 31
435+ )
436+ selectRoot := make([]byte, 16)
437+ selectRoot[0], selectRoot[2] = 2, 4
438+ binary.LittleEndian.PutUint32(selectRoot[4:], root)
439+ binary.LittleEndian.PutUint32(selectRoot[8:], 1<<11) // CWEventMask
440+ binary.LittleEndian.PutUint32(selectRoot[12:], maskSubstructureNotify|maskSubstructureRedirect|maskPropertyChange)
441+ write(selectRoot)
442+ selectSel := make([]byte, 16)
443+ selectSel[0], selectSel[1], selectSel[2] = major, 2, 4
444+ binary.LittleEndian.PutUint32(selectSel[4:], root)
445+ binary.LittleEndian.PutUint32(selectSel[8:], atomPrimary)
446+ binary.LittleEndian.PutUint32(selectSel[12:], 1) // SetSelectionOwner
447+ write(selectSel)
448+ collect(nil)
449+
450+ other, err := net.DialTimeout("unix", fmt.Sprintf("/tmp/.X11-unix/X%d", num), 5*time.Second)
451+ if err != nil {
452+ t.Fatal(err)
453+ }
454+ defer other.Close()
455+ _ = other.SetDeadline(time.Now().Add(5 * time.Second))
456+ send := func(p []byte) {
457+ t.Helper()
458+ if _, err := other.Write(p); err != nil {
459+ t.Fatal(err)
460+ }
461+ }
462+ recv := func(p []byte) {
463+ t.Helper()
464+ if _, err := io.ReadFull(other, p); err != nil {
465+ t.Fatal(err)
466+ }
467+ }
468+ // sync round-trips the second client; any error fails the capture.
469+ sync := func() {
470+ t.Helper()
471+ send([]byte{43, 0, 1, 0})
472+ var p [32]byte
473+ recv(p[:])
474+ if p[0] != 1 {
475+ t.Fatalf("second client: %x", p)
476+ }
477+ }
478+ send([]byte{'l', 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0})
479+ var otherHead [8]byte
480+ recv(otherHead[:])
481+ if otherHead[0] != 1 {
482+ t.Fatalf("second setup failed: %x", otherHead)
483+ }
484+ otherSetup := make([]byte, int(binary.LittleEndian.Uint16(otherHead[6:]))*4)
485+ recv(otherSetup)
486+ client2 := binary.LittleEndian.Uint32(otherSetup[4:]) | 1
487+ win := make([]byte, 32)
488+ win[0], win[2] = 1, 8
489+ binary.LittleEndian.PutUint32(win[4:], client2)
490+ binary.LittleEndian.PutUint32(win[8:], root)
491+ binary.LittleEndian.PutUint16(win[12:], 10)
492+ binary.LittleEndian.PutUint16(win[14:], 20)
493+ binary.LittleEndian.PutUint16(win[16:], 300)
494+ binary.LittleEndian.PutUint16(win[18:], 200)
495+ binary.LittleEndian.PutUint16(win[22:], 1)
496+ send(win) // CreateNotify
497+ mapOther := make([]byte, 8)
498+ mapOther[0], mapOther[2] = 8, 2
499+ binary.LittleEndian.PutUint32(mapOther[4:], client2)
500+ send(mapOther) // MapRequest (redirected)
501+ cfg := make([]byte, 24)
502+ cfg[0], cfg[2] = 12, 6
503+ binary.LittleEndian.PutUint32(cfg[4:], client2)
504+ binary.LittleEndian.PutUint16(cfg[8:], 1|2|4) // x, y, width
505+ binary.LittleEndian.PutUint32(cfg[12:], 0xfffffffb)
506+ binary.LittleEndian.PutUint32(cfg[16:], 40)
507+ binary.LittleEndian.PutUint32(cfg[20:], 640)
508+ send(cfg) // ConfigureRequest (redirected)
509+ prop := make([]byte, 28)
510+ prop[0], prop[2] = 18, 7
511+ binary.LittleEndian.PutUint32(prop[4:], root)
512+ binary.LittleEndian.PutUint32(prop[8:], atom)
513+ binary.LittleEndian.PutUint32(prop[12:], atomString)
514+ prop[16] = 8
515+ binary.LittleEndian.PutUint32(prop[20:], 2)
516+ copy(prop[24:], "hi")
517+ send(prop) // PropertyNotify (NewValue)
518+ msg := make([]byte, 44)
519+ msg[0], msg[2] = 25, 11
520+ binary.LittleEndian.PutUint32(msg[4:], root)
521+ binary.LittleEndian.PutUint32(msg[8:], maskSubstructureNotify|maskSubstructureRedirect)
522+ msg[12], msg[13] = 33, 32 // ClientMessage, format 32
523+ binary.LittleEndian.PutUint32(msg[16:], client2)
524+ binary.LittleEndian.PutUint32(msg[20:], atom)
525+ for i := range 5 {
526+ binary.LittleEndian.PutUint32(msg[24+4*i:], uint32(0x01020304*(i+1)))
527+ }
528+ send(msg) // ClientMessage (SendEvent bit set)
529+ owner := make([]byte, 16)
530+ owner[0], owner[2] = 22, 4
531+ binary.LittleEndian.PutUint32(owner[4:], client2)
532+ binary.LittleEndian.PutUint32(owner[8:], atomPrimary)
533+ send(owner) // xfixes SelectionNotify (SetSelectionOwner)
534+ sync()
535+ collect(map[byte]string{16: "xproto/CreateNotify", 20: "xproto/MapRequest", 23: "xproto/ConfigureRequest",
536+ 28: "xproto/PropertyNotify", 0x80 | 33: "xproto/ClientMessage", xfixesEvent: "xfixes/SelectionNotify"})
537+ // The window manager maps the window itself (MapNotify); the second client
538+ // then unmaps and destroys it.
539+ mapByWM := make([]byte, 8)
540+ mapByWM[0], mapByWM[2] = 8, 2
541+ binary.LittleEndian.PutUint32(mapByWM[4:], client2)
542+ write(mapByWM)
543+ collect(map[byte]string{19: "xproto/MapNotify"})
544+ unmap := []byte{10, 0, 2, 0, 0, 0, 0, 0}
545+ binary.LittleEndian.PutUint32(unmap[4:], client2)
546+ send(unmap)
547+ destroy := []byte{4, 0, 2, 0, 0, 0, 0, 0}
548+ binary.LittleEndian.PutUint32(destroy[4:], client2)
549+ send(destroy)
550+ sync()
551+ collect(map[byte]string{18: "xproto/UnmapNotify", 17: "xproto/DestroyNotify"})
552+ // Core SelectionNotify: SECONDARY has no owner, so the server replies itself.
553+ convert := make([]byte, 24)
554+ convert[0], convert[2] = 24, 6
555+ binary.LittleEndian.PutUint32(convert[4:], child)
556+ binary.LittleEndian.PutUint32(convert[8:], atomSecondary)
557+ binary.LittleEndian.PutUint32(convert[12:], atomString)
558+ binary.LittleEndian.PutUint32(convert[16:], atom)
559+ write(convert)
560+ collect(map[byte]string{31: "xproto/SelectionNotify"})
561+ // Errors: BadAtom, BadWindow and an extension error (XFIXES BadRegion).
562+ badAtom := []byte{17, 0, 2, 0, 0xef, 0xbe, 0xad, 0x0d}
563+ write(badAtom)
564+ badWindow := []byte{3, 0, 2, 0, 0, 0, 0, 0}
565+ binary.LittleEndian.PutUint32(badWindow[4:], base|0x7777)
566+ write(badWindow)
567+ badRegion := []byte{major, 10, 2, 0, 0, 0, 0, 0}
568+ binary.LittleEndian.PutUint32(badRegion[4:], base|0x8888)
569+ write(badRegion)
570+ collect(map[byte]string{5: "xproto/BadAtom", 3: "xproto/BadWindow", xfixesError: "xfixes/BadRegion"})
571+ if len(events) != 10 || len(xerrors) != 3 {
572+ t.Fatalf("captured %d events, %d errors", len(events), len(xerrors))
573+ }
330 // Closing the client drains the request direction; wait before inspecting tee.574 // Closing the client drains the request direction; wait before inspecting tee.
331 client.Close()575 client.Close()
332 select {576 select {
@@ -353,13 +597,17 @@ func TestCaptureRequests(t *testing.T) {
353 captured["xproto/CreateWindow"] = p597 captured["xproto/CreateWindow"] = p
354 }598 }
355 case 2:599 case 2:
356- captured["xproto/ChangeWindowAttributes"] = p600+ if binary.LittleEndian.Uint32(p[4:]) == child {
601+ captured["xproto/ChangeWindowAttributes"] = p
602+ }
357 case 12:603 case 12:
358 names := []string{"ConfigureWindow", "ConfigureWindowSparse", "ConfigureWindowAll"}604 names := []string{"ConfigureWindow", "ConfigureWindowSparse", "ConfigureWindowAll"}
359 captured["xproto/"+names[configIndex]] = p605 captured["xproto/"+names[configIndex]] = p
360 configIndex++606 configIndex++
361 case 8:607 case 8:
362- captured["xproto/MapWindow"] = p608+ if binary.LittleEndian.Uint32(p[4:]) == root {
609+ captured["xproto/MapWindow"] = p
610+ }
363 case 14:611 case 14:
364 captured["xproto/GetGeometry"] = p612 captured["xproto/GetGeometry"] = p
365 case 16:613 case 16:
@@ -373,7 +621,7 @@ func TestCaptureRequests(t *testing.T) {
373 if p[0] == resMajor && p[1] == 4 {621 if p[0] == resMajor && p[1] == 4 {
374 captured["res/QueryClientIds"] = p622 captured["res/QueryClientIds"] = p
375 }623 }
376- if p[0] == major {624+ if p[0] == major && p[1] == 0 {
377 captured["xfixes/QueryVersion"] = p625 captured["xfixes/QueryVersion"] = p
378 }626 }
379 }627 }
@@ -382,24 +630,211 @@ func TestCaptureRequests(t *testing.T) {
382 if len(captured) != 12 || len(replies) != 11 {630 if len(captured) != 12 || len(replies) != 11 {
383 t.Fatal("missing captured requests or replies")631 t.Fatal("missing captured requests or replies")
384 }632 }
633+ out := map[string][]byte{}
385 for name, p := range replies {634 for name, p := range replies {
386- path := filepath.Join("../../testdata/golden", name+".reply.hex")635+ out[name+".reply"] = p
387- if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {636+ }
637+ for name, p := range captured {
638+ out[name+".req"] = p
639+ }
640+ for name, p := range events {
641+ out[name+".event"] = p
642+ }
643+ for name, p := range xerrors {
644+ out[name+".error"] = p
645+ }
646+ // Server-chosen values, keyed by the roles used in serverFields.
647+ roles := map[string]uint32{
648+ "root": root, "atom": atom, "base": base, "child": child, "sibling": sibling, "picture": picture,
649+ "client2": client2, "badWindow": base | 0x7777, "badRegion": base | 0x8888,
650+ "rootVisual": rootVisual, "rootColormap": binary.LittleEndian.Uint32(setup[screenOff+4:]), "rootFormat": format,
651+ "xfixesMajor": uint32(major), "xfixesEvent": uint32(xfixesEvent), "xfixesError": uint32(xfixesError),
652+ "resMajor": uint32(resMajor), "renderMajor": uint32(renderMajor),
653+ }
654+ if *update {
655+ for name, p := range out {
656+ writeGolden(t, name, p)
657+ }
658+ rolesJSON, err := json.MarshalIndent(roles, "", " ")
659+ if err != nil {
388 t.Fatal(err)660 t.Fatal(err)
389 }661 }
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"662+ if err := os.WriteFile(rolesPath, append(rolesJSON, '\n'), 0644); err != nil {
391- if err := os.WriteFile(path, []byte(text), 0644); err != nil {
392 t.Fatal(err)663 t.Fatal(err)
393 }664 }
665+ return
394 }666 }
395- for name, p := range captured {667+ rolesJSON, err := os.ReadFile(rolesPath)
396- path := filepath.Join("../../testdata/golden", name+".req.hex")668+ if err != nil {
397- if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {669+ t.Fatal(err)
398- t.Fatal(err)670+ }
671+ var capturedRoles map[string]uint32
672+ if err := json.Unmarshal(rolesJSON, &capturedRoles); err != nil {
673+ t.Fatal(err)
674+ }
675+ for _, role := range slices.Sorted(maps.Keys(roles)) {
676+ if roles[role] != capturedRoles[role] {
677+ t.Logf("server-chosen %s: live %#x, golden %#x", role, roles[role], capturedRoles[role])
399 }678 }
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"679+ }
401- if err := os.WriteFile(path, []byte(text), 0644); err != nil {680+ for name, p := range out {
402- t.Fatal(err)681+ compareGolden(t, name, p, roles, capturedRoles)
682+ }
683+}
684+
685+var sources = map[string]string{
686+ "req": "# source: Xvfb via raw socketpair tee; independent reference encoder; server accepted\n",
687+ "reply": "# source: Xvfb reply via raw socketpair tee to an independent reference request\n",
688+ "event": "# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request\n",
689+ "error": "# source: Xvfb error via raw socketpair tee to an independent reference request\n",
690+}
691+
692+func writeGolden(t *testing.T, name string, p []byte) {
693+ t.Helper()
694+ path := filepath.Join("../../testdata/golden", name+".hex")
695+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
696+ t.Fatal(err)
697+ }
698+ source := sources[name[strings.LastIndexByte(name, '.')+1:]]
699+ text := source + "# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace\n" + hex.EncodeToString(p) + "\n"
700+ if err := os.WriteFile(path, []byte(text), 0644); err != nil {
701+ t.Fatal(err)
702+ }
703+}
704+
705+// compareGolden checks a live packet against the committed golden without
706+// depending on the server build: server-chosen fields must hold each side's
707+// own value (live roles, recorded roles) and are masked before an exact
708+// comparison; structural replies are checked for invariants only.
709+func compareGolden(t *testing.T, name string, live []byte, roles, recorded map[string]uint32) {
710+ t.Helper()
711+ want, err := LoadHex(filepath.Join("../../testdata/golden", name+".hex"))
712+ if err != nil {
713+ t.Errorf("%s: %v (run with -update to create it)", name, err)
714+ return
715+ }
716+ if check := structural[name]; check != nil {
717+ if err := check(live, roles); err != nil {
718+ t.Errorf("%s (live): %v", name, err)
719+ }
720+ if err := check(want, recorded); err != nil {
721+ t.Errorf("%s (golden): %v", name, err)
722+ }
723+ return
724+ }
725+ got, exp, err := normalize(name, live, roles), normalize(name, want, recorded), error(nil)
726+ for _, e := range []struct {
727+ side string
728+ err error
729+ }{{"live", got.err}, {"golden", exp.err}} {
730+ if e.err != nil {
731+ t.Errorf("%s (%s): %v", name, e.side, e.err)
732+ err = e.err
733+ }
734+ }
735+ if err == nil && !bytes.Equal(got.b, exp.b) {
736+ t.Errorf("%s differs from live Xvfb after masking server-chosen fields:\n got %x\nwant %x", name, live, want)
737+ }
738+}
739+
740+type normalized struct {
741+ b []byte
742+ err error
743+}
744+
745+// normalize verifies each server-chosen field of p holds roles[Role] and
746+// replaces it with zero bytes.
747+func normalize(name string, p []byte, roles map[string]uint32) normalized {
748+ b := bytes.Clone(p)
749+ for _, f := range serverFields[name] {
750+ if f.Off+f.Size > len(b) {
751+ return normalized{err: fmt.Errorf("field %s at %d beyond %d bytes", f.Role, f.Off, len(b))}
752+ }
753+ if f.Role != "time" {
754+ v, ok := roles[f.Role]
755+ if !ok {
756+ return normalized{err: fmt.Errorf("no recorded value for role %s", f.Role)}
757+ }
758+ var got uint32
759+ if f.Size == 1 {
760+ got = uint32(b[f.Off])
761+ } else {
762+ got = binary.LittleEndian.Uint32(b[f.Off:])
763+ }
764+ if got != v {
765+ return normalized{err: fmt.Errorf("field %s at %d = %#x, want %#x", f.Role, f.Off, got, v)}
766+ }
767+ }
768+ clear(b[f.Off : f.Off+f.Size])
769+ }
770+ return normalized{b: b}
771+}
772+
773+// checkListExtensions: a framed reply whose STR list holds exactly names_len
774+// entries filling the reply, including the extensions xturbo binds.
775+func checkListExtensions(p []byte, _ map[string]uint32) error {
776+ if len(p) < 32 || p[0] != 1 || len(p) != 32+4*int(binary.LittleEndian.Uint32(p[4:])) {
777+ return fmt.Errorf("bad framing")
778+ }
779+ names := map[string]bool{}
780+ off := 32
781+ for range int(p[1]) {
782+ if off >= len(p) || off+1+int(p[off]) > len(p) {
783+ return fmt.Errorf("STR list overruns the reply")
784+ }
785+ names[string(p[off+1:off+1+int(p[off])])] = true
786+ off += 1 + int(p[off])
787+ }
788+ if (off+3)&^3 != len(p) {
789+ return fmt.Errorf("STR list ends at %d, reply has %d bytes", off, len(p))
790+ }
791+ for _, want := range []string{"BIG-REQUESTS", "XC-MISC", "XFIXES", "RENDER", "SHAPE", "Composite", "X-Resource", "Generic Event Extension"} {
792+ if !names[want] {
793+ return fmt.Errorf("missing extension %q", want)
794+ }
795+ }
796+ return nil
797+}
798+
799+// checkQueryPictFormats: declared counts match the list sizes exactly and the
800+// root visual maps to the picture format CreatePicture used.
801+func checkQueryPictFormats(p []byte, roles map[string]uint32) error {
802+ u32 := binary.LittleEndian.Uint32
803+ if len(p) < 32 || p[0] != 1 || len(p) != 32+4*int(u32(p[4:])) {
804+ return fmt.Errorf("bad framing")
805+ }
806+ formats, screens, subpixels := int(u32(p[8:])), int(u32(p[12:])), int(u32(p[24:]))
807+ off := 32 + 28*formats
808+ found := false
809+ for range screens {
810+ if off+8 > len(p) {
811+ return fmt.Errorf("screens overrun")
403 }812 }
813+ depths := int(u32(p[off:]))
814+ off += 8
815+ for range depths {
816+ if off+8 > len(p) {
817+ return fmt.Errorf("depths overrun")
818+ }
819+ visuals := int(binary.LittleEndian.Uint16(p[off+2:]))
820+ off += 8
821+ for range visuals {
822+ if off+8 > len(p) {
823+ return fmt.Errorf("visuals overrun")
824+ }
825+ if u32(p[off:]) == roles["rootVisual"] && u32(p[off+4:]) == roles["rootFormat"] {
826+ found = true
827+ }
828+ off += 8
829+ }
830+ }
831+ }
832+ off += 4 * subpixels
833+ if off != len(p) {
834+ return fmt.Errorf("lists end at %d, reply has %d bytes", off, len(p))
835+ }
836+ if !found {
837+ return fmt.Errorf("root visual %#x does not map to format %#x", roles["rootVisual"], roles["rootFormat"])
404 }838 }
839+ return nil
405 }840 }
A render/errors_generated.go +21 -0
@@ -0,0 +1,21 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+// Error codes: error codes relative to the extension's FirstError (XError.Rel).
5+const (
6+ BadPictFormat uint8 = 0
7+ BadPicture uint8 = 1
8+ BadPictOp uint8 = 2
9+ BadGlyphSet uint8 = 3
10+ BadGlyph uint8 = 4
11+)
12+
13+var errorNames = [...]string{"BadPictFormat", "BadPicture", "BadPictOp", "BadGlyphSet", "BadGlyph"}
14+
15+// ErrorName returns the name of an error code (error codes relative to the extension's FirstError (XError.Rel)), or "" when unknown.
16+func ErrorName(code uint8) string {
17+ if int(code) < len(errorNames) {
18+ return errorNames[code]
19+ }
20+ return ""
21+}
A shape/events_fuzz_test.go +28 -0
@@ -0,0 +1,28 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package shape
3+
4+import "testing"
5+import "bnema.dev/xturbo"
6+
7+func FuzzEventViews(f *testing.F) {
8+ f.Add(make([]byte, 32))
9+ f.Fuzz(func(t *testing.T, raw []byte) {
10+ if len(raw) < 32 {
11+ return
12+ }
13+ var ev xturbo.Event
14+ copy(ev.Raw[:], raw)
15+ ev.Tail = raw[32:]
16+ {
17+ vNotify := NotifyOf(&ev)
18+ _ = vNotify.ShapeKind()
19+ _ = vNotify.AffectedWindow()
20+ _ = vNotify.ExtentsX()
21+ _ = vNotify.ExtentsY()
22+ _ = vNotify.ExtentsWidth()
23+ _ = vNotify.ExtentsHeight()
24+ _ = vNotify.ServerTime()
25+ _ = vNotify.Shaped()
26+ }
27+ })
28+}
A shape/events_generated.go +28 -0
@@ -0,0 +1,28 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package shape
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+import "bnema.dev/xturbo"
7+
8+// KindNotify identifies Notify events in xturbo.Event.Kind.
9+const KindNotify = xturbo.EventKind(xturbo.ExtShape) << 8
10+
11+// NotifyEvent is a view over a Notify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
12+type NotifyEvent struct{ raw *[32]byte }
13+
14+// NotifyOf views ev as Notify without checking ev.Kind: a wrong kind yields garbage, never a panic.
15+func NotifyOf(ev *xturbo.Event) NotifyEvent { return NotifyEvent{raw: &ev.Raw} }
16+func (e NotifyEvent) Raw() *[32]byte { return e.raw }
17+func (e NotifyEvent) ShapeKind() Sk { return Sk(e.raw[1:][0]) }
18+func (e NotifyEvent) AffectedWindow() xproto.Window {
19+ return xproto.Window(binary.LittleEndian.Uint32(e.raw[4:]))
20+}
21+func (e NotifyEvent) ExtentsX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[8:])) }
22+func (e NotifyEvent) ExtentsY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[10:])) }
23+func (e NotifyEvent) ExtentsWidth() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[12:])) }
24+func (e NotifyEvent) ExtentsHeight() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[14:])) }
25+func (e NotifyEvent) ServerTime() xproto.Timestamp {
26+ return xproto.Timestamp(binary.LittleEndian.Uint32(e.raw[16:]))
27+}
28+func (e NotifyEvent) Shaped() bool { return (e.raw[20:][0] != 0) }
A testdata/golden/xfixes/BadRegion.error.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb error via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+008c2400888820000a008a000000000000000000000000000000000000000000
A testdata/golden/xfixes/SelectionNotify.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+57001b009f03000001004000010000003e8987003e8987000000000000000000
A testdata/golden/xproto/BadAtom.error.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb error via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+00052200efbead0d000011000000000000000000000000000000000000000000
A testdata/golden/xproto/BadWindow.error.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb error via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+0003230077772000000003000000000000000000000000000000000000000000
A testdata/golden/xproto/ClientMessage.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+a1201b0001004000e700000004030201080604020c090603100c0804140f0a05
A testdata/golden/xproto/ConfigureRequest.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+17001b009f0300000100400000000000fbff28008002c8000000070000000000
A testdata/golden/xproto/CreateNotify.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+10001b009f030000010040000a0014002c01c800000000000000000000000000
A testdata/golden/xproto/DestroyNotify.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+11001e009f030000010040000000000000000000000000000000000000000000
A testdata/golden/xproto/GeGeneric.event.hex +5 -0
@@ -0,0 +1,5 @@
1+# hand: no v1 extension emits XGE events on Xvfb; GenericEvent framing per the
2+# Generic Event Extension spec: code 35, extension major 0x83, seq 5, length 2,
3+# evtype 1, 22 pad bytes, then 8 tail bytes
4+2383050002000000010000000000000000000000000000000000000000000000
5+a0a1a2a3a4a5a6a7
A testdata/golden/xproto/KeymapNotify.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# hand: Xvfb has no keyboard to focus; KeymapNotify carries no sequence number,
2+# code 11 then 31 key-vector bytes (X11 protocol, KeymapNotify encoding)
3+0b0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
A testdata/golden/xproto/MapNotify.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+13001d009f030000010040000000000000000000000000000000000000000000
A testdata/golden/xproto/MapRequest.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+14001b009f030000010040000000000000000000000000000000000000000000
A testdata/golden/xproto/PropertyNotify.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+1c001b009f030000e70000003e89870000000000000000000000000000000000
A testdata/golden/xproto/SelectionNotify.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+1f0020000000000001002000020000001f000000000000000000000000000000
A testdata/golden/xproto/UnmapNotify.event.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+12001e009f030000010040000000000000000000000000000000000000000000
A testdata/golden/xvfb-roles.json +19 -0
@@ -0,0 +1,19 @@
1+{
2+ "atom": 231,
3+ "badRegion": 2132104,
4+ "badWindow": 2127735,
5+ "base": 2097152,
6+ "child": 2097153,
7+ "client2": 4194305,
8+ "picture": 2097155,
9+ "renderMajor": 139,
10+ "resMajor": 148,
11+ "root": 927,
12+ "rootColormap": 32,
13+ "rootFormat": 41,
14+ "rootVisual": 33,
15+ "sibling": 2097154,
16+ "xfixesError": 140,
17+ "xfixesEvent": 87,
18+ "xfixesMajor": 138
19+}
A xerror.go +54 -0
@@ -0,0 +1,54 @@
1+package xturbo
2+
3+import (
4+ "encoding/binary"
5+ "fmt"
6+)
7+
8+// XError is an X protocol error, passed by value (no interface on the hot path).
9+type XError struct {
10+ Seq Seq
11+ Code uint8 // wire error code
12+ Ext ExtID // ExtCore for codes < 128 or unknown
13+ Rel uint8 // Code - FirstError of Ext
14+ Bad uint32 // bad resource/value
15+ Minor uint16
16+ Major uint8
17+ Kind uint16 // ring slot kind, 0 when the request was not registered
18+ Args [4]uint32
19+ Raw [32]byte
20+}
21+
22+// Error formats the error. It allocates: use it for logging only.
23+func (e *XError) Error() string {
24+ ext := "core"
25+ if e.Ext != ExtCore && e.Ext < extCount {
26+ ext = ExtXName[e.Ext]
27+ }
28+ return fmt.Sprintf("xturbo: X error code %d (%s rel %d) seq %d major %d minor %d bad 0x%x kind %d",
29+ e.Code, ext, e.Rel, e.Seq, e.Major, e.Minor, e.Bad, e.Kind)
30+}
31+
32+// decodeError fills e from a 32-byte error packet (raw[0] == 0). Seq is the
33+// 16-bit wire value; Kind/Args are left for the reply ring. Extension codes
34+// resolve through ext; a nil ext or unknown code leaves Ext=ExtCore, Rel=Code.
35+func decodeError(raw []byte, ext *ExtTable, e *XError) bool {
36+ if len(raw) < 32 || raw[0] != 0 {
37+ return false
38+ }
39+ *e = XError{
40+ Seq: Seq(binary.LittleEndian.Uint16(raw[2:])),
41+ Code: raw[1],
42+ Rel: raw[1],
43+ Bad: binary.LittleEndian.Uint32(raw[4:]),
44+ Minor: binary.LittleEndian.Uint16(raw[8:]),
45+ Major: raw[10],
46+ }
47+ copy(e.Raw[:], raw[:32])
48+ if ext != nil && e.Code >= 128 {
49+ if id, rel, ok := ext.ByError(e.Code); ok {
50+ e.Ext, e.Rel = id, rel
51+ }
52+ }
53+ return true
54+}
A xerror_test.go +67 -0
@@ -0,0 +1,67 @@
1+package xturbo
2+
3+import (
4+ "encoding/binary"
5+ "strings"
6+ "testing"
7+
8+ "bnema.dev/xturbo/internal/xtest"
9+)
10+
11+// Error goldens are Xvfb's answers to the capture script's GetAtomName(0x0dadbeef),
12+// GetWindowAttributes(base|0x7777) and XFIXES DestroyRegion(base|0x8888). Error
13+// codes are the X11 protocol's (BadAtom 5, BadWindow 3) and XFIXES's first error
14+// plus BadRegion (0); the generated constants are checked in events_test.go.
15+func TestErrorGoldens(t *testing.T) {
16+ var ext ExtTable
17+ ext.Set(ExtXFixes, 138, 87, 140)
18+ for _, tc := range []struct {
19+ name string
20+ code, rel, major uint8
21+ minor uint16
22+ ext ExtID
23+ bad uint32
24+ badMask uint32
25+ }{
26+ {"xproto/BadAtom", 5, 5, 17, 0, ExtCore, 0x0dadbeef, 0xffffffff},
27+ {"xproto/BadWindow", 3, 3, 3, 0, ExtCore, 0x7777, 0x1fffff},
28+ {"xfixes/BadRegion", 140, 0, 138, 10, ExtXFixes, 0x8888, 0x1fffff},
29+ } {
30+ raw, err := xtest.LoadHex("testdata/golden/" + tc.name + ".error.hex")
31+ if err != nil {
32+ t.Fatal(err)
33+ }
34+ var e XError
35+ if !decodeError(raw, &ext, &e) {
36+ t.Fatalf("%s: rejected", tc.name)
37+ }
38+ if e.Code != tc.code || e.Rel != tc.rel || e.Ext != tc.ext || e.Major != tc.major || e.Minor != tc.minor ||
39+ e.Bad&tc.badMask != tc.bad || e.Seq != Seq(binary.LittleEndian.Uint16(raw[2:])) || e.Raw != [32]byte(raw) || e.Kind != 0 {
40+ t.Fatalf("%s: %+v", tc.name, e)
41+ }
42+ if !strings.Contains(e.Error(), "X error code") {
43+ t.Fatal(e.Error())
44+ }
45+ // Without a table the extension error stays unresolved.
46+ if decodeError(raw, nil, &e); e.Ext != ExtCore || e.Rel != tc.code {
47+ t.Fatalf("%s: nil table resolved", tc.name)
48+ }
49+ }
50+ var e XError
51+ if decodeError(make([]byte, 31), nil, &e) || decodeError(append([]byte{2}, make([]byte, 31)...), nil, &e) {
52+ t.Fatal("non-error packet accepted")
53+ }
54+}
55+
56+func TestAllocXError(t *testing.T) {
57+ raw, err := xtest.LoadHex("testdata/golden/xfixes/BadRegion.error.hex")
58+ if err != nil {
59+ t.Fatal(err)
60+ }
61+ var ext ExtTable
62+ ext.Set(ExtXFixes, 138, 87, 140)
63+ var e XError
64+ if n := testing.AllocsPerRun(1000, func() { decodeError(raw, &ext, &e) }); n != 0 {
65+ t.Fatalf("decodeError allocates %v times", n)
66+ }
67+}
A xfixes/errors_generated.go +17 -0
@@ -0,0 +1,17 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xfixes
3+
4+// Error codes: error codes relative to the extension's FirstError (XError.Rel).
5+const (
6+ BadRegion uint8 = 0
7+)
8+
9+var errorNames = [...]string{"BadRegion"}
10+
11+// ErrorName returns the name of an error code (error codes relative to the extension's FirstError (XError.Rel)), or "" when unknown.
12+func ErrorName(code uint8) string {
13+ if int(code) < len(errorNames) {
14+ return errorNames[code]
15+ }
16+ return ""
17+}
A xfixes/events_fuzz_test.go +40 -0
@@ -0,0 +1,40 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xfixes
3+
4+import "testing"
5+import "bnema.dev/xturbo"
6+import "bnema.dev/xturbo/internal/xtest"
7+
8+func FuzzEventViews(f *testing.F) {
9+ f.Add(make([]byte, 32))
10+ if raw, err := xtest.LoadHex("../testdata/golden/xfixes/SelectionNotify.event.hex"); err != nil {
11+ f.Fatal(err)
12+ } else {
13+ f.Add(raw)
14+ }
15+ f.Fuzz(func(t *testing.T, raw []byte) {
16+ if len(raw) < 32 {
17+ return
18+ }
19+ var ev xturbo.Event
20+ copy(ev.Raw[:], raw)
21+ ev.Tail = raw[32:]
22+ {
23+ vSelectionNotify := SelectionNotifyOf(&ev)
24+ _ = vSelectionNotify.Subtype()
25+ _ = vSelectionNotify.Window()
26+ _ = vSelectionNotify.Owner()
27+ _ = vSelectionNotify.Selection()
28+ _ = vSelectionNotify.Timestamp()
29+ _ = vSelectionNotify.SelectionTimestamp()
30+ }
31+ {
32+ vCursorNotify := CursorNotifyOf(&ev)
33+ _ = vCursorNotify.Subtype()
34+ _ = vCursorNotify.Window()
35+ _ = vCursorNotify.CursorSerial()
36+ _ = vCursorNotify.Timestamp()
37+ _ = vCursorNotify.Name()
38+ }
39+ })
40+}
A xfixes/events_generated.go +57 -0
@@ -0,0 +1,57 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xfixes
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+import "bnema.dev/xturbo"
7+
8+// KindSelectionNotify identifies SelectionNotify events in xturbo.Event.Kind.
9+const KindSelectionNotify = xturbo.EventKind(xturbo.ExtXFixes) << 8
10+
11+// SelectionNotifyEvent is a view over a SelectionNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
12+type SelectionNotifyEvent struct{ raw *[32]byte }
13+
14+// SelectionNotifyOf views ev as SelectionNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
15+func SelectionNotifyOf(ev *xturbo.Event) SelectionNotifyEvent {
16+ return SelectionNotifyEvent{raw: &ev.Raw}
17+}
18+func (e SelectionNotifyEvent) Raw() *[32]byte { return e.raw }
19+func (e SelectionNotifyEvent) Subtype() SelectionEvent { return SelectionEvent(e.raw[1:][0]) }
20+func (e SelectionNotifyEvent) Window() xproto.Window {
21+ return xproto.Window(binary.LittleEndian.Uint32(e.raw[4:]))
22+}
23+func (e SelectionNotifyEvent) Owner() xproto.Window {
24+ return xproto.Window(binary.LittleEndian.Uint32(e.raw[8:]))
25+}
26+func (e SelectionNotifyEvent) Selection() xproto.Atom {
27+ return xproto.Atom(binary.LittleEndian.Uint32(e.raw[12:]))
28+}
29+func (e SelectionNotifyEvent) Timestamp() xproto.Timestamp {
30+ return xproto.Timestamp(binary.LittleEndian.Uint32(e.raw[16:]))
31+}
32+func (e SelectionNotifyEvent) SelectionTimestamp() xproto.Timestamp {
33+ return xproto.Timestamp(binary.LittleEndian.Uint32(e.raw[20:]))
34+}
35+
36+// KindCursorNotify identifies CursorNotify events in xturbo.Event.Kind.
37+const KindCursorNotify = xturbo.EventKind(xturbo.ExtXFixes)<<8 | 1
38+
39+// CursorNotifyEvent is a view over a CursorNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
40+type CursorNotifyEvent struct{ raw *[32]byte }
41+
42+// CursorNotifyOf views ev as CursorNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
43+func CursorNotifyOf(ev *xturbo.Event) CursorNotifyEvent { return CursorNotifyEvent{raw: &ev.Raw} }
44+func (e CursorNotifyEvent) Raw() *[32]byte { return e.raw }
45+func (e CursorNotifyEvent) Subtype() CursorNotify { return CursorNotify(e.raw[1:][0]) }
46+func (e CursorNotifyEvent) Window() xproto.Window {
47+ return xproto.Window(binary.LittleEndian.Uint32(e.raw[4:]))
48+}
49+func (e CursorNotifyEvent) CursorSerial() uint32 {
50+ return uint32(binary.LittleEndian.Uint32(e.raw[8:]))
51+}
52+func (e CursorNotifyEvent) Timestamp() xproto.Timestamp {
53+ return xproto.Timestamp(binary.LittleEndian.Uint32(e.raw[12:]))
54+}
55+func (e CursorNotifyEvent) Name() xproto.Atom {
56+ return xproto.Atom(binary.LittleEndian.Uint32(e.raw[16:]))
57+}
A xproto/errors_generated.go +33 -0
@@ -0,0 +1,33 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xproto
3+
4+// Error codes: wire error codes.
5+const (
6+ BadRequest uint8 = 1
7+ BadValue uint8 = 2
8+ BadWindow uint8 = 3
9+ BadPixmap uint8 = 4
10+ BadAtom uint8 = 5
11+ BadCursor uint8 = 6
12+ BadFont uint8 = 7
13+ BadMatch uint8 = 8
14+ BadDrawable uint8 = 9
15+ BadAccess uint8 = 10
16+ BadAlloc uint8 = 11
17+ BadColormap uint8 = 12
18+ BadGContext uint8 = 13
19+ BadIDChoice uint8 = 14
20+ BadName uint8 = 15
21+ BadLength uint8 = 16
22+ BadImplementation uint8 = 17
23+)
24+
25+var errorNames = [...]string{"", "BadRequest", "BadValue", "BadWindow", "BadPixmap", "BadAtom", "BadCursor", "BadFont", "BadMatch", "BadDrawable", "BadAccess", "BadAlloc", "BadColormap", "BadGContext", "BadIDChoice", "BadName", "BadLength", "BadImplementation"}
26+
27+// ErrorName returns the name of an error code (wire error codes), or "" when unknown.
28+func ErrorName(code uint8) string {
29+ if int(code) < len(errorNames) {
30+ return errorNames[code]
31+ }
32+ return ""
33+}
A xproto/events_fuzz_test.go +1189 -0
@@ -0,0 +1,1189 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xproto
3+
4+import "testing"
5+import "bnema.dev/xturbo"
6+import "bnema.dev/xturbo/internal/xtest"
7+import "bytes"
8+
9+func FuzzEventViews(f *testing.F) {
10+ f.Add(make([]byte, 32))
11+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/ClientMessage.event.hex"); err != nil {
12+ f.Fatal(err)
13+ } else {
14+ f.Add(raw)
15+ }
16+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/ConfigureRequest.event.hex"); err != nil {
17+ f.Fatal(err)
18+ } else {
19+ f.Add(raw)
20+ }
21+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/CreateNotify.event.hex"); err != nil {
22+ f.Fatal(err)
23+ } else {
24+ f.Add(raw)
25+ }
26+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/DestroyNotify.event.hex"); err != nil {
27+ f.Fatal(err)
28+ } else {
29+ f.Add(raw)
30+ }
31+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/GeGeneric.event.hex"); err != nil {
32+ f.Fatal(err)
33+ } else {
34+ f.Add(raw)
35+ }
36+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/KeymapNotify.event.hex"); err != nil {
37+ f.Fatal(err)
38+ } else {
39+ f.Add(raw)
40+ }
41+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/MapNotify.event.hex"); err != nil {
42+ f.Fatal(err)
43+ } else {
44+ f.Add(raw)
45+ }
46+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/MapRequest.event.hex"); err != nil {
47+ f.Fatal(err)
48+ } else {
49+ f.Add(raw)
50+ }
51+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/PropertyNotify.event.hex"); err != nil {
52+ f.Fatal(err)
53+ } else {
54+ f.Add(raw)
55+ }
56+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/SelectionNotify.event.hex"); err != nil {
57+ f.Fatal(err)
58+ } else {
59+ f.Add(raw)
60+ }
61+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/UnmapNotify.event.hex"); err != nil {
62+ f.Fatal(err)
63+ } else {
64+ f.Add(raw)
65+ }
66+ f.Fuzz(func(t *testing.T, raw []byte) {
67+ if len(raw) < 32 {
68+ return
69+ }
70+ var ev xturbo.Event
71+ copy(ev.Raw[:], raw)
72+ ev.Tail = raw[32:]
73+ {
74+ vKeyPress := KeyPressOf(&ev)
75+ _ = vKeyPress.Detail()
76+ _ = vKeyPress.Time()
77+ _ = vKeyPress.Root()
78+ _ = vKeyPress.Event()
79+ _ = vKeyPress.Child()
80+ _ = vKeyPress.RootX()
81+ _ = vKeyPress.RootY()
82+ _ = vKeyPress.EventX()
83+ _ = vKeyPress.EventY()
84+ _ = vKeyPress.State()
85+ _ = vKeyPress.SameScreen()
86+ d := KeyPressEventData{Detail: vKeyPress.Detail(), Time: vKeyPress.Time(), Root: vKeyPress.Root(), Event: vKeyPress.Event(), Child: vKeyPress.Child(), RootX: vKeyPress.RootX(), RootY: vKeyPress.RootY(), EventX: vKeyPress.EventX(), EventY: vKeyPress.EventY(), State: vKeyPress.State(), SameScreen: vKeyPress.SameScreen()}
87+ var out [32]byte
88+ d.Put(&out)
89+ if out[0] != 2 {
90+ t.Fatal("KeyPress code")
91+ }
92+ back := xturbo.Event{Raw: out}
93+ w := KeyPressOf(&back)
94+ if w.Detail() != vKeyPress.Detail() {
95+ t.Fatal("KeyPress.Detail")
96+ }
97+ if w.Time() != vKeyPress.Time() {
98+ t.Fatal("KeyPress.Time")
99+ }
100+ if w.Root() != vKeyPress.Root() {
101+ t.Fatal("KeyPress.Root")
102+ }
103+ if w.Event() != vKeyPress.Event() {
104+ t.Fatal("KeyPress.Event")
105+ }
106+ if w.Child() != vKeyPress.Child() {
107+ t.Fatal("KeyPress.Child")
108+ }
109+ if w.RootX() != vKeyPress.RootX() {
110+ t.Fatal("KeyPress.RootX")
111+ }
112+ if w.RootY() != vKeyPress.RootY() {
113+ t.Fatal("KeyPress.RootY")
114+ }
115+ if w.EventX() != vKeyPress.EventX() {
116+ t.Fatal("KeyPress.EventX")
117+ }
118+ if w.EventY() != vKeyPress.EventY() {
119+ t.Fatal("KeyPress.EventY")
120+ }
121+ if w.State() != vKeyPress.State() {
122+ t.Fatal("KeyPress.State")
123+ }
124+ if w.SameScreen() != vKeyPress.SameScreen() {
125+ t.Fatal("KeyPress.SameScreen")
126+ }
127+ in := xturbo.Event{Raw: ev.Raw}
128+ vi := KeyPressOf(&in)
129+ di := KeyPressEventData{Detail: vi.Detail(), Time: vi.Time(), Root: vi.Root(), Event: vi.Event(), Child: vi.Child(), RootX: vi.RootX(), RootY: vi.RootY(), EventX: vi.EventX(), EventY: vi.EventY(), State: vi.State(), SameScreen: vi.SameScreen()}
130+ di.Put(&in.Raw)
131+ if in.Raw != out {
132+ t.Fatal("KeyPress in-place Put")
133+ }
134+ }
135+ _ = KeyReleaseOf(&ev)
136+ {
137+ vButtonPress := ButtonPressOf(&ev)
138+ _ = vButtonPress.Detail()
139+ _ = vButtonPress.Time()
140+ _ = vButtonPress.Root()
141+ _ = vButtonPress.Event()
142+ _ = vButtonPress.Child()
143+ _ = vButtonPress.RootX()
144+ _ = vButtonPress.RootY()
145+ _ = vButtonPress.EventX()
146+ _ = vButtonPress.EventY()
147+ _ = vButtonPress.State()
148+ _ = vButtonPress.SameScreen()
149+ d := ButtonPressEventData{Detail: vButtonPress.Detail(), Time: vButtonPress.Time(), Root: vButtonPress.Root(), Event: vButtonPress.Event(), Child: vButtonPress.Child(), RootX: vButtonPress.RootX(), RootY: vButtonPress.RootY(), EventX: vButtonPress.EventX(), EventY: vButtonPress.EventY(), State: vButtonPress.State(), SameScreen: vButtonPress.SameScreen()}
150+ var out [32]byte
151+ d.Put(&out)
152+ if out[0] != 4 {
153+ t.Fatal("ButtonPress code")
154+ }
155+ back := xturbo.Event{Raw: out}
156+ w := ButtonPressOf(&back)
157+ if w.Detail() != vButtonPress.Detail() {
158+ t.Fatal("ButtonPress.Detail")
159+ }
160+ if w.Time() != vButtonPress.Time() {
161+ t.Fatal("ButtonPress.Time")
162+ }
163+ if w.Root() != vButtonPress.Root() {
164+ t.Fatal("ButtonPress.Root")
165+ }
166+ if w.Event() != vButtonPress.Event() {
167+ t.Fatal("ButtonPress.Event")
168+ }
169+ if w.Child() != vButtonPress.Child() {
170+ t.Fatal("ButtonPress.Child")
171+ }
172+ if w.RootX() != vButtonPress.RootX() {
173+ t.Fatal("ButtonPress.RootX")
174+ }
175+ if w.RootY() != vButtonPress.RootY() {
176+ t.Fatal("ButtonPress.RootY")
177+ }
178+ if w.EventX() != vButtonPress.EventX() {
179+ t.Fatal("ButtonPress.EventX")
180+ }
181+ if w.EventY() != vButtonPress.EventY() {
182+ t.Fatal("ButtonPress.EventY")
183+ }
184+ if w.State() != vButtonPress.State() {
185+ t.Fatal("ButtonPress.State")
186+ }
187+ if w.SameScreen() != vButtonPress.SameScreen() {
188+ t.Fatal("ButtonPress.SameScreen")
189+ }
190+ in := xturbo.Event{Raw: ev.Raw}
191+ vi := ButtonPressOf(&in)
192+ di := ButtonPressEventData{Detail: vi.Detail(), Time: vi.Time(), Root: vi.Root(), Event: vi.Event(), Child: vi.Child(), RootX: vi.RootX(), RootY: vi.RootY(), EventX: vi.EventX(), EventY: vi.EventY(), State: vi.State(), SameScreen: vi.SameScreen()}
193+ di.Put(&in.Raw)
194+ if in.Raw != out {
195+ t.Fatal("ButtonPress in-place Put")
196+ }
197+ }
198+ _ = ButtonReleaseOf(&ev)
199+ {
200+ vMotionNotify := MotionNotifyOf(&ev)
201+ _ = vMotionNotify.Detail()
202+ _ = vMotionNotify.Time()
203+ _ = vMotionNotify.Root()
204+ _ = vMotionNotify.Event()
205+ _ = vMotionNotify.Child()
206+ _ = vMotionNotify.RootX()
207+ _ = vMotionNotify.RootY()
208+ _ = vMotionNotify.EventX()
209+ _ = vMotionNotify.EventY()
210+ _ = vMotionNotify.State()
211+ _ = vMotionNotify.SameScreen()
212+ d := MotionNotifyEventData{Detail: vMotionNotify.Detail(), Time: vMotionNotify.Time(), Root: vMotionNotify.Root(), Event: vMotionNotify.Event(), Child: vMotionNotify.Child(), RootX: vMotionNotify.RootX(), RootY: vMotionNotify.RootY(), EventX: vMotionNotify.EventX(), EventY: vMotionNotify.EventY(), State: vMotionNotify.State(), SameScreen: vMotionNotify.SameScreen()}
213+ var out [32]byte
214+ d.Put(&out)
215+ if out[0] != 6 {
216+ t.Fatal("MotionNotify code")
217+ }
218+ back := xturbo.Event{Raw: out}
219+ w := MotionNotifyOf(&back)
220+ if w.Detail() != vMotionNotify.Detail() {
221+ t.Fatal("MotionNotify.Detail")
222+ }
223+ if w.Time() != vMotionNotify.Time() {
224+ t.Fatal("MotionNotify.Time")
225+ }
226+ if w.Root() != vMotionNotify.Root() {
227+ t.Fatal("MotionNotify.Root")
228+ }
229+ if w.Event() != vMotionNotify.Event() {
230+ t.Fatal("MotionNotify.Event")
231+ }
232+ if w.Child() != vMotionNotify.Child() {
233+ t.Fatal("MotionNotify.Child")
234+ }
235+ if w.RootX() != vMotionNotify.RootX() {
236+ t.Fatal("MotionNotify.RootX")
237+ }
238+ if w.RootY() != vMotionNotify.RootY() {
239+ t.Fatal("MotionNotify.RootY")
240+ }
241+ if w.EventX() != vMotionNotify.EventX() {
242+ t.Fatal("MotionNotify.EventX")
243+ }
244+ if w.EventY() != vMotionNotify.EventY() {
245+ t.Fatal("MotionNotify.EventY")
246+ }
247+ if w.State() != vMotionNotify.State() {
248+ t.Fatal("MotionNotify.State")
249+ }
250+ if w.SameScreen() != vMotionNotify.SameScreen() {
251+ t.Fatal("MotionNotify.SameScreen")
252+ }
253+ in := xturbo.Event{Raw: ev.Raw}
254+ vi := MotionNotifyOf(&in)
255+ di := MotionNotifyEventData{Detail: vi.Detail(), Time: vi.Time(), Root: vi.Root(), Event: vi.Event(), Child: vi.Child(), RootX: vi.RootX(), RootY: vi.RootY(), EventX: vi.EventX(), EventY: vi.EventY(), State: vi.State(), SameScreen: vi.SameScreen()}
256+ di.Put(&in.Raw)
257+ if in.Raw != out {
258+ t.Fatal("MotionNotify in-place Put")
259+ }
260+ }
261+ {
262+ vEnterNotify := EnterNotifyOf(&ev)
263+ _ = vEnterNotify.Detail()
264+ _ = vEnterNotify.Time()
265+ _ = vEnterNotify.Root()
266+ _ = vEnterNotify.Event()
267+ _ = vEnterNotify.Child()
268+ _ = vEnterNotify.RootX()
269+ _ = vEnterNotify.RootY()
270+ _ = vEnterNotify.EventX()
271+ _ = vEnterNotify.EventY()
272+ _ = vEnterNotify.State()
273+ _ = vEnterNotify.Mode()
274+ _ = vEnterNotify.SameScreenFocus()
275+ d := EnterNotifyEventData{Detail: vEnterNotify.Detail(), Time: vEnterNotify.Time(), Root: vEnterNotify.Root(), Event: vEnterNotify.Event(), Child: vEnterNotify.Child(), RootX: vEnterNotify.RootX(), RootY: vEnterNotify.RootY(), EventX: vEnterNotify.EventX(), EventY: vEnterNotify.EventY(), State: vEnterNotify.State(), Mode: vEnterNotify.Mode(), SameScreenFocus: vEnterNotify.SameScreenFocus()}
276+ var out [32]byte
277+ d.Put(&out)
278+ if out[0] != 7 {
279+ t.Fatal("EnterNotify code")
280+ }
281+ back := xturbo.Event{Raw: out}
282+ w := EnterNotifyOf(&back)
283+ if w.Detail() != vEnterNotify.Detail() {
284+ t.Fatal("EnterNotify.Detail")
285+ }
286+ if w.Time() != vEnterNotify.Time() {
287+ t.Fatal("EnterNotify.Time")
288+ }
289+ if w.Root() != vEnterNotify.Root() {
290+ t.Fatal("EnterNotify.Root")
291+ }
292+ if w.Event() != vEnterNotify.Event() {
293+ t.Fatal("EnterNotify.Event")
294+ }
295+ if w.Child() != vEnterNotify.Child() {
296+ t.Fatal("EnterNotify.Child")
297+ }
298+ if w.RootX() != vEnterNotify.RootX() {
299+ t.Fatal("EnterNotify.RootX")
300+ }
301+ if w.RootY() != vEnterNotify.RootY() {
302+ t.Fatal("EnterNotify.RootY")
303+ }
304+ if w.EventX() != vEnterNotify.EventX() {
305+ t.Fatal("EnterNotify.EventX")
306+ }
307+ if w.EventY() != vEnterNotify.EventY() {
308+ t.Fatal("EnterNotify.EventY")
309+ }
310+ if w.State() != vEnterNotify.State() {
311+ t.Fatal("EnterNotify.State")
312+ }
313+ if w.Mode() != vEnterNotify.Mode() {
314+ t.Fatal("EnterNotify.Mode")
315+ }
316+ if w.SameScreenFocus() != vEnterNotify.SameScreenFocus() {
317+ t.Fatal("EnterNotify.SameScreenFocus")
318+ }
319+ in := xturbo.Event{Raw: ev.Raw}
320+ vi := EnterNotifyOf(&in)
321+ di := EnterNotifyEventData{Detail: vi.Detail(), Time: vi.Time(), Root: vi.Root(), Event: vi.Event(), Child: vi.Child(), RootX: vi.RootX(), RootY: vi.RootY(), EventX: vi.EventX(), EventY: vi.EventY(), State: vi.State(), Mode: vi.Mode(), SameScreenFocus: vi.SameScreenFocus()}
322+ di.Put(&in.Raw)
323+ if in.Raw != out {
324+ t.Fatal("EnterNotify in-place Put")
325+ }
326+ }
327+ _ = LeaveNotifyOf(&ev)
328+ {
329+ vFocusIn := FocusInOf(&ev)
330+ _ = vFocusIn.Detail()
331+ _ = vFocusIn.Event()
332+ _ = vFocusIn.Mode()
333+ d := FocusInEventData{Detail: vFocusIn.Detail(), Event: vFocusIn.Event(), Mode: vFocusIn.Mode()}
334+ var out [32]byte
335+ d.Put(&out)
336+ if out[0] != 9 {
337+ t.Fatal("FocusIn code")
338+ }
339+ back := xturbo.Event{Raw: out}
340+ w := FocusInOf(&back)
341+ if w.Detail() != vFocusIn.Detail() {
342+ t.Fatal("FocusIn.Detail")
343+ }
344+ if w.Event() != vFocusIn.Event() {
345+ t.Fatal("FocusIn.Event")
346+ }
347+ if w.Mode() != vFocusIn.Mode() {
348+ t.Fatal("FocusIn.Mode")
349+ }
350+ in := xturbo.Event{Raw: ev.Raw}
351+ vi := FocusInOf(&in)
352+ di := FocusInEventData{Detail: vi.Detail(), Event: vi.Event(), Mode: vi.Mode()}
353+ di.Put(&in.Raw)
354+ if in.Raw != out {
355+ t.Fatal("FocusIn in-place Put")
356+ }
357+ }
358+ _ = FocusOutOf(&ev)
359+ {
360+ vKeymapNotify := KeymapNotifyOf(&ev)
361+ _ = len(vKeymapNotify.Keys())
362+ d := KeymapNotifyEventData{Keys: [31]uint8(vKeymapNotify.Keys())}
363+ var out [32]byte
364+ d.Put(&out)
365+ if out[0] != 11 {
366+ t.Fatal("KeymapNotify code")
367+ }
368+ back := xturbo.Event{Raw: out}
369+ w := KeymapNotifyOf(&back)
370+ if !bytes.Equal(w.Keys(), vKeymapNotify.Keys()) {
371+ t.Fatal("KeymapNotify.Keys")
372+ }
373+ in := xturbo.Event{Raw: ev.Raw}
374+ vi := KeymapNotifyOf(&in)
375+ di := KeymapNotifyEventData{Keys: [31]uint8(vi.Keys())}
376+ di.Put(&in.Raw)
377+ if in.Raw != out {
378+ t.Fatal("KeymapNotify in-place Put")
379+ }
380+ }
381+ {
382+ vExpose := ExposeOf(&ev)
383+ _ = vExpose.Window()
384+ _ = vExpose.X()
385+ _ = vExpose.Y()
386+ _ = vExpose.Width()
387+ _ = vExpose.Height()
388+ _ = vExpose.Count()
389+ d := ExposeEventData{Window: vExpose.Window(), X: vExpose.X(), Y: vExpose.Y(), Width: vExpose.Width(), Height: vExpose.Height(), Count: vExpose.Count()}
390+ var out [32]byte
391+ d.Put(&out)
392+ if out[0] != 12 {
393+ t.Fatal("Expose code")
394+ }
395+ back := xturbo.Event{Raw: out}
396+ w := ExposeOf(&back)
397+ if w.Window() != vExpose.Window() {
398+ t.Fatal("Expose.Window")
399+ }
400+ if w.X() != vExpose.X() {
401+ t.Fatal("Expose.X")
402+ }
403+ if w.Y() != vExpose.Y() {
404+ t.Fatal("Expose.Y")
405+ }
406+ if w.Width() != vExpose.Width() {
407+ t.Fatal("Expose.Width")
408+ }
409+ if w.Height() != vExpose.Height() {
410+ t.Fatal("Expose.Height")
411+ }
412+ if w.Count() != vExpose.Count() {
413+ t.Fatal("Expose.Count")
414+ }
415+ in := xturbo.Event{Raw: ev.Raw}
416+ vi := ExposeOf(&in)
417+ di := ExposeEventData{Window: vi.Window(), X: vi.X(), Y: vi.Y(), Width: vi.Width(), Height: vi.Height(), Count: vi.Count()}
418+ di.Put(&in.Raw)
419+ if in.Raw != out {
420+ t.Fatal("Expose in-place Put")
421+ }
422+ }
423+ {
424+ vGraphicsExposure := GraphicsExposureOf(&ev)
425+ _ = vGraphicsExposure.Drawable()
426+ _ = vGraphicsExposure.X()
427+ _ = vGraphicsExposure.Y()
428+ _ = vGraphicsExposure.Width()
429+ _ = vGraphicsExposure.Height()
430+ _ = vGraphicsExposure.MinorOpcode()
431+ _ = vGraphicsExposure.Count()
432+ _ = vGraphicsExposure.MajorOpcode()
433+ d := GraphicsExposureEventData{Drawable: vGraphicsExposure.Drawable(), X: vGraphicsExposure.X(), Y: vGraphicsExposure.Y(), Width: vGraphicsExposure.Width(), Height: vGraphicsExposure.Height(), MinorOpcode: vGraphicsExposure.MinorOpcode(), Count: vGraphicsExposure.Count(), MajorOpcode: vGraphicsExposure.MajorOpcode()}
434+ var out [32]byte
435+ d.Put(&out)
436+ if out[0] != 13 {
437+ t.Fatal("GraphicsExposure code")
438+ }
439+ back := xturbo.Event{Raw: out}
440+ w := GraphicsExposureOf(&back)
441+ if w.Drawable() != vGraphicsExposure.Drawable() {
442+ t.Fatal("GraphicsExposure.Drawable")
443+ }
444+ if w.X() != vGraphicsExposure.X() {
445+ t.Fatal("GraphicsExposure.X")
446+ }
447+ if w.Y() != vGraphicsExposure.Y() {
448+ t.Fatal("GraphicsExposure.Y")
449+ }
450+ if w.Width() != vGraphicsExposure.Width() {
451+ t.Fatal("GraphicsExposure.Width")
452+ }
453+ if w.Height() != vGraphicsExposure.Height() {
454+ t.Fatal("GraphicsExposure.Height")
455+ }
456+ if w.MinorOpcode() != vGraphicsExposure.MinorOpcode() {
457+ t.Fatal("GraphicsExposure.MinorOpcode")
458+ }
459+ if w.Count() != vGraphicsExposure.Count() {
460+ t.Fatal("GraphicsExposure.Count")
461+ }
462+ if w.MajorOpcode() != vGraphicsExposure.MajorOpcode() {
463+ t.Fatal("GraphicsExposure.MajorOpcode")
464+ }
465+ in := xturbo.Event{Raw: ev.Raw}
466+ vi := GraphicsExposureOf(&in)
467+ di := GraphicsExposureEventData{Drawable: vi.Drawable(), X: vi.X(), Y: vi.Y(), Width: vi.Width(), Height: vi.Height(), MinorOpcode: vi.MinorOpcode(), Count: vi.Count(), MajorOpcode: vi.MajorOpcode()}
468+ di.Put(&in.Raw)
469+ if in.Raw != out {
470+ t.Fatal("GraphicsExposure in-place Put")
471+ }
472+ }
473+ {
474+ vNoExposure := NoExposureOf(&ev)
475+ _ = vNoExposure.Drawable()
476+ _ = vNoExposure.MinorOpcode()
477+ _ = vNoExposure.MajorOpcode()
478+ d := NoExposureEventData{Drawable: vNoExposure.Drawable(), MinorOpcode: vNoExposure.MinorOpcode(), MajorOpcode: vNoExposure.MajorOpcode()}
479+ var out [32]byte
480+ d.Put(&out)
481+ if out[0] != 14 {
482+ t.Fatal("NoExposure code")
483+ }
484+ back := xturbo.Event{Raw: out}
485+ w := NoExposureOf(&back)
486+ if w.Drawable() != vNoExposure.Drawable() {
487+ t.Fatal("NoExposure.Drawable")
488+ }
489+ if w.MinorOpcode() != vNoExposure.MinorOpcode() {
490+ t.Fatal("NoExposure.MinorOpcode")
491+ }
492+ if w.MajorOpcode() != vNoExposure.MajorOpcode() {
493+ t.Fatal("NoExposure.MajorOpcode")
494+ }
495+ in := xturbo.Event{Raw: ev.Raw}
496+ vi := NoExposureOf(&in)
497+ di := NoExposureEventData{Drawable: vi.Drawable(), MinorOpcode: vi.MinorOpcode(), MajorOpcode: vi.MajorOpcode()}
498+ di.Put(&in.Raw)
499+ if in.Raw != out {
500+ t.Fatal("NoExposure in-place Put")
501+ }
502+ }
503+ {
504+ vVisibilityNotify := VisibilityNotifyOf(&ev)
505+ _ = vVisibilityNotify.Window()
506+ _ = vVisibilityNotify.State()
507+ d := VisibilityNotifyEventData{Window: vVisibilityNotify.Window(), State: vVisibilityNotify.State()}
508+ var out [32]byte
509+ d.Put(&out)
510+ if out[0] != 15 {
511+ t.Fatal("VisibilityNotify code")
512+ }
513+ back := xturbo.Event{Raw: out}
514+ w := VisibilityNotifyOf(&back)
515+ if w.Window() != vVisibilityNotify.Window() {
516+ t.Fatal("VisibilityNotify.Window")
517+ }
518+ if w.State() != vVisibilityNotify.State() {
519+ t.Fatal("VisibilityNotify.State")
520+ }
521+ in := xturbo.Event{Raw: ev.Raw}
522+ vi := VisibilityNotifyOf(&in)
523+ di := VisibilityNotifyEventData{Window: vi.Window(), State: vi.State()}
524+ di.Put(&in.Raw)
525+ if in.Raw != out {
526+ t.Fatal("VisibilityNotify in-place Put")
527+ }
528+ }
529+ {
530+ vCreateNotify := CreateNotifyOf(&ev)
531+ _ = vCreateNotify.Parent()
532+ _ = vCreateNotify.Window()
533+ _ = vCreateNotify.X()
534+ _ = vCreateNotify.Y()
535+ _ = vCreateNotify.Width()
536+ _ = vCreateNotify.Height()
537+ _ = vCreateNotify.BorderWidth()
538+ _ = vCreateNotify.OverrideRedirect()
539+ d := CreateNotifyEventData{Parent: vCreateNotify.Parent(), Window: vCreateNotify.Window(), X: vCreateNotify.X(), Y: vCreateNotify.Y(), Width: vCreateNotify.Width(), Height: vCreateNotify.Height(), BorderWidth: vCreateNotify.BorderWidth(), OverrideRedirect: vCreateNotify.OverrideRedirect()}
540+ var out [32]byte
541+ d.Put(&out)
542+ if out[0] != 16 {
543+ t.Fatal("CreateNotify code")
544+ }
545+ back := xturbo.Event{Raw: out}
546+ w := CreateNotifyOf(&back)
547+ if w.Parent() != vCreateNotify.Parent() {
548+ t.Fatal("CreateNotify.Parent")
549+ }
550+ if w.Window() != vCreateNotify.Window() {
551+ t.Fatal("CreateNotify.Window")
552+ }
553+ if w.X() != vCreateNotify.X() {
554+ t.Fatal("CreateNotify.X")
555+ }
556+ if w.Y() != vCreateNotify.Y() {
557+ t.Fatal("CreateNotify.Y")
558+ }
559+ if w.Width() != vCreateNotify.Width() {
560+ t.Fatal("CreateNotify.Width")
561+ }
562+ if w.Height() != vCreateNotify.Height() {
563+ t.Fatal("CreateNotify.Height")
564+ }
565+ if w.BorderWidth() != vCreateNotify.BorderWidth() {
566+ t.Fatal("CreateNotify.BorderWidth")
567+ }
568+ if w.OverrideRedirect() != vCreateNotify.OverrideRedirect() {
569+ t.Fatal("CreateNotify.OverrideRedirect")
570+ }
571+ in := xturbo.Event{Raw: ev.Raw}
572+ vi := CreateNotifyOf(&in)
573+ di := CreateNotifyEventData{Parent: vi.Parent(), Window: vi.Window(), X: vi.X(), Y: vi.Y(), Width: vi.Width(), Height: vi.Height(), BorderWidth: vi.BorderWidth(), OverrideRedirect: vi.OverrideRedirect()}
574+ di.Put(&in.Raw)
575+ if in.Raw != out {
576+ t.Fatal("CreateNotify in-place Put")
577+ }
578+ }
579+ {
580+ vDestroyNotify := DestroyNotifyOf(&ev)
581+ _ = vDestroyNotify.Event()
582+ _ = vDestroyNotify.Window()
583+ d := DestroyNotifyEventData{Event: vDestroyNotify.Event(), Window: vDestroyNotify.Window()}
584+ var out [32]byte
585+ d.Put(&out)
586+ if out[0] != 17 {
587+ t.Fatal("DestroyNotify code")
588+ }
589+ back := xturbo.Event{Raw: out}
590+ w := DestroyNotifyOf(&back)
591+ if w.Event() != vDestroyNotify.Event() {
592+ t.Fatal("DestroyNotify.Event")
593+ }
594+ if w.Window() != vDestroyNotify.Window() {
595+ t.Fatal("DestroyNotify.Window")
596+ }
597+ in := xturbo.Event{Raw: ev.Raw}
598+ vi := DestroyNotifyOf(&in)
599+ di := DestroyNotifyEventData{Event: vi.Event(), Window: vi.Window()}
600+ di.Put(&in.Raw)
601+ if in.Raw != out {
602+ t.Fatal("DestroyNotify in-place Put")
603+ }
604+ }
605+ {
606+ vUnmapNotify := UnmapNotifyOf(&ev)
607+ _ = vUnmapNotify.Event()
608+ _ = vUnmapNotify.Window()
609+ _ = vUnmapNotify.FromConfigure()
610+ d := UnmapNotifyEventData{Event: vUnmapNotify.Event(), Window: vUnmapNotify.Window(), FromConfigure: vUnmapNotify.FromConfigure()}
611+ var out [32]byte
612+ d.Put(&out)
613+ if out[0] != 18 {
614+ t.Fatal("UnmapNotify code")
615+ }
616+ back := xturbo.Event{Raw: out}
617+ w := UnmapNotifyOf(&back)
618+ if w.Event() != vUnmapNotify.Event() {
619+ t.Fatal("UnmapNotify.Event")
620+ }
621+ if w.Window() != vUnmapNotify.Window() {
622+ t.Fatal("UnmapNotify.Window")
623+ }
624+ if w.FromConfigure() != vUnmapNotify.FromConfigure() {
625+ t.Fatal("UnmapNotify.FromConfigure")
626+ }
627+ in := xturbo.Event{Raw: ev.Raw}
628+ vi := UnmapNotifyOf(&in)
629+ di := UnmapNotifyEventData{Event: vi.Event(), Window: vi.Window(), FromConfigure: vi.FromConfigure()}
630+ di.Put(&in.Raw)
631+ if in.Raw != out {
632+ t.Fatal("UnmapNotify in-place Put")
633+ }
634+ }
635+ {
636+ vMapNotify := MapNotifyOf(&ev)
637+ _ = vMapNotify.Event()
638+ _ = vMapNotify.Window()
639+ _ = vMapNotify.OverrideRedirect()
640+ d := MapNotifyEventData{Event: vMapNotify.Event(), Window: vMapNotify.Window(), OverrideRedirect: vMapNotify.OverrideRedirect()}
641+ var out [32]byte
642+ d.Put(&out)
643+ if out[0] != 19 {
644+ t.Fatal("MapNotify code")
645+ }
646+ back := xturbo.Event{Raw: out}
647+ w := MapNotifyOf(&back)
648+ if w.Event() != vMapNotify.Event() {
649+ t.Fatal("MapNotify.Event")
650+ }
651+ if w.Window() != vMapNotify.Window() {
652+ t.Fatal("MapNotify.Window")
653+ }
654+ if w.OverrideRedirect() != vMapNotify.OverrideRedirect() {
655+ t.Fatal("MapNotify.OverrideRedirect")
656+ }
657+ in := xturbo.Event{Raw: ev.Raw}
658+ vi := MapNotifyOf(&in)
659+ di := MapNotifyEventData{Event: vi.Event(), Window: vi.Window(), OverrideRedirect: vi.OverrideRedirect()}
660+ di.Put(&in.Raw)
661+ if in.Raw != out {
662+ t.Fatal("MapNotify in-place Put")
663+ }
664+ }
665+ {
666+ vMapRequest := MapRequestOf(&ev)
667+ _ = vMapRequest.Parent()
668+ _ = vMapRequest.Window()
669+ d := MapRequestEventData{Parent: vMapRequest.Parent(), Window: vMapRequest.Window()}
670+ var out [32]byte
671+ d.Put(&out)
672+ if out[0] != 20 {
673+ t.Fatal("MapRequest code")
674+ }
675+ back := xturbo.Event{Raw: out}
676+ w := MapRequestOf(&back)
677+ if w.Parent() != vMapRequest.Parent() {
678+ t.Fatal("MapRequest.Parent")
679+ }
680+ if w.Window() != vMapRequest.Window() {
681+ t.Fatal("MapRequest.Window")
682+ }
683+ in := xturbo.Event{Raw: ev.Raw}
684+ vi := MapRequestOf(&in)
685+ di := MapRequestEventData{Parent: vi.Parent(), Window: vi.Window()}
686+ di.Put(&in.Raw)
687+ if in.Raw != out {
688+ t.Fatal("MapRequest in-place Put")
689+ }
690+ }
691+ {
692+ vReparentNotify := ReparentNotifyOf(&ev)
693+ _ = vReparentNotify.Event()
694+ _ = vReparentNotify.Window()
695+ _ = vReparentNotify.Parent()
696+ _ = vReparentNotify.X()
697+ _ = vReparentNotify.Y()
698+ _ = vReparentNotify.OverrideRedirect()
699+ d := ReparentNotifyEventData{Event: vReparentNotify.Event(), Window: vReparentNotify.Window(), Parent: vReparentNotify.Parent(), X: vReparentNotify.X(), Y: vReparentNotify.Y(), OverrideRedirect: vReparentNotify.OverrideRedirect()}
700+ var out [32]byte
701+ d.Put(&out)
702+ if out[0] != 21 {
703+ t.Fatal("ReparentNotify code")
704+ }
705+ back := xturbo.Event{Raw: out}
706+ w := ReparentNotifyOf(&back)
707+ if w.Event() != vReparentNotify.Event() {
708+ t.Fatal("ReparentNotify.Event")
709+ }
710+ if w.Window() != vReparentNotify.Window() {
711+ t.Fatal("ReparentNotify.Window")
712+ }
713+ if w.Parent() != vReparentNotify.Parent() {
714+ t.Fatal("ReparentNotify.Parent")
715+ }
716+ if w.X() != vReparentNotify.X() {
717+ t.Fatal("ReparentNotify.X")
718+ }
719+ if w.Y() != vReparentNotify.Y() {
720+ t.Fatal("ReparentNotify.Y")
721+ }
722+ if w.OverrideRedirect() != vReparentNotify.OverrideRedirect() {
723+ t.Fatal("ReparentNotify.OverrideRedirect")
724+ }
725+ in := xturbo.Event{Raw: ev.Raw}
726+ vi := ReparentNotifyOf(&in)
727+ di := ReparentNotifyEventData{Event: vi.Event(), Window: vi.Window(), Parent: vi.Parent(), X: vi.X(), Y: vi.Y(), OverrideRedirect: vi.OverrideRedirect()}
728+ di.Put(&in.Raw)
729+ if in.Raw != out {
730+ t.Fatal("ReparentNotify in-place Put")
731+ }
732+ }
733+ {
734+ vConfigureNotify := ConfigureNotifyOf(&ev)
735+ _ = vConfigureNotify.Event()
736+ _ = vConfigureNotify.Window()
737+ _ = vConfigureNotify.AboveSibling()
738+ _ = vConfigureNotify.X()
739+ _ = vConfigureNotify.Y()
740+ _ = vConfigureNotify.Width()
741+ _ = vConfigureNotify.Height()
742+ _ = vConfigureNotify.BorderWidth()
743+ _ = vConfigureNotify.OverrideRedirect()
744+ d := ConfigureNotifyEventData{Event: vConfigureNotify.Event(), Window: vConfigureNotify.Window(), AboveSibling: vConfigureNotify.AboveSibling(), X: vConfigureNotify.X(), Y: vConfigureNotify.Y(), Width: vConfigureNotify.Width(), Height: vConfigureNotify.Height(), BorderWidth: vConfigureNotify.BorderWidth(), OverrideRedirect: vConfigureNotify.OverrideRedirect()}
745+ var out [32]byte
746+ d.Put(&out)
747+ if out[0] != 22 {
748+ t.Fatal("ConfigureNotify code")
749+ }
750+ back := xturbo.Event{Raw: out}
751+ w := ConfigureNotifyOf(&back)
752+ if w.Event() != vConfigureNotify.Event() {
753+ t.Fatal("ConfigureNotify.Event")
754+ }
755+ if w.Window() != vConfigureNotify.Window() {
756+ t.Fatal("ConfigureNotify.Window")
757+ }
758+ if w.AboveSibling() != vConfigureNotify.AboveSibling() {
759+ t.Fatal("ConfigureNotify.AboveSibling")
760+ }
761+ if w.X() != vConfigureNotify.X() {
762+ t.Fatal("ConfigureNotify.X")
763+ }
764+ if w.Y() != vConfigureNotify.Y() {
765+ t.Fatal("ConfigureNotify.Y")
766+ }
767+ if w.Width() != vConfigureNotify.Width() {
768+ t.Fatal("ConfigureNotify.Width")
769+ }
770+ if w.Height() != vConfigureNotify.Height() {
771+ t.Fatal("ConfigureNotify.Height")
772+ }
773+ if w.BorderWidth() != vConfigureNotify.BorderWidth() {
774+ t.Fatal("ConfigureNotify.BorderWidth")
775+ }
776+ if w.OverrideRedirect() != vConfigureNotify.OverrideRedirect() {
777+ t.Fatal("ConfigureNotify.OverrideRedirect")
778+ }
779+ in := xturbo.Event{Raw: ev.Raw}
780+ vi := ConfigureNotifyOf(&in)
781+ di := ConfigureNotifyEventData{Event: vi.Event(), Window: vi.Window(), AboveSibling: vi.AboveSibling(), X: vi.X(), Y: vi.Y(), Width: vi.Width(), Height: vi.Height(), BorderWidth: vi.BorderWidth(), OverrideRedirect: vi.OverrideRedirect()}
782+ di.Put(&in.Raw)
783+ if in.Raw != out {
784+ t.Fatal("ConfigureNotify in-place Put")
785+ }
786+ }
787+ {
788+ vConfigureRequest := ConfigureRequestOf(&ev)
789+ _ = vConfigureRequest.StackMode()
790+ _ = vConfigureRequest.Parent()
791+ _ = vConfigureRequest.Window()
792+ _ = vConfigureRequest.Sibling()
793+ _ = vConfigureRequest.X()
794+ _ = vConfigureRequest.Y()
795+ _ = vConfigureRequest.Width()
796+ _ = vConfigureRequest.Height()
797+ _ = vConfigureRequest.BorderWidth()
798+ _ = vConfigureRequest.ValueMask()
799+ d := ConfigureRequestEventData{StackMode: vConfigureRequest.StackMode(), Parent: vConfigureRequest.Parent(), Window: vConfigureRequest.Window(), Sibling: vConfigureRequest.Sibling(), X: vConfigureRequest.X(), Y: vConfigureRequest.Y(), Width: vConfigureRequest.Width(), Height: vConfigureRequest.Height(), BorderWidth: vConfigureRequest.BorderWidth(), ValueMask: vConfigureRequest.ValueMask()}
800+ var out [32]byte
801+ d.Put(&out)
802+ if out[0] != 23 {
803+ t.Fatal("ConfigureRequest code")
804+ }
805+ back := xturbo.Event{Raw: out}
806+ w := ConfigureRequestOf(&back)
807+ if w.StackMode() != vConfigureRequest.StackMode() {
808+ t.Fatal("ConfigureRequest.StackMode")
809+ }
810+ if w.Parent() != vConfigureRequest.Parent() {
811+ t.Fatal("ConfigureRequest.Parent")
812+ }
813+ if w.Window() != vConfigureRequest.Window() {
814+ t.Fatal("ConfigureRequest.Window")
815+ }
816+ if w.Sibling() != vConfigureRequest.Sibling() {
817+ t.Fatal("ConfigureRequest.Sibling")
818+ }
819+ if w.X() != vConfigureRequest.X() {
820+ t.Fatal("ConfigureRequest.X")
821+ }
822+ if w.Y() != vConfigureRequest.Y() {
823+ t.Fatal("ConfigureRequest.Y")
824+ }
825+ if w.Width() != vConfigureRequest.Width() {
826+ t.Fatal("ConfigureRequest.Width")
827+ }
828+ if w.Height() != vConfigureRequest.Height() {
829+ t.Fatal("ConfigureRequest.Height")
830+ }
831+ if w.BorderWidth() != vConfigureRequest.BorderWidth() {
832+ t.Fatal("ConfigureRequest.BorderWidth")
833+ }
834+ if w.ValueMask() != vConfigureRequest.ValueMask() {
835+ t.Fatal("ConfigureRequest.ValueMask")
836+ }
837+ in := xturbo.Event{Raw: ev.Raw}
838+ vi := ConfigureRequestOf(&in)
839+ di := ConfigureRequestEventData{StackMode: vi.StackMode(), Parent: vi.Parent(), Window: vi.Window(), Sibling: vi.Sibling(), X: vi.X(), Y: vi.Y(), Width: vi.Width(), Height: vi.Height(), BorderWidth: vi.BorderWidth(), ValueMask: vi.ValueMask()}
840+ di.Put(&in.Raw)
841+ if in.Raw != out {
842+ t.Fatal("ConfigureRequest in-place Put")
843+ }
844+ }
845+ {
846+ vGravityNotify := GravityNotifyOf(&ev)
847+ _ = vGravityNotify.Event()
848+ _ = vGravityNotify.Window()
849+ _ = vGravityNotify.X()
850+ _ = vGravityNotify.Y()
851+ d := GravityNotifyEventData{Event: vGravityNotify.Event(), Window: vGravityNotify.Window(), X: vGravityNotify.X(), Y: vGravityNotify.Y()}
852+ var out [32]byte
853+ d.Put(&out)
854+ if out[0] != 24 {
855+ t.Fatal("GravityNotify code")
856+ }
857+ back := xturbo.Event{Raw: out}
858+ w := GravityNotifyOf(&back)
859+ if w.Event() != vGravityNotify.Event() {
860+ t.Fatal("GravityNotify.Event")
861+ }
862+ if w.Window() != vGravityNotify.Window() {
863+ t.Fatal("GravityNotify.Window")
864+ }
865+ if w.X() != vGravityNotify.X() {
866+ t.Fatal("GravityNotify.X")
867+ }
868+ if w.Y() != vGravityNotify.Y() {
869+ t.Fatal("GravityNotify.Y")
870+ }
871+ in := xturbo.Event{Raw: ev.Raw}
872+ vi := GravityNotifyOf(&in)
873+ di := GravityNotifyEventData{Event: vi.Event(), Window: vi.Window(), X: vi.X(), Y: vi.Y()}
874+ di.Put(&in.Raw)
875+ if in.Raw != out {
876+ t.Fatal("GravityNotify in-place Put")
877+ }
878+ }
879+ {
880+ vResizeRequest := ResizeRequestOf(&ev)
881+ _ = vResizeRequest.Window()
882+ _ = vResizeRequest.Width()
883+ _ = vResizeRequest.Height()
884+ d := ResizeRequestEventData{Window: vResizeRequest.Window(), Width: vResizeRequest.Width(), Height: vResizeRequest.Height()}
885+ var out [32]byte
886+ d.Put(&out)
887+ if out[0] != 25 {
888+ t.Fatal("ResizeRequest code")
889+ }
890+ back := xturbo.Event{Raw: out}
891+ w := ResizeRequestOf(&back)
892+ if w.Window() != vResizeRequest.Window() {
893+ t.Fatal("ResizeRequest.Window")
894+ }
895+ if w.Width() != vResizeRequest.Width() {
896+ t.Fatal("ResizeRequest.Width")
897+ }
898+ if w.Height() != vResizeRequest.Height() {
899+ t.Fatal("ResizeRequest.Height")
900+ }
901+ in := xturbo.Event{Raw: ev.Raw}
902+ vi := ResizeRequestOf(&in)
903+ di := ResizeRequestEventData{Window: vi.Window(), Width: vi.Width(), Height: vi.Height()}
904+ di.Put(&in.Raw)
905+ if in.Raw != out {
906+ t.Fatal("ResizeRequest in-place Put")
907+ }
908+ }
909+ {
910+ vCirculateNotify := CirculateNotifyOf(&ev)
911+ _ = vCirculateNotify.Event()
912+ _ = vCirculateNotify.Window()
913+ _ = vCirculateNotify.Place()
914+ d := CirculateNotifyEventData{Event: vCirculateNotify.Event(), Window: vCirculateNotify.Window(), Place: vCirculateNotify.Place()}
915+ var out [32]byte
916+ d.Put(&out)
917+ if out[0] != 26 {
918+ t.Fatal("CirculateNotify code")
919+ }
920+ back := xturbo.Event{Raw: out}
921+ w := CirculateNotifyOf(&back)
922+ if w.Event() != vCirculateNotify.Event() {
923+ t.Fatal("CirculateNotify.Event")
924+ }
925+ if w.Window() != vCirculateNotify.Window() {
926+ t.Fatal("CirculateNotify.Window")
927+ }
928+ if w.Place() != vCirculateNotify.Place() {
929+ t.Fatal("CirculateNotify.Place")
930+ }
931+ in := xturbo.Event{Raw: ev.Raw}
932+ vi := CirculateNotifyOf(&in)
933+ di := CirculateNotifyEventData{Event: vi.Event(), Window: vi.Window(), Place: vi.Place()}
934+ di.Put(&in.Raw)
935+ if in.Raw != out {
936+ t.Fatal("CirculateNotify in-place Put")
937+ }
938+ }
939+ _ = CirculateRequestOf(&ev)
940+ {
941+ vPropertyNotify := PropertyNotifyOf(&ev)
942+ _ = vPropertyNotify.Window()
943+ _ = vPropertyNotify.Atom()
944+ _ = vPropertyNotify.Time()
945+ _ = vPropertyNotify.State()
946+ d := PropertyNotifyEventData{Window: vPropertyNotify.Window(), Atom: vPropertyNotify.Atom(), Time: vPropertyNotify.Time(), State: vPropertyNotify.State()}
947+ var out [32]byte
948+ d.Put(&out)
949+ if out[0] != 28 {
950+ t.Fatal("PropertyNotify code")
951+ }
952+ back := xturbo.Event{Raw: out}
953+ w := PropertyNotifyOf(&back)
954+ if w.Window() != vPropertyNotify.Window() {
955+ t.Fatal("PropertyNotify.Window")
956+ }
957+ if w.Atom() != vPropertyNotify.Atom() {
958+ t.Fatal("PropertyNotify.Atom")
959+ }
960+ if w.Time() != vPropertyNotify.Time() {
961+ t.Fatal("PropertyNotify.Time")
962+ }
963+ if w.State() != vPropertyNotify.State() {
964+ t.Fatal("PropertyNotify.State")
965+ }
966+ in := xturbo.Event{Raw: ev.Raw}
967+ vi := PropertyNotifyOf(&in)
968+ di := PropertyNotifyEventData{Window: vi.Window(), Atom: vi.Atom(), Time: vi.Time(), State: vi.State()}
969+ di.Put(&in.Raw)
970+ if in.Raw != out {
971+ t.Fatal("PropertyNotify in-place Put")
972+ }
973+ }
974+ {
975+ vSelectionClear := SelectionClearOf(&ev)
976+ _ = vSelectionClear.Time()
977+ _ = vSelectionClear.Owner()
978+ _ = vSelectionClear.Selection()
979+ d := SelectionClearEventData{Time: vSelectionClear.Time(), Owner: vSelectionClear.Owner(), Selection: vSelectionClear.Selection()}
980+ var out [32]byte
981+ d.Put(&out)
982+ if out[0] != 29 {
983+ t.Fatal("SelectionClear code")
984+ }
985+ back := xturbo.Event{Raw: out}
986+ w := SelectionClearOf(&back)
987+ if w.Time() != vSelectionClear.Time() {
988+ t.Fatal("SelectionClear.Time")
989+ }
990+ if w.Owner() != vSelectionClear.Owner() {
991+ t.Fatal("SelectionClear.Owner")
992+ }
993+ if w.Selection() != vSelectionClear.Selection() {
994+ t.Fatal("SelectionClear.Selection")
995+ }
996+ in := xturbo.Event{Raw: ev.Raw}
997+ vi := SelectionClearOf(&in)
998+ di := SelectionClearEventData{Time: vi.Time(), Owner: vi.Owner(), Selection: vi.Selection()}
999+ di.Put(&in.Raw)
1000+ if in.Raw != out {
1001+ t.Fatal("SelectionClear in-place Put")
1002+ }
1003+ }
1004+ {
1005+ vSelectionRequest := SelectionRequestOf(&ev)
1006+ _ = vSelectionRequest.Time()
1007+ _ = vSelectionRequest.Owner()
1008+ _ = vSelectionRequest.Requestor()
1009+ _ = vSelectionRequest.Selection()
1010+ _ = vSelectionRequest.Target()
1011+ _ = vSelectionRequest.Property()
1012+ d := SelectionRequestEventData{Time: vSelectionRequest.Time(), Owner: vSelectionRequest.Owner(), Requestor: vSelectionRequest.Requestor(), Selection: vSelectionRequest.Selection(), Target: vSelectionRequest.Target(), Property: vSelectionRequest.Property()}
1013+ var out [32]byte
1014+ d.Put(&out)
1015+ if out[0] != 30 {
1016+ t.Fatal("SelectionRequest code")
1017+ }
1018+ back := xturbo.Event{Raw: out}
1019+ w := SelectionRequestOf(&back)
1020+ if w.Time() != vSelectionRequest.Time() {
1021+ t.Fatal("SelectionRequest.Time")
1022+ }
1023+ if w.Owner() != vSelectionRequest.Owner() {
1024+ t.Fatal("SelectionRequest.Owner")
1025+ }
1026+ if w.Requestor() != vSelectionRequest.Requestor() {
1027+ t.Fatal("SelectionRequest.Requestor")
1028+ }
1029+ if w.Selection() != vSelectionRequest.Selection() {
1030+ t.Fatal("SelectionRequest.Selection")
1031+ }
1032+ if w.Target() != vSelectionRequest.Target() {
1033+ t.Fatal("SelectionRequest.Target")
1034+ }
1035+ if w.Property() != vSelectionRequest.Property() {
1036+ t.Fatal("SelectionRequest.Property")
1037+ }
1038+ in := xturbo.Event{Raw: ev.Raw}
1039+ vi := SelectionRequestOf(&in)
1040+ di := SelectionRequestEventData{Time: vi.Time(), Owner: vi.Owner(), Requestor: vi.Requestor(), Selection: vi.Selection(), Target: vi.Target(), Property: vi.Property()}
1041+ di.Put(&in.Raw)
1042+ if in.Raw != out {
1043+ t.Fatal("SelectionRequest in-place Put")
1044+ }
1045+ }
1046+ {
1047+ vSelectionNotify := SelectionNotifyOf(&ev)
1048+ _ = vSelectionNotify.Time()
1049+ _ = vSelectionNotify.Requestor()
1050+ _ = vSelectionNotify.Selection()
1051+ _ = vSelectionNotify.Target()
1052+ _ = vSelectionNotify.Property()
1053+ d := SelectionNotifyEventData{Time: vSelectionNotify.Time(), Requestor: vSelectionNotify.Requestor(), Selection: vSelectionNotify.Selection(), Target: vSelectionNotify.Target(), Property: vSelectionNotify.Property()}
1054+ var out [32]byte
1055+ d.Put(&out)
1056+ if out[0] != 31 {
1057+ t.Fatal("SelectionNotify code")
1058+ }
1059+ back := xturbo.Event{Raw: out}
1060+ w := SelectionNotifyOf(&back)
1061+ if w.Time() != vSelectionNotify.Time() {
1062+ t.Fatal("SelectionNotify.Time")
1063+ }
1064+ if w.Requestor() != vSelectionNotify.Requestor() {
1065+ t.Fatal("SelectionNotify.Requestor")
1066+ }
1067+ if w.Selection() != vSelectionNotify.Selection() {
1068+ t.Fatal("SelectionNotify.Selection")
1069+ }
1070+ if w.Target() != vSelectionNotify.Target() {
1071+ t.Fatal("SelectionNotify.Target")
1072+ }
1073+ if w.Property() != vSelectionNotify.Property() {
1074+ t.Fatal("SelectionNotify.Property")
1075+ }
1076+ in := xturbo.Event{Raw: ev.Raw}
1077+ vi := SelectionNotifyOf(&in)
1078+ di := SelectionNotifyEventData{Time: vi.Time(), Requestor: vi.Requestor(), Selection: vi.Selection(), Target: vi.Target(), Property: vi.Property()}
1079+ di.Put(&in.Raw)
1080+ if in.Raw != out {
1081+ t.Fatal("SelectionNotify in-place Put")
1082+ }
1083+ }
1084+ {
1085+ vColormapNotify := ColormapNotifyOf(&ev)
1086+ _ = vColormapNotify.Window()
1087+ _ = vColormapNotify.Colormap()
1088+ _ = vColormapNotify.New()
1089+ _ = vColormapNotify.State()
1090+ d := ColormapNotifyEventData{Window: vColormapNotify.Window(), Colormap: vColormapNotify.Colormap(), New: vColormapNotify.New(), State: vColormapNotify.State()}
1091+ var out [32]byte
1092+ d.Put(&out)
1093+ if out[0] != 32 {
1094+ t.Fatal("ColormapNotify code")
1095+ }
1096+ back := xturbo.Event{Raw: out}
1097+ w := ColormapNotifyOf(&back)
1098+ if w.Window() != vColormapNotify.Window() {
1099+ t.Fatal("ColormapNotify.Window")
1100+ }
1101+ if w.Colormap() != vColormapNotify.Colormap() {
1102+ t.Fatal("ColormapNotify.Colormap")
1103+ }
1104+ if w.New() != vColormapNotify.New() {
1105+ t.Fatal("ColormapNotify.New")
1106+ }
1107+ if w.State() != vColormapNotify.State() {
1108+ t.Fatal("ColormapNotify.State")
1109+ }
1110+ in := xturbo.Event{Raw: ev.Raw}
1111+ vi := ColormapNotifyOf(&in)
1112+ di := ColormapNotifyEventData{Window: vi.Window(), Colormap: vi.Colormap(), New: vi.New(), State: vi.State()}
1113+ di.Put(&in.Raw)
1114+ if in.Raw != out {
1115+ t.Fatal("ColormapNotify in-place Put")
1116+ }
1117+ }
1118+ {
1119+ vClientMessage := ClientMessageOf(&ev)
1120+ _ = vClientMessage.Format()
1121+ _ = vClientMessage.Window()
1122+ _ = vClientMessage.Type()
1123+ _ = vClientMessage.Data()
1124+ d := ClientMessageEventData{Format: vClientMessage.Format(), Window: vClientMessage.Window(), Type: vClientMessage.Type(), Data: vClientMessage.Data()}
1125+ var out [32]byte
1126+ d.Put(&out)
1127+ if out[0] != 33 {
1128+ t.Fatal("ClientMessage code")
1129+ }
1130+ back := xturbo.Event{Raw: out}
1131+ w := ClientMessageOf(&back)
1132+ if w.Format() != vClientMessage.Format() {
1133+ t.Fatal("ClientMessage.Format")
1134+ }
1135+ if w.Window() != vClientMessage.Window() {
1136+ t.Fatal("ClientMessage.Window")
1137+ }
1138+ if w.Type() != vClientMessage.Type() {
1139+ t.Fatal("ClientMessage.Type")
1140+ }
1141+ if !bytes.Equal(out[12:32], ev.Raw[12:32]) {
1142+ t.Fatal("ClientMessage.Data")
1143+ }
1144+ in := xturbo.Event{Raw: ev.Raw}
1145+ vi := ClientMessageOf(&in)
1146+ di := ClientMessageEventData{Format: vi.Format(), Window: vi.Window(), Type: vi.Type(), Data: vi.Data()}
1147+ di.Put(&in.Raw)
1148+ if in.Raw != out {
1149+ t.Fatal("ClientMessage in-place Put")
1150+ }
1151+ }
1152+ {
1153+ vMappingNotify := MappingNotifyOf(&ev)
1154+ _ = vMappingNotify.Request()
1155+ _ = vMappingNotify.FirstKeycode()
1156+ _ = vMappingNotify.Count()
1157+ d := MappingNotifyEventData{Request: vMappingNotify.Request(), FirstKeycode: vMappingNotify.FirstKeycode(), Count: vMappingNotify.Count()}
1158+ var out [32]byte
1159+ d.Put(&out)
1160+ if out[0] != 34 {
1161+ t.Fatal("MappingNotify code")
1162+ }
1163+ back := xturbo.Event{Raw: out}
1164+ w := MappingNotifyOf(&back)
1165+ if w.Request() != vMappingNotify.Request() {
1166+ t.Fatal("MappingNotify.Request")
1167+ }
1168+ if w.FirstKeycode() != vMappingNotify.FirstKeycode() {
1169+ t.Fatal("MappingNotify.FirstKeycode")
1170+ }
1171+ if w.Count() != vMappingNotify.Count() {
1172+ t.Fatal("MappingNotify.Count")
1173+ }
1174+ in := xturbo.Event{Raw: ev.Raw}
1175+ vi := MappingNotifyOf(&in)
1176+ di := MappingNotifyEventData{Request: vi.Request(), FirstKeycode: vi.FirstKeycode(), Count: vi.Count()}
1177+ di.Put(&in.Raw)
1178+ if in.Raw != out {
1179+ t.Fatal("MappingNotify in-place Put")
1180+ }
1181+ }
1182+ if vGeGeneric, ok := GeGenericOf(&ev); ok {
1183+ _ = vGeGeneric.Extension()
1184+ _ = vGeGeneric.Length()
1185+ _ = vGeGeneric.EventType()
1186+ _ = len(vGeGeneric.Tail())
1187+ }
1188+ })
1189+}
A xproto/events_generated.go +1257 -0
@@ -0,0 +1,1257 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xproto
3+
4+import "encoding/binary"
5+import "bnema.dev/xturbo"
6+
7+// KindKeyPress identifies KeyPress events in xturbo.Event.Kind.
8+const KindKeyPress = xturbo.EventKind(2)
9+
10+// KeyPressEvent is a view over a KeyPress event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
11+type KeyPressEvent struct{ raw *[32]byte }
12+
13+// KeyPressOf views ev as KeyPress without checking ev.Kind: a wrong kind yields garbage, never a panic.
14+func KeyPressOf(ev *xturbo.Event) KeyPressEvent { return KeyPressEvent{raw: &ev.Raw} }
15+func (e KeyPressEvent) Raw() *[32]byte { return e.raw }
16+func (e KeyPressEvent) Detail() Keycode { return Keycode(e.raw[1:][0]) }
17+func (e KeyPressEvent) Time() Timestamp { return Timestamp(binary.LittleEndian.Uint32(e.raw[4:])) }
18+func (e KeyPressEvent) Root() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
19+func (e KeyPressEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[12:])) }
20+func (e KeyPressEvent) Child() Window { return Window(binary.LittleEndian.Uint32(e.raw[16:])) }
21+func (e KeyPressEvent) RootX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[20:])) }
22+func (e KeyPressEvent) RootY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[22:])) }
23+func (e KeyPressEvent) EventX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[24:])) }
24+func (e KeyPressEvent) EventY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[26:])) }
25+func (e KeyPressEvent) State() KeyButMask { return KeyButMask(binary.LittleEndian.Uint16(e.raw[28:])) }
26+func (e KeyPressEvent) SameScreen() bool { return (e.raw[30:][0] != 0) }
27+
28+// KeyPressEventData encodes a KeyPress event for SendEvent.
29+type KeyPressEventData struct {
30+ Detail Keycode
31+ Time Timestamp
32+ Root Window
33+ Event Window
34+ Child Window
35+ RootX int16
36+ RootY int16
37+ EventX int16
38+ EventY int16
39+ State KeyButMask
40+ SameScreen bool
41+}
42+
43+// Put writes the event (code 2, pads and sequence zeroed) into dst. dst may alias
44+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
45+func (d *KeyPressEventData) Put(dst *[32]byte) {
46+ var tmp [32]byte
47+ tmp[0] = 2
48+ tmp[1:][0] = byte(d.Detail)
49+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Time))
50+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Root))
51+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Event))
52+ binary.LittleEndian.PutUint32(tmp[16:], uint32(d.Child))
53+ binary.LittleEndian.PutUint16(tmp[20:], uint16(d.RootX))
54+ binary.LittleEndian.PutUint16(tmp[22:], uint16(d.RootY))
55+ binary.LittleEndian.PutUint16(tmp[24:], uint16(d.EventX))
56+ binary.LittleEndian.PutUint16(tmp[26:], uint16(d.EventY))
57+ binary.LittleEndian.PutUint16(tmp[28:], uint16(d.State))
58+ if d.SameScreen {
59+ tmp[30:][0] = 1
60+ } else {
61+ tmp[30:][0] = 0
62+ }
63+ *dst = tmp
64+}
65+
66+// KindKeyRelease identifies KeyRelease events in xturbo.Event.Kind.
67+const KindKeyRelease = xturbo.EventKind(3)
68+
69+// KeyReleaseEvent shares KeyPress's layout (eventcopy).
70+type KeyReleaseEvent = KeyPressEvent
71+
72+// KeyReleaseOf views ev as KeyRelease without checking ev.Kind.
73+func KeyReleaseOf(ev *xturbo.Event) KeyReleaseEvent { return KeyPressOf(ev) }
74+
75+// KeyReleaseEventData encodes a KeyRelease event for SendEvent with KeyPress's fields.
76+type KeyReleaseEventData KeyPressEventData
77+
78+// Put writes the event (code 3, pads and sequence zeroed) into dst.
79+func (d *KeyReleaseEventData) Put(dst *[32]byte) { (*KeyPressEventData)(d).Put(dst); dst[0] = 3 }
80+
81+// KindButtonPress identifies ButtonPress events in xturbo.Event.Kind.
82+const KindButtonPress = xturbo.EventKind(4)
83+
84+// ButtonPressEvent is a view over a ButtonPress event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
85+type ButtonPressEvent struct{ raw *[32]byte }
86+
87+// ButtonPressOf views ev as ButtonPress without checking ev.Kind: a wrong kind yields garbage, never a panic.
88+func ButtonPressOf(ev *xturbo.Event) ButtonPressEvent { return ButtonPressEvent{raw: &ev.Raw} }
89+func (e ButtonPressEvent) Raw() *[32]byte { return e.raw }
90+func (e ButtonPressEvent) Detail() Button { return Button(e.raw[1:][0]) }
91+func (e ButtonPressEvent) Time() Timestamp { return Timestamp(binary.LittleEndian.Uint32(e.raw[4:])) }
92+func (e ButtonPressEvent) Root() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
93+func (e ButtonPressEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[12:])) }
94+func (e ButtonPressEvent) Child() Window { return Window(binary.LittleEndian.Uint32(e.raw[16:])) }
95+func (e ButtonPressEvent) RootX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[20:])) }
96+func (e ButtonPressEvent) RootY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[22:])) }
97+func (e ButtonPressEvent) EventX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[24:])) }
98+func (e ButtonPressEvent) EventY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[26:])) }
99+func (e ButtonPressEvent) State() KeyButMask {
100+ return KeyButMask(binary.LittleEndian.Uint16(e.raw[28:]))
101+}
102+func (e ButtonPressEvent) SameScreen() bool { return (e.raw[30:][0] != 0) }
103+
104+// ButtonPressEventData encodes a ButtonPress event for SendEvent.
105+type ButtonPressEventData struct {
106+ Detail Button
107+ Time Timestamp
108+ Root Window
109+ Event Window
110+ Child Window
111+ RootX int16
112+ RootY int16
113+ EventX int16
114+ EventY int16
115+ State KeyButMask
116+ SameScreen bool
117+}
118+
119+// Put writes the event (code 4, pads and sequence zeroed) into dst. dst may alias
120+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
121+func (d *ButtonPressEventData) Put(dst *[32]byte) {
122+ var tmp [32]byte
123+ tmp[0] = 4
124+ tmp[1:][0] = byte(d.Detail)
125+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Time))
126+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Root))
127+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Event))
128+ binary.LittleEndian.PutUint32(tmp[16:], uint32(d.Child))
129+ binary.LittleEndian.PutUint16(tmp[20:], uint16(d.RootX))
130+ binary.LittleEndian.PutUint16(tmp[22:], uint16(d.RootY))
131+ binary.LittleEndian.PutUint16(tmp[24:], uint16(d.EventX))
132+ binary.LittleEndian.PutUint16(tmp[26:], uint16(d.EventY))
133+ binary.LittleEndian.PutUint16(tmp[28:], uint16(d.State))
134+ if d.SameScreen {
135+ tmp[30:][0] = 1
136+ } else {
137+ tmp[30:][0] = 0
138+ }
139+ *dst = tmp
140+}
141+
142+// KindButtonRelease identifies ButtonRelease events in xturbo.Event.Kind.
143+const KindButtonRelease = xturbo.EventKind(5)
144+
145+// ButtonReleaseEvent shares ButtonPress's layout (eventcopy).
146+type ButtonReleaseEvent = ButtonPressEvent
147+
148+// ButtonReleaseOf views ev as ButtonRelease without checking ev.Kind.
149+func ButtonReleaseOf(ev *xturbo.Event) ButtonReleaseEvent { return ButtonPressOf(ev) }
150+
151+// ButtonReleaseEventData encodes a ButtonRelease event for SendEvent with ButtonPress's fields.
152+type ButtonReleaseEventData ButtonPressEventData
153+
154+// Put writes the event (code 5, pads and sequence zeroed) into dst.
155+func (d *ButtonReleaseEventData) Put(dst *[32]byte) { (*ButtonPressEventData)(d).Put(dst); dst[0] = 5 }
156+
157+// KindMotionNotify identifies MotionNotify events in xturbo.Event.Kind.
158+const KindMotionNotify = xturbo.EventKind(6)
159+
160+// MotionNotifyEvent is a view over a MotionNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
161+type MotionNotifyEvent struct{ raw *[32]byte }
162+
163+// MotionNotifyOf views ev as MotionNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
164+func MotionNotifyOf(ev *xturbo.Event) MotionNotifyEvent { return MotionNotifyEvent{raw: &ev.Raw} }
165+func (e MotionNotifyEvent) Raw() *[32]byte { return e.raw }
166+func (e MotionNotifyEvent) Detail() Motion { return Motion(e.raw[1:][0]) }
167+func (e MotionNotifyEvent) Time() Timestamp { return Timestamp(binary.LittleEndian.Uint32(e.raw[4:])) }
168+func (e MotionNotifyEvent) Root() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
169+func (e MotionNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[12:])) }
170+func (e MotionNotifyEvent) Child() Window { return Window(binary.LittleEndian.Uint32(e.raw[16:])) }
171+func (e MotionNotifyEvent) RootX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[20:])) }
172+func (e MotionNotifyEvent) RootY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[22:])) }
173+func (e MotionNotifyEvent) EventX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[24:])) }
174+func (e MotionNotifyEvent) EventY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[26:])) }
175+func (e MotionNotifyEvent) State() KeyButMask {
176+ return KeyButMask(binary.LittleEndian.Uint16(e.raw[28:]))
177+}
178+func (e MotionNotifyEvent) SameScreen() bool { return (e.raw[30:][0] != 0) }
179+
180+// MotionNotifyEventData encodes a MotionNotify event for SendEvent.
181+type MotionNotifyEventData struct {
182+ Detail Motion
183+ Time Timestamp
184+ Root Window
185+ Event Window
186+ Child Window
187+ RootX int16
188+ RootY int16
189+ EventX int16
190+ EventY int16
191+ State KeyButMask
192+ SameScreen bool
193+}
194+
195+// Put writes the event (code 6, pads and sequence zeroed) into dst. dst may alias
196+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
197+func (d *MotionNotifyEventData) Put(dst *[32]byte) {
198+ var tmp [32]byte
199+ tmp[0] = 6
200+ tmp[1:][0] = byte(d.Detail)
201+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Time))
202+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Root))
203+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Event))
204+ binary.LittleEndian.PutUint32(tmp[16:], uint32(d.Child))
205+ binary.LittleEndian.PutUint16(tmp[20:], uint16(d.RootX))
206+ binary.LittleEndian.PutUint16(tmp[22:], uint16(d.RootY))
207+ binary.LittleEndian.PutUint16(tmp[24:], uint16(d.EventX))
208+ binary.LittleEndian.PutUint16(tmp[26:], uint16(d.EventY))
209+ binary.LittleEndian.PutUint16(tmp[28:], uint16(d.State))
210+ if d.SameScreen {
211+ tmp[30:][0] = 1
212+ } else {
213+ tmp[30:][0] = 0
214+ }
215+ *dst = tmp
216+}
217+
218+// KindEnterNotify identifies EnterNotify events in xturbo.Event.Kind.
219+const KindEnterNotify = xturbo.EventKind(7)
220+
221+// EnterNotifyEvent is a view over a EnterNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
222+type EnterNotifyEvent struct{ raw *[32]byte }
223+
224+// EnterNotifyOf views ev as EnterNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
225+func EnterNotifyOf(ev *xturbo.Event) EnterNotifyEvent { return EnterNotifyEvent{raw: &ev.Raw} }
226+func (e EnterNotifyEvent) Raw() *[32]byte { return e.raw }
227+func (e EnterNotifyEvent) Detail() NotifyDetail { return NotifyDetail(e.raw[1:][0]) }
228+func (e EnterNotifyEvent) Time() Timestamp { return Timestamp(binary.LittleEndian.Uint32(e.raw[4:])) }
229+func (e EnterNotifyEvent) Root() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
230+func (e EnterNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[12:])) }
231+func (e EnterNotifyEvent) Child() Window { return Window(binary.LittleEndian.Uint32(e.raw[16:])) }
232+func (e EnterNotifyEvent) RootX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[20:])) }
233+func (e EnterNotifyEvent) RootY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[22:])) }
234+func (e EnterNotifyEvent) EventX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[24:])) }
235+func (e EnterNotifyEvent) EventY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[26:])) }
236+func (e EnterNotifyEvent) State() KeyButMask {
237+ return KeyButMask(binary.LittleEndian.Uint16(e.raw[28:]))
238+}
239+func (e EnterNotifyEvent) Mode() NotifyMode { return NotifyMode(e.raw[30:][0]) }
240+func (e EnterNotifyEvent) SameScreenFocus() byte { return byte(e.raw[31:][0]) }
241+
242+// EnterNotifyEventData encodes a EnterNotify event for SendEvent.
243+type EnterNotifyEventData struct {
244+ Detail NotifyDetail
245+ Time Timestamp
246+ Root Window
247+ Event Window
248+ Child Window
249+ RootX int16
250+ RootY int16
251+ EventX int16
252+ EventY int16
253+ State KeyButMask
254+ Mode NotifyMode
255+ SameScreenFocus byte
256+}
257+
258+// Put writes the event (code 7, pads and sequence zeroed) into dst. dst may alias
259+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
260+func (d *EnterNotifyEventData) Put(dst *[32]byte) {
261+ var tmp [32]byte
262+ tmp[0] = 7
263+ tmp[1:][0] = byte(d.Detail)
264+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Time))
265+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Root))
266+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Event))
267+ binary.LittleEndian.PutUint32(tmp[16:], uint32(d.Child))
268+ binary.LittleEndian.PutUint16(tmp[20:], uint16(d.RootX))
269+ binary.LittleEndian.PutUint16(tmp[22:], uint16(d.RootY))
270+ binary.LittleEndian.PutUint16(tmp[24:], uint16(d.EventX))
271+ binary.LittleEndian.PutUint16(tmp[26:], uint16(d.EventY))
272+ binary.LittleEndian.PutUint16(tmp[28:], uint16(d.State))
273+ tmp[30:][0] = byte(d.Mode)
274+ tmp[31:][0] = byte(d.SameScreenFocus)
275+ *dst = tmp
276+}
277+
278+// KindLeaveNotify identifies LeaveNotify events in xturbo.Event.Kind.
279+const KindLeaveNotify = xturbo.EventKind(8)
280+
281+// LeaveNotifyEvent shares EnterNotify's layout (eventcopy).
282+type LeaveNotifyEvent = EnterNotifyEvent
283+
284+// LeaveNotifyOf views ev as LeaveNotify without checking ev.Kind.
285+func LeaveNotifyOf(ev *xturbo.Event) LeaveNotifyEvent { return EnterNotifyOf(ev) }
286+
287+// LeaveNotifyEventData encodes a LeaveNotify event for SendEvent with EnterNotify's fields.
288+type LeaveNotifyEventData EnterNotifyEventData
289+
290+// Put writes the event (code 8, pads and sequence zeroed) into dst.
291+func (d *LeaveNotifyEventData) Put(dst *[32]byte) { (*EnterNotifyEventData)(d).Put(dst); dst[0] = 8 }
292+
293+// KindFocusIn identifies FocusIn events in xturbo.Event.Kind.
294+const KindFocusIn = xturbo.EventKind(9)
295+
296+// FocusInEvent is a view over a FocusIn event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
297+type FocusInEvent struct{ raw *[32]byte }
298+
299+// FocusInOf views ev as FocusIn without checking ev.Kind: a wrong kind yields garbage, never a panic.
300+func FocusInOf(ev *xturbo.Event) FocusInEvent { return FocusInEvent{raw: &ev.Raw} }
301+func (e FocusInEvent) Raw() *[32]byte { return e.raw }
302+func (e FocusInEvent) Detail() NotifyDetail { return NotifyDetail(e.raw[1:][0]) }
303+func (e FocusInEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
304+func (e FocusInEvent) Mode() NotifyMode { return NotifyMode(e.raw[8:][0]) }
305+
306+// FocusInEventData encodes a FocusIn event for SendEvent.
307+type FocusInEventData struct {
308+ Detail NotifyDetail
309+ Event Window
310+ Mode NotifyMode
311+}
312+
313+// Put writes the event (code 9, pads and sequence zeroed) into dst. dst may alias
314+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
315+func (d *FocusInEventData) Put(dst *[32]byte) {
316+ var tmp [32]byte
317+ tmp[0] = 9
318+ tmp[1:][0] = byte(d.Detail)
319+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Event))
320+ tmp[8:][0] = byte(d.Mode)
321+ *dst = tmp
322+}
323+
324+// KindFocusOut identifies FocusOut events in xturbo.Event.Kind.
325+const KindFocusOut = xturbo.EventKind(10)
326+
327+// FocusOutEvent shares FocusIn's layout (eventcopy).
328+type FocusOutEvent = FocusInEvent
329+
330+// FocusOutOf views ev as FocusOut without checking ev.Kind.
331+func FocusOutOf(ev *xturbo.Event) FocusOutEvent { return FocusInOf(ev) }
332+
333+// FocusOutEventData encodes a FocusOut event for SendEvent with FocusIn's fields.
334+type FocusOutEventData FocusInEventData
335+
336+// Put writes the event (code 10, pads and sequence zeroed) into dst.
337+func (d *FocusOutEventData) Put(dst *[32]byte) { (*FocusInEventData)(d).Put(dst); dst[0] = 10 }
338+
339+// KindKeymapNotify identifies KeymapNotify events in xturbo.Event.Kind.
340+const KindKeymapNotify = xturbo.EventKind(11)
341+
342+// KeymapNotifyEvent is a view over a KeymapNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
343+type KeymapNotifyEvent struct{ raw *[32]byte }
344+
345+// KeymapNotifyOf views ev as KeymapNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
346+func KeymapNotifyOf(ev *xturbo.Event) KeymapNotifyEvent { return KeymapNotifyEvent{raw: &ev.Raw} }
347+func (e KeymapNotifyEvent) Raw() *[32]byte { return e.raw }
348+func (e KeymapNotifyEvent) Keys() []byte { off := 1; end := off + 31; return e.raw[off:end:end] }
349+
350+// KeymapNotifyEventData encodes a KeymapNotify event for SendEvent.
351+type KeymapNotifyEventData struct {
352+ Keys [31]uint8
353+}
354+
355+// Put writes the event (code 11, pads and sequence zeroed) into dst. dst may alias
356+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
357+func (d *KeymapNotifyEventData) Put(dst *[32]byte) {
358+ var tmp [32]byte
359+ tmp[0] = 11
360+ for i := range d.Keys {
361+ tmp[1+i*1:][0] = byte(d.Keys[i])
362+ }
363+ *dst = tmp
364+}
365+
366+// KindExpose identifies Expose events in xturbo.Event.Kind.
367+const KindExpose = xturbo.EventKind(12)
368+
369+// ExposeEvent is a view over a Expose event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
370+type ExposeEvent struct{ raw *[32]byte }
371+
372+// ExposeOf views ev as Expose without checking ev.Kind: a wrong kind yields garbage, never a panic.
373+func ExposeOf(ev *xturbo.Event) ExposeEvent { return ExposeEvent{raw: &ev.Raw} }
374+func (e ExposeEvent) Raw() *[32]byte { return e.raw }
375+func (e ExposeEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
376+func (e ExposeEvent) X() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[8:])) }
377+func (e ExposeEvent) Y() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[10:])) }
378+func (e ExposeEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[12:])) }
379+func (e ExposeEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[14:])) }
380+func (e ExposeEvent) Count() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[16:])) }
381+
382+// ExposeEventData encodes a Expose event for SendEvent.
383+type ExposeEventData struct {
384+ Window Window
385+ X uint16
386+ Y uint16
387+ Width uint16
388+ Height uint16
389+ Count uint16
390+}
391+
392+// Put writes the event (code 12, pads and sequence zeroed) into dst. dst may alias
393+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
394+func (d *ExposeEventData) Put(dst *[32]byte) {
395+ var tmp [32]byte
396+ tmp[0] = 12
397+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Window))
398+ binary.LittleEndian.PutUint16(tmp[8:], uint16(d.X))
399+ binary.LittleEndian.PutUint16(tmp[10:], uint16(d.Y))
400+ binary.LittleEndian.PutUint16(tmp[12:], uint16(d.Width))
401+ binary.LittleEndian.PutUint16(tmp[14:], uint16(d.Height))
402+ binary.LittleEndian.PutUint16(tmp[16:], uint16(d.Count))
403+ *dst = tmp
404+}
405+
406+// KindGraphicsExposure identifies GraphicsExposure events in xturbo.Event.Kind.
407+const KindGraphicsExposure = xturbo.EventKind(13)
408+
409+// GraphicsExposureEvent is a view over a GraphicsExposure event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
410+type GraphicsExposureEvent struct{ raw *[32]byte }
411+
412+// GraphicsExposureOf views ev as GraphicsExposure without checking ev.Kind: a wrong kind yields garbage, never a panic.
413+func GraphicsExposureOf(ev *xturbo.Event) GraphicsExposureEvent {
414+ return GraphicsExposureEvent{raw: &ev.Raw}
415+}
416+func (e GraphicsExposureEvent) Raw() *[32]byte { return e.raw }
417+func (e GraphicsExposureEvent) Drawable() Drawable {
418+ return Drawable(binary.LittleEndian.Uint32(e.raw[4:]))
419+}
420+func (e GraphicsExposureEvent) X() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[8:])) }
421+func (e GraphicsExposureEvent) Y() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[10:])) }
422+func (e GraphicsExposureEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[12:])) }
423+func (e GraphicsExposureEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[14:])) }
424+func (e GraphicsExposureEvent) MinorOpcode() uint16 {
425+ return uint16(binary.LittleEndian.Uint16(e.raw[16:]))
426+}
427+func (e GraphicsExposureEvent) Count() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[18:])) }
428+func (e GraphicsExposureEvent) MajorOpcode() uint8 { return uint8(e.raw[20:][0]) }
429+
430+// GraphicsExposureEventData encodes a GraphicsExposure event for SendEvent.
431+type GraphicsExposureEventData struct {
432+ Drawable Drawable
433+ X uint16
434+ Y uint16
435+ Width uint16
436+ Height uint16
437+ MinorOpcode uint16
438+ Count uint16
439+ MajorOpcode uint8
440+}
441+
442+// Put writes the event (code 13, pads and sequence zeroed) into dst. dst may alias
443+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
444+func (d *GraphicsExposureEventData) Put(dst *[32]byte) {
445+ var tmp [32]byte
446+ tmp[0] = 13
447+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Drawable))
448+ binary.LittleEndian.PutUint16(tmp[8:], uint16(d.X))
449+ binary.LittleEndian.PutUint16(tmp[10:], uint16(d.Y))
450+ binary.LittleEndian.PutUint16(tmp[12:], uint16(d.Width))
451+ binary.LittleEndian.PutUint16(tmp[14:], uint16(d.Height))
452+ binary.LittleEndian.PutUint16(tmp[16:], uint16(d.MinorOpcode))
453+ binary.LittleEndian.PutUint16(tmp[18:], uint16(d.Count))
454+ tmp[20:][0] = byte(d.MajorOpcode)
455+ *dst = tmp
456+}
457+
458+// KindNoExposure identifies NoExposure events in xturbo.Event.Kind.
459+const KindNoExposure = xturbo.EventKind(14)
460+
461+// NoExposureEvent is a view over a NoExposure event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
462+type NoExposureEvent struct{ raw *[32]byte }
463+
464+// NoExposureOf views ev as NoExposure without checking ev.Kind: a wrong kind yields garbage, never a panic.
465+func NoExposureOf(ev *xturbo.Event) NoExposureEvent { return NoExposureEvent{raw: &ev.Raw} }
466+func (e NoExposureEvent) Raw() *[32]byte { return e.raw }
467+func (e NoExposureEvent) Drawable() Drawable { return Drawable(binary.LittleEndian.Uint32(e.raw[4:])) }
468+func (e NoExposureEvent) MinorOpcode() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[8:])) }
469+func (e NoExposureEvent) MajorOpcode() uint8 { return uint8(e.raw[10:][0]) }
470+
471+// NoExposureEventData encodes a NoExposure event for SendEvent.
472+type NoExposureEventData struct {
473+ Drawable Drawable
474+ MinorOpcode uint16
475+ MajorOpcode uint8
476+}
477+
478+// Put writes the event (code 14, pads and sequence zeroed) into dst. dst may alias
479+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
480+func (d *NoExposureEventData) Put(dst *[32]byte) {
481+ var tmp [32]byte
482+ tmp[0] = 14
483+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Drawable))
484+ binary.LittleEndian.PutUint16(tmp[8:], uint16(d.MinorOpcode))
485+ tmp[10:][0] = byte(d.MajorOpcode)
486+ *dst = tmp
487+}
488+
489+// KindVisibilityNotify identifies VisibilityNotify events in xturbo.Event.Kind.
490+const KindVisibilityNotify = xturbo.EventKind(15)
491+
492+// VisibilityNotifyEvent is a view over a VisibilityNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
493+type VisibilityNotifyEvent struct{ raw *[32]byte }
494+
495+// VisibilityNotifyOf views ev as VisibilityNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
496+func VisibilityNotifyOf(ev *xturbo.Event) VisibilityNotifyEvent {
497+ return VisibilityNotifyEvent{raw: &ev.Raw}
498+}
499+func (e VisibilityNotifyEvent) Raw() *[32]byte { return e.raw }
500+func (e VisibilityNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
501+func (e VisibilityNotifyEvent) State() Visibility { return Visibility(e.raw[8:][0]) }
502+
503+// VisibilityNotifyEventData encodes a VisibilityNotify event for SendEvent.
504+type VisibilityNotifyEventData struct {
505+ Window Window
506+ State Visibility
507+}
508+
509+// Put writes the event (code 15, pads and sequence zeroed) into dst. dst may alias
510+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
511+func (d *VisibilityNotifyEventData) Put(dst *[32]byte) {
512+ var tmp [32]byte
513+ tmp[0] = 15
514+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Window))
515+ tmp[8:][0] = byte(d.State)
516+ *dst = tmp
517+}
518+
519+// KindCreateNotify identifies CreateNotify events in xturbo.Event.Kind.
520+const KindCreateNotify = xturbo.EventKind(16)
521+
522+// CreateNotifyEvent is a view over a CreateNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
523+type CreateNotifyEvent struct{ raw *[32]byte }
524+
525+// CreateNotifyOf views ev as CreateNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
526+func CreateNotifyOf(ev *xturbo.Event) CreateNotifyEvent { return CreateNotifyEvent{raw: &ev.Raw} }
527+func (e CreateNotifyEvent) Raw() *[32]byte { return e.raw }
528+func (e CreateNotifyEvent) Parent() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
529+func (e CreateNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
530+func (e CreateNotifyEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[12:])) }
531+func (e CreateNotifyEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[14:])) }
532+func (e CreateNotifyEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[16:])) }
533+func (e CreateNotifyEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[18:])) }
534+func (e CreateNotifyEvent) BorderWidth() uint16 {
535+ return uint16(binary.LittleEndian.Uint16(e.raw[20:]))
536+}
537+func (e CreateNotifyEvent) OverrideRedirect() bool { return (e.raw[22:][0] != 0) }
538+
539+// CreateNotifyEventData encodes a CreateNotify event for SendEvent.
540+type CreateNotifyEventData struct {
541+ Parent Window
542+ Window Window
543+ X int16
544+ Y int16
545+ Width uint16
546+ Height uint16
547+ BorderWidth uint16
548+ OverrideRedirect bool
549+}
550+
551+// Put writes the event (code 16, pads and sequence zeroed) into dst. dst may alias
552+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
553+func (d *CreateNotifyEventData) Put(dst *[32]byte) {
554+ var tmp [32]byte
555+ tmp[0] = 16
556+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Parent))
557+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
558+ binary.LittleEndian.PutUint16(tmp[12:], uint16(d.X))
559+ binary.LittleEndian.PutUint16(tmp[14:], uint16(d.Y))
560+ binary.LittleEndian.PutUint16(tmp[16:], uint16(d.Width))
561+ binary.LittleEndian.PutUint16(tmp[18:], uint16(d.Height))
562+ binary.LittleEndian.PutUint16(tmp[20:], uint16(d.BorderWidth))
563+ if d.OverrideRedirect {
564+ tmp[22:][0] = 1
565+ } else {
566+ tmp[22:][0] = 0
567+ }
568+ *dst = tmp
569+}
570+
571+// KindDestroyNotify identifies DestroyNotify events in xturbo.Event.Kind.
572+const KindDestroyNotify = xturbo.EventKind(17)
573+
574+// DestroyNotifyEvent is a view over a DestroyNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
575+type DestroyNotifyEvent struct{ raw *[32]byte }
576+
577+// DestroyNotifyOf views ev as DestroyNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
578+func DestroyNotifyOf(ev *xturbo.Event) DestroyNotifyEvent { return DestroyNotifyEvent{raw: &ev.Raw} }
579+func (e DestroyNotifyEvent) Raw() *[32]byte { return e.raw }
580+func (e DestroyNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
581+func (e DestroyNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
582+
583+// DestroyNotifyEventData encodes a DestroyNotify event for SendEvent.
584+type DestroyNotifyEventData struct {
585+ Event Window
586+ Window Window
587+}
588+
589+// Put writes the event (code 17, pads and sequence zeroed) into dst. dst may alias
590+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
591+func (d *DestroyNotifyEventData) Put(dst *[32]byte) {
592+ var tmp [32]byte
593+ tmp[0] = 17
594+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Event))
595+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
596+ *dst = tmp
597+}
598+
599+// KindUnmapNotify identifies UnmapNotify events in xturbo.Event.Kind.
600+const KindUnmapNotify = xturbo.EventKind(18)
601+
602+// UnmapNotifyEvent is a view over a UnmapNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
603+type UnmapNotifyEvent struct{ raw *[32]byte }
604+
605+// UnmapNotifyOf views ev as UnmapNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
606+func UnmapNotifyOf(ev *xturbo.Event) UnmapNotifyEvent { return UnmapNotifyEvent{raw: &ev.Raw} }
607+func (e UnmapNotifyEvent) Raw() *[32]byte { return e.raw }
608+func (e UnmapNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
609+func (e UnmapNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
610+func (e UnmapNotifyEvent) FromConfigure() bool { return (e.raw[12:][0] != 0) }
611+
612+// UnmapNotifyEventData encodes a UnmapNotify event for SendEvent.
613+type UnmapNotifyEventData struct {
614+ Event Window
615+ Window Window
616+ FromConfigure bool
617+}
618+
619+// Put writes the event (code 18, pads and sequence zeroed) into dst. dst may alias
620+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
621+func (d *UnmapNotifyEventData) Put(dst *[32]byte) {
622+ var tmp [32]byte
623+ tmp[0] = 18
624+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Event))
625+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
626+ if d.FromConfigure {
627+ tmp[12:][0] = 1
628+ } else {
629+ tmp[12:][0] = 0
630+ }
631+ *dst = tmp
632+}
633+
634+// KindMapNotify identifies MapNotify events in xturbo.Event.Kind.
635+const KindMapNotify = xturbo.EventKind(19)
636+
637+// MapNotifyEvent is a view over a MapNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
638+type MapNotifyEvent struct{ raw *[32]byte }
639+
640+// MapNotifyOf views ev as MapNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
641+func MapNotifyOf(ev *xturbo.Event) MapNotifyEvent { return MapNotifyEvent{raw: &ev.Raw} }
642+func (e MapNotifyEvent) Raw() *[32]byte { return e.raw }
643+func (e MapNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
644+func (e MapNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
645+func (e MapNotifyEvent) OverrideRedirect() bool { return (e.raw[12:][0] != 0) }
646+
647+// MapNotifyEventData encodes a MapNotify event for SendEvent.
648+type MapNotifyEventData struct {
649+ Event Window
650+ Window Window
651+ OverrideRedirect bool
652+}
653+
654+// Put writes the event (code 19, pads and sequence zeroed) into dst. dst may alias
655+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
656+func (d *MapNotifyEventData) Put(dst *[32]byte) {
657+ var tmp [32]byte
658+ tmp[0] = 19
659+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Event))
660+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
661+ if d.OverrideRedirect {
662+ tmp[12:][0] = 1
663+ } else {
664+ tmp[12:][0] = 0
665+ }
666+ *dst = tmp
667+}
668+
669+// KindMapRequest identifies MapRequest events in xturbo.Event.Kind.
670+const KindMapRequest = xturbo.EventKind(20)
671+
672+// MapRequestEvent is a view over a MapRequest event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
673+type MapRequestEvent struct{ raw *[32]byte }
674+
675+// MapRequestOf views ev as MapRequest without checking ev.Kind: a wrong kind yields garbage, never a panic.
676+func MapRequestOf(ev *xturbo.Event) MapRequestEvent { return MapRequestEvent{raw: &ev.Raw} }
677+func (e MapRequestEvent) Raw() *[32]byte { return e.raw }
678+func (e MapRequestEvent) Parent() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
679+func (e MapRequestEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
680+
681+// MapRequestEventData encodes a MapRequest event for SendEvent.
682+type MapRequestEventData struct {
683+ Parent Window
684+ Window Window
685+}
686+
687+// Put writes the event (code 20, pads and sequence zeroed) into dst. dst may alias
688+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
689+func (d *MapRequestEventData) Put(dst *[32]byte) {
690+ var tmp [32]byte
691+ tmp[0] = 20
692+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Parent))
693+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
694+ *dst = tmp
695+}
696+
697+// KindReparentNotify identifies ReparentNotify events in xturbo.Event.Kind.
698+const KindReparentNotify = xturbo.EventKind(21)
699+
700+// ReparentNotifyEvent is a view over a ReparentNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
701+type ReparentNotifyEvent struct{ raw *[32]byte }
702+
703+// ReparentNotifyOf views ev as ReparentNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
704+func ReparentNotifyOf(ev *xturbo.Event) ReparentNotifyEvent { return ReparentNotifyEvent{raw: &ev.Raw} }
705+func (e ReparentNotifyEvent) Raw() *[32]byte { return e.raw }
706+func (e ReparentNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
707+func (e ReparentNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
708+func (e ReparentNotifyEvent) Parent() Window { return Window(binary.LittleEndian.Uint32(e.raw[12:])) }
709+func (e ReparentNotifyEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[16:])) }
710+func (e ReparentNotifyEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[18:])) }
711+func (e ReparentNotifyEvent) OverrideRedirect() bool { return (e.raw[20:][0] != 0) }
712+
713+// ReparentNotifyEventData encodes a ReparentNotify event for SendEvent.
714+type ReparentNotifyEventData struct {
715+ Event Window
716+ Window Window
717+ Parent Window
718+ X int16
719+ Y int16
720+ OverrideRedirect bool
721+}
722+
723+// Put writes the event (code 21, pads and sequence zeroed) into dst. dst may alias
724+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
725+func (d *ReparentNotifyEventData) Put(dst *[32]byte) {
726+ var tmp [32]byte
727+ tmp[0] = 21
728+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Event))
729+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
730+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Parent))
731+ binary.LittleEndian.PutUint16(tmp[16:], uint16(d.X))
732+ binary.LittleEndian.PutUint16(tmp[18:], uint16(d.Y))
733+ if d.OverrideRedirect {
734+ tmp[20:][0] = 1
735+ } else {
736+ tmp[20:][0] = 0
737+ }
738+ *dst = tmp
739+}
740+
741+// KindConfigureNotify identifies ConfigureNotify events in xturbo.Event.Kind.
742+const KindConfigureNotify = xturbo.EventKind(22)
743+
744+// ConfigureNotifyEvent is a view over a ConfigureNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
745+type ConfigureNotifyEvent struct{ raw *[32]byte }
746+
747+// ConfigureNotifyOf views ev as ConfigureNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
748+func ConfigureNotifyOf(ev *xturbo.Event) ConfigureNotifyEvent {
749+ return ConfigureNotifyEvent{raw: &ev.Raw}
750+}
751+func (e ConfigureNotifyEvent) Raw() *[32]byte { return e.raw }
752+func (e ConfigureNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
753+func (e ConfigureNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
754+func (e ConfigureNotifyEvent) AboveSibling() Window {
755+ return Window(binary.LittleEndian.Uint32(e.raw[12:]))
756+}
757+func (e ConfigureNotifyEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[16:])) }
758+func (e ConfigureNotifyEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[18:])) }
759+func (e ConfigureNotifyEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[20:])) }
760+func (e ConfigureNotifyEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[22:])) }
761+func (e ConfigureNotifyEvent) BorderWidth() uint16 {
762+ return uint16(binary.LittleEndian.Uint16(e.raw[24:]))
763+}
764+func (e ConfigureNotifyEvent) OverrideRedirect() bool { return (e.raw[26:][0] != 0) }
765+
766+// ConfigureNotifyEventData encodes a ConfigureNotify event for SendEvent.
767+type ConfigureNotifyEventData struct {
768+ Event Window
769+ Window Window
770+ AboveSibling Window
771+ X int16
772+ Y int16
773+ Width uint16
774+ Height uint16
775+ BorderWidth uint16
776+ OverrideRedirect bool
777+}
778+
779+// Put writes the event (code 22, pads and sequence zeroed) into dst. dst may alias
780+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
781+func (d *ConfigureNotifyEventData) Put(dst *[32]byte) {
782+ var tmp [32]byte
783+ tmp[0] = 22
784+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Event))
785+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
786+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.AboveSibling))
787+ binary.LittleEndian.PutUint16(tmp[16:], uint16(d.X))
788+ binary.LittleEndian.PutUint16(tmp[18:], uint16(d.Y))
789+ binary.LittleEndian.PutUint16(tmp[20:], uint16(d.Width))
790+ binary.LittleEndian.PutUint16(tmp[22:], uint16(d.Height))
791+ binary.LittleEndian.PutUint16(tmp[24:], uint16(d.BorderWidth))
792+ if d.OverrideRedirect {
793+ tmp[26:][0] = 1
794+ } else {
795+ tmp[26:][0] = 0
796+ }
797+ *dst = tmp
798+}
799+
800+// KindConfigureRequest identifies ConfigureRequest events in xturbo.Event.Kind.
801+const KindConfigureRequest = xturbo.EventKind(23)
802+
803+// ConfigureRequestEvent is a view over a ConfigureRequest event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
804+type ConfigureRequestEvent struct{ raw *[32]byte }
805+
806+// ConfigureRequestOf views ev as ConfigureRequest without checking ev.Kind: a wrong kind yields garbage, never a panic.
807+func ConfigureRequestOf(ev *xturbo.Event) ConfigureRequestEvent {
808+ return ConfigureRequestEvent{raw: &ev.Raw}
809+}
810+func (e ConfigureRequestEvent) Raw() *[32]byte { return e.raw }
811+func (e ConfigureRequestEvent) StackMode() StackMode { return StackMode(e.raw[1:][0]) }
812+func (e ConfigureRequestEvent) Parent() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
813+func (e ConfigureRequestEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
814+func (e ConfigureRequestEvent) Sibling() Window {
815+ return Window(binary.LittleEndian.Uint32(e.raw[12:]))
816+}
817+func (e ConfigureRequestEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[16:])) }
818+func (e ConfigureRequestEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[18:])) }
819+func (e ConfigureRequestEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[20:])) }
820+func (e ConfigureRequestEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[22:])) }
821+func (e ConfigureRequestEvent) BorderWidth() uint16 {
822+ return uint16(binary.LittleEndian.Uint16(e.raw[24:]))
823+}
824+func (e ConfigureRequestEvent) ValueMask() ConfigWindow {
825+ return ConfigWindow(binary.LittleEndian.Uint16(e.raw[26:]))
826+}
827+
828+// ConfigureRequestEventData encodes a ConfigureRequest event for SendEvent.
829+type ConfigureRequestEventData struct {
830+ StackMode StackMode
831+ Parent Window
832+ Window Window
833+ Sibling Window
834+ X int16
835+ Y int16
836+ Width uint16
837+ Height uint16
838+ BorderWidth uint16
839+ ValueMask ConfigWindow
840+}
841+
842+// Put writes the event (code 23, pads and sequence zeroed) into dst. dst may alias
843+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
844+func (d *ConfigureRequestEventData) Put(dst *[32]byte) {
845+ var tmp [32]byte
846+ tmp[0] = 23
847+ tmp[1:][0] = byte(d.StackMode)
848+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Parent))
849+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
850+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Sibling))
851+ binary.LittleEndian.PutUint16(tmp[16:], uint16(d.X))
852+ binary.LittleEndian.PutUint16(tmp[18:], uint16(d.Y))
853+ binary.LittleEndian.PutUint16(tmp[20:], uint16(d.Width))
854+ binary.LittleEndian.PutUint16(tmp[22:], uint16(d.Height))
855+ binary.LittleEndian.PutUint16(tmp[24:], uint16(d.BorderWidth))
856+ binary.LittleEndian.PutUint16(tmp[26:], uint16(d.ValueMask))
857+ *dst = tmp
858+}
859+
860+// KindGravityNotify identifies GravityNotify events in xturbo.Event.Kind.
861+const KindGravityNotify = xturbo.EventKind(24)
862+
863+// GravityNotifyEvent is a view over a GravityNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
864+type GravityNotifyEvent struct{ raw *[32]byte }
865+
866+// GravityNotifyOf views ev as GravityNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
867+func GravityNotifyOf(ev *xturbo.Event) GravityNotifyEvent { return GravityNotifyEvent{raw: &ev.Raw} }
868+func (e GravityNotifyEvent) Raw() *[32]byte { return e.raw }
869+func (e GravityNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
870+func (e GravityNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
871+func (e GravityNotifyEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[12:])) }
872+func (e GravityNotifyEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[14:])) }
873+
874+// GravityNotifyEventData encodes a GravityNotify event for SendEvent.
875+type GravityNotifyEventData struct {
876+ Event Window
877+ Window Window
878+ X int16
879+ Y int16
880+}
881+
882+// Put writes the event (code 24, pads and sequence zeroed) into dst. dst may alias
883+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
884+func (d *GravityNotifyEventData) Put(dst *[32]byte) {
885+ var tmp [32]byte
886+ tmp[0] = 24
887+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Event))
888+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
889+ binary.LittleEndian.PutUint16(tmp[12:], uint16(d.X))
890+ binary.LittleEndian.PutUint16(tmp[14:], uint16(d.Y))
891+ *dst = tmp
892+}
893+
894+// KindResizeRequest identifies ResizeRequest events in xturbo.Event.Kind.
895+const KindResizeRequest = xturbo.EventKind(25)
896+
897+// ResizeRequestEvent is a view over a ResizeRequest event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
898+type ResizeRequestEvent struct{ raw *[32]byte }
899+
900+// ResizeRequestOf views ev as ResizeRequest without checking ev.Kind: a wrong kind yields garbage, never a panic.
901+func ResizeRequestOf(ev *xturbo.Event) ResizeRequestEvent { return ResizeRequestEvent{raw: &ev.Raw} }
902+func (e ResizeRequestEvent) Raw() *[32]byte { return e.raw }
903+func (e ResizeRequestEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
904+func (e ResizeRequestEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[8:])) }
905+func (e ResizeRequestEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[10:])) }
906+
907+// ResizeRequestEventData encodes a ResizeRequest event for SendEvent.
908+type ResizeRequestEventData struct {
909+ Window Window
910+ Width uint16
911+ Height uint16
912+}
913+
914+// Put writes the event (code 25, pads and sequence zeroed) into dst. dst may alias
915+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
916+func (d *ResizeRequestEventData) Put(dst *[32]byte) {
917+ var tmp [32]byte
918+ tmp[0] = 25
919+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Window))
920+ binary.LittleEndian.PutUint16(tmp[8:], uint16(d.Width))
921+ binary.LittleEndian.PutUint16(tmp[10:], uint16(d.Height))
922+ *dst = tmp
923+}
924+
925+// KindCirculateNotify identifies CirculateNotify events in xturbo.Event.Kind.
926+const KindCirculateNotify = xturbo.EventKind(26)
927+
928+// CirculateNotifyEvent is a view over a CirculateNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
929+type CirculateNotifyEvent struct{ raw *[32]byte }
930+
931+// CirculateNotifyOf views ev as CirculateNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
932+func CirculateNotifyOf(ev *xturbo.Event) CirculateNotifyEvent {
933+ return CirculateNotifyEvent{raw: &ev.Raw}
934+}
935+func (e CirculateNotifyEvent) Raw() *[32]byte { return e.raw }
936+func (e CirculateNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
937+func (e CirculateNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
938+func (e CirculateNotifyEvent) Place() Place { return Place(e.raw[16:][0]) }
939+
940+// CirculateNotifyEventData encodes a CirculateNotify event for SendEvent.
941+type CirculateNotifyEventData struct {
942+ Event Window
943+ Window Window
944+ Place Place
945+}
946+
947+// Put writes the event (code 26, pads and sequence zeroed) into dst. dst may alias
948+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
949+func (d *CirculateNotifyEventData) Put(dst *[32]byte) {
950+ var tmp [32]byte
951+ tmp[0] = 26
952+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Event))
953+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Window))
954+ tmp[16:][0] = byte(d.Place)
955+ *dst = tmp
956+}
957+
958+// KindCirculateRequest identifies CirculateRequest events in xturbo.Event.Kind.
959+const KindCirculateRequest = xturbo.EventKind(27)
960+
961+// CirculateRequestEvent shares CirculateNotify's layout (eventcopy).
962+type CirculateRequestEvent = CirculateNotifyEvent
963+
964+// CirculateRequestOf views ev as CirculateRequest without checking ev.Kind.
965+func CirculateRequestOf(ev *xturbo.Event) CirculateRequestEvent { return CirculateNotifyOf(ev) }
966+
967+// CirculateRequestEventData encodes a CirculateRequest event for SendEvent with CirculateNotify's fields.
968+type CirculateRequestEventData CirculateNotifyEventData
969+
970+// Put writes the event (code 27, pads and sequence zeroed) into dst.
971+func (d *CirculateRequestEventData) Put(dst *[32]byte) {
972+ (*CirculateNotifyEventData)(d).Put(dst)
973+ dst[0] = 27
974+}
975+
976+// KindPropertyNotify identifies PropertyNotify events in xturbo.Event.Kind.
977+const KindPropertyNotify = xturbo.EventKind(28)
978+
979+// PropertyNotifyEvent is a view over a PropertyNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
980+type PropertyNotifyEvent struct{ raw *[32]byte }
981+
982+// PropertyNotifyOf views ev as PropertyNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
983+func PropertyNotifyOf(ev *xturbo.Event) PropertyNotifyEvent { return PropertyNotifyEvent{raw: &ev.Raw} }
984+func (e PropertyNotifyEvent) Raw() *[32]byte { return e.raw }
985+func (e PropertyNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
986+func (e PropertyNotifyEvent) Atom() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[8:])) }
987+func (e PropertyNotifyEvent) Time() Timestamp {
988+ return Timestamp(binary.LittleEndian.Uint32(e.raw[12:]))
989+}
990+func (e PropertyNotifyEvent) State() Property { return Property(e.raw[16:][0]) }
991+
992+// PropertyNotifyEventData encodes a PropertyNotify event for SendEvent.
993+type PropertyNotifyEventData struct {
994+ Window Window
995+ Atom Atom
996+ Time Timestamp
997+ State Property
998+}
999+
1000+// Put writes the event (code 28, pads and sequence zeroed) into dst. dst may alias
1001+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
1002+func (d *PropertyNotifyEventData) Put(dst *[32]byte) {
1003+ var tmp [32]byte
1004+ tmp[0] = 28
1005+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Window))
1006+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Atom))
1007+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Time))
1008+ tmp[16:][0] = byte(d.State)
1009+ *dst = tmp
1010+}
1011+
1012+// KindSelectionClear identifies SelectionClear events in xturbo.Event.Kind.
1013+const KindSelectionClear = xturbo.EventKind(29)
1014+
1015+// SelectionClearEvent is a view over a SelectionClear event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
1016+type SelectionClearEvent struct{ raw *[32]byte }
1017+
1018+// SelectionClearOf views ev as SelectionClear without checking ev.Kind: a wrong kind yields garbage, never a panic.
1019+func SelectionClearOf(ev *xturbo.Event) SelectionClearEvent { return SelectionClearEvent{raw: &ev.Raw} }
1020+func (e SelectionClearEvent) Raw() *[32]byte { return e.raw }
1021+func (e SelectionClearEvent) Time() Timestamp {
1022+ return Timestamp(binary.LittleEndian.Uint32(e.raw[4:]))
1023+}
1024+func (e SelectionClearEvent) Owner() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
1025+func (e SelectionClearEvent) Selection() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[12:])) }
1026+
1027+// SelectionClearEventData encodes a SelectionClear event for SendEvent.
1028+type SelectionClearEventData struct {
1029+ Time Timestamp
1030+ Owner Window
1031+ Selection Atom
1032+}
1033+
1034+// Put writes the event (code 29, pads and sequence zeroed) into dst. dst may alias
1035+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
1036+func (d *SelectionClearEventData) Put(dst *[32]byte) {
1037+ var tmp [32]byte
1038+ tmp[0] = 29
1039+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Time))
1040+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Owner))
1041+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Selection))
1042+ *dst = tmp
1043+}
1044+
1045+// KindSelectionRequest identifies SelectionRequest events in xturbo.Event.Kind.
1046+const KindSelectionRequest = xturbo.EventKind(30)
1047+
1048+// SelectionRequestEvent is a view over a SelectionRequest event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
1049+type SelectionRequestEvent struct{ raw *[32]byte }
1050+
1051+// SelectionRequestOf views ev as SelectionRequest without checking ev.Kind: a wrong kind yields garbage, never a panic.
1052+func SelectionRequestOf(ev *xturbo.Event) SelectionRequestEvent {
1053+ return SelectionRequestEvent{raw: &ev.Raw}
1054+}
1055+func (e SelectionRequestEvent) Raw() *[32]byte { return e.raw }
1056+func (e SelectionRequestEvent) Time() Timestamp {
1057+ return Timestamp(binary.LittleEndian.Uint32(e.raw[4:]))
1058+}
1059+func (e SelectionRequestEvent) Owner() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
1060+func (e SelectionRequestEvent) Requestor() Window {
1061+ return Window(binary.LittleEndian.Uint32(e.raw[12:]))
1062+}
1063+func (e SelectionRequestEvent) Selection() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[16:])) }
1064+func (e SelectionRequestEvent) Target() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[20:])) }
1065+func (e SelectionRequestEvent) Property() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[24:])) }
1066+
1067+// SelectionRequestEventData encodes a SelectionRequest event for SendEvent.
1068+type SelectionRequestEventData struct {
1069+ Time Timestamp
1070+ Owner Window
1071+ Requestor Window
1072+ Selection Atom
1073+ Target Atom
1074+ Property Atom
1075+}
1076+
1077+// Put writes the event (code 30, pads and sequence zeroed) into dst. dst may alias
1078+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
1079+func (d *SelectionRequestEventData) Put(dst *[32]byte) {
1080+ var tmp [32]byte
1081+ tmp[0] = 30
1082+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Time))
1083+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Owner))
1084+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Requestor))
1085+ binary.LittleEndian.PutUint32(tmp[16:], uint32(d.Selection))
1086+ binary.LittleEndian.PutUint32(tmp[20:], uint32(d.Target))
1087+ binary.LittleEndian.PutUint32(tmp[24:], uint32(d.Property))
1088+ *dst = tmp
1089+}
1090+
1091+// KindSelectionNotify identifies SelectionNotify events in xturbo.Event.Kind.
1092+const KindSelectionNotify = xturbo.EventKind(31)
1093+
1094+// SelectionNotifyEvent is a view over a SelectionNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
1095+type SelectionNotifyEvent struct{ raw *[32]byte }
1096+
1097+// SelectionNotifyOf views ev as SelectionNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
1098+func SelectionNotifyOf(ev *xturbo.Event) SelectionNotifyEvent {
1099+ return SelectionNotifyEvent{raw: &ev.Raw}
1100+}
1101+func (e SelectionNotifyEvent) Raw() *[32]byte { return e.raw }
1102+func (e SelectionNotifyEvent) Time() Timestamp {
1103+ return Timestamp(binary.LittleEndian.Uint32(e.raw[4:]))
1104+}
1105+func (e SelectionNotifyEvent) Requestor() Window {
1106+ return Window(binary.LittleEndian.Uint32(e.raw[8:]))
1107+}
1108+func (e SelectionNotifyEvent) Selection() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[12:])) }
1109+func (e SelectionNotifyEvent) Target() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[16:])) }
1110+func (e SelectionNotifyEvent) Property() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[20:])) }
1111+
1112+// SelectionNotifyEventData encodes a SelectionNotify event for SendEvent.
1113+type SelectionNotifyEventData struct {
1114+ Time Timestamp
1115+ Requestor Window
1116+ Selection Atom
1117+ Target Atom
1118+ Property Atom
1119+}
1120+
1121+// Put writes the event (code 31, pads and sequence zeroed) into dst. dst may alias
1122+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
1123+func (d *SelectionNotifyEventData) Put(dst *[32]byte) {
1124+ var tmp [32]byte
1125+ tmp[0] = 31
1126+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Time))
1127+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Requestor))
1128+ binary.LittleEndian.PutUint32(tmp[12:], uint32(d.Selection))
1129+ binary.LittleEndian.PutUint32(tmp[16:], uint32(d.Target))
1130+ binary.LittleEndian.PutUint32(tmp[20:], uint32(d.Property))
1131+ *dst = tmp
1132+}
1133+
1134+// KindColormapNotify identifies ColormapNotify events in xturbo.Event.Kind.
1135+const KindColormapNotify = xturbo.EventKind(32)
1136+
1137+// ColormapNotifyEvent is a view over a ColormapNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
1138+type ColormapNotifyEvent struct{ raw *[32]byte }
1139+
1140+// ColormapNotifyOf views ev as ColormapNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
1141+func ColormapNotifyOf(ev *xturbo.Event) ColormapNotifyEvent { return ColormapNotifyEvent{raw: &ev.Raw} }
1142+func (e ColormapNotifyEvent) Raw() *[32]byte { return e.raw }
1143+func (e ColormapNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
1144+func (e ColormapNotifyEvent) Colormap() Colormap {
1145+ return Colormap(binary.LittleEndian.Uint32(e.raw[8:]))
1146+}
1147+func (e ColormapNotifyEvent) New() bool { return (e.raw[12:][0] != 0) }
1148+func (e ColormapNotifyEvent) State() ColormapState { return ColormapState(e.raw[13:][0]) }
1149+
1150+// ColormapNotifyEventData encodes a ColormapNotify event for SendEvent.
1151+type ColormapNotifyEventData struct {
1152+ Window Window
1153+ Colormap Colormap
1154+ New bool
1155+ State ColormapState
1156+}
1157+
1158+// Put writes the event (code 32, pads and sequence zeroed) into dst. dst may alias
1159+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
1160+func (d *ColormapNotifyEventData) Put(dst *[32]byte) {
1161+ var tmp [32]byte
1162+ tmp[0] = 32
1163+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Window))
1164+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Colormap))
1165+ if d.New {
1166+ tmp[12:][0] = 1
1167+ } else {
1168+ tmp[12:][0] = 0
1169+ }
1170+ tmp[13:][0] = byte(d.State)
1171+ *dst = tmp
1172+}
1173+
1174+// KindClientMessage identifies ClientMessage events in xturbo.Event.Kind.
1175+const KindClientMessage = xturbo.EventKind(33)
1176+
1177+// ClientMessageEvent is a view over a ClientMessage event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
1178+type ClientMessageEvent struct{ raw *[32]byte }
1179+
1180+// ClientMessageOf views ev as ClientMessage without checking ev.Kind: a wrong kind yields garbage, never a panic.
1181+func ClientMessageOf(ev *xturbo.Event) ClientMessageEvent { return ClientMessageEvent{raw: &ev.Raw} }
1182+func (e ClientMessageEvent) Raw() *[32]byte { return e.raw }
1183+func (e ClientMessageEvent) Format() uint8 { return uint8(e.raw[1:][0]) }
1184+func (e ClientMessageEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
1185+func (e ClientMessageEvent) Type() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[8:])) }
1186+func (e ClientMessageEvent) Data() ClientMessageData { return GetClientMessageData(e.raw[12:]) }
1187+
1188+// ClientMessageEventData encodes a ClientMessage event for SendEvent.
1189+type ClientMessageEventData struct {
1190+ Format uint8
1191+ Window Window
1192+ Type Atom
1193+ Data ClientMessageData
1194+}
1195+
1196+// Put writes the event (code 33, pads and sequence zeroed) into dst. dst may alias
1197+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
1198+func (d *ClientMessageEventData) Put(dst *[32]byte) {
1199+ var tmp [32]byte
1200+ tmp[0] = 33
1201+ tmp[1:][0] = byte(d.Format)
1202+ binary.LittleEndian.PutUint32(tmp[4:], uint32(d.Window))
1203+ binary.LittleEndian.PutUint32(tmp[8:], uint32(d.Type))
1204+ d.Data.Put(tmp[12:])
1205+ *dst = tmp
1206+}
1207+
1208+// KindMappingNotify identifies MappingNotify events in xturbo.Event.Kind.
1209+const KindMappingNotify = xturbo.EventKind(34)
1210+
1211+// MappingNotifyEvent is a view over a MappingNotify event's 32 bytes. Getters read the copied Event.Raw, so a view stays valid as long as the Event value it was taken from.
1212+type MappingNotifyEvent struct{ raw *[32]byte }
1213+
1214+// MappingNotifyOf views ev as MappingNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
1215+func MappingNotifyOf(ev *xturbo.Event) MappingNotifyEvent { return MappingNotifyEvent{raw: &ev.Raw} }
1216+func (e MappingNotifyEvent) Raw() *[32]byte { return e.raw }
1217+func (e MappingNotifyEvent) Request() Mapping { return Mapping(e.raw[4:][0]) }
1218+func (e MappingNotifyEvent) FirstKeycode() Keycode { return Keycode(e.raw[5:][0]) }
1219+func (e MappingNotifyEvent) Count() uint8 { return uint8(e.raw[6:][0]) }
1220+
1221+// MappingNotifyEventData encodes a MappingNotify event for SendEvent.
1222+type MappingNotifyEventData struct {
1223+ Request Mapping
1224+ FirstKeycode Keycode
1225+ Count uint8
1226+}
1227+
1228+// Put writes the event (code 34, pads and sequence zeroed) into dst. dst may alias
1229+// memory that d's borrowed fields view, such as the Event.Raw they were read from.
1230+func (d *MappingNotifyEventData) Put(dst *[32]byte) {
1231+ var tmp [32]byte
1232+ tmp[0] = 34
1233+ tmp[4:][0] = byte(d.Request)
1234+ tmp[5:][0] = byte(d.FirstKeycode)
1235+ tmp[6:][0] = byte(d.Count)
1236+ *dst = tmp
1237+}
1238+
1239+// GeGenericEvent is a view over a GeGeneric generic event: the copied Event.Raw plus the borrowed Event.Tail.
1240+// Tail is valid only until the Handler callback returns; copy what you keep.
1241+type GeGenericEvent struct {
1242+ raw *[32]byte
1243+ tail []byte
1244+}
1245+
1246+// GeGenericOf views ev as GeGeneric; false when ev is not a generic event or Tail does not match its length.
1247+func GeGenericOf(ev *xturbo.Event) (GeGenericEvent, bool) {
1248+ if ev.Raw[0]&0x7f != 35 || len(ev.Tail) != 4*int(binary.LittleEndian.Uint32(ev.Raw[4:])) {
1249+ return GeGenericEvent{}, false
1250+ }
1251+ return GeGenericEvent{raw: &ev.Raw, tail: ev.Tail}, true
1252+}
1253+func (e GeGenericEvent) Extension() uint8 { return e.raw[1] }
1254+func (e GeGenericEvent) Length() uint32 { return binary.LittleEndian.Uint32(e.raw[4:]) }
1255+func (e GeGenericEvent) EventType() uint16 { return binary.LittleEndian.Uint16(e.raw[8:]) }
1256+func (e GeGenericEvent) Tail() []byte { return e.tail }
1257+func (e GeGenericEvent) Raw() *[32]byte { return e.raw }

Keyboard shortcuts

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

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

Branches 1

Tags 1

↑↓ moveenter openesc close