detached 7f3ee2e

feat(xtgen): decode events and errors as tagged values with views

Add the root Event value (Kind, SendEvent, Ext, Seq, copied Raw, borrowed
XGE Tail) and DecodeEvent, which frames core, extension and generic events
and resolves extension kinds through new ExtTable reverse maps (ByEvent,
ByError, ByMajor) filled from per-extension code ranges generated into
ext_generated.go. Add XError with an internal decoder for Dispatch.

Generate per event a Kind constant, a <Name>Event view over Event.Raw with
fixed-offset getters and <Name>Of; eventcopy as type and function aliases;
<Name>EventData with Put(*[32]byte) for every core event; a checked
GeGenericOf view over the XGE Tail. Generate Bad<Name> error constants and
ErrorName tables, relative to FirstError for extensions. buildContainer now
takes a wire layout (reply, struct, event, no-sequence event, XGE).

Capture ten event goldens and BadAtom, BadWindow and XFIXES BadRegion error
goldens from Xvfb through the socketpair tee, with a second raw client
triggering redirected requests; hand-write KeymapNotify and XGE framing.
Add FuzzDecodeEvent, generated FuzzEventViews with EventData round trips,
zero-alloc tests for decode, view getters, Put and SendEvent, and
BenchmarkDecodeEvents10k.
commit
7f3ee2e8cf0f3dfbcdb125e1f11500129ae4c2c8
author
brice · · 2026-10-22 07:50 UTC
committer
brice · · 2026-10-22 08:14 UTC
parent
3b98825
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
42 files · +3990 -18
M cmd/xtgen/emit.go +5 -0
@@ -241,6 +241,11 @@ func emitTypes(modules []*Module) error {
241241 var extensions []*Module
242242 for _, m := range modules {
243243 if m.ExtName != "" {
244+ count, err := eventCount(m)
245+ if err != nil {
246+ return err
247+ }
248+ m.NumEvents, m.NumErrors = count, errorCount(m)
244249 extensions = append(extensions, m)
245250 }
246251 }
A cmd/xtgen/events.go +429 -0
@@ -0,0 +1,429 @@
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("dst[%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("dst[%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+ fmt.Fprintf(&b, "// Put writes the event (code %d, pads and sequence zeroed) into dst.\nfunc (d *%sEventData) Put(dst *[32]byte) { clear(dst[:]); dst[0] = %d; %s };\n", e.Code, e.Name, e.Code, strings.Join(puts, ""))
211+ return b.String(), nil
212+}
213+
214+// errorIR is one <error> or <errorcopy>.
215+type errorIR struct {
216+ Const, Name string
217+ Code int
218+}
219+
220+func errorConst(xname string) string {
221+ return "Bad" + goName(strings.TrimPrefix(xname, "Bad"))
222+}
223+
224+func resolveErrors(m *Module) ([]errorIR, error) {
225+ var errs []errorIR
226+ seen := map[int]bool{}
227+ for _, n := range m.Root.Children {
228+ if n.Kind != "error" && n.Kind != "errorcopy" {
229+ continue
230+ }
231+ code, err := strconv.Atoi(n.Attr["number"])
232+ if err != nil || code < 0 || code > 255 || seen[code] {
233+ return nil, n.fail("invalid or duplicate error number %q", n.Attr["number"])
234+ }
235+ seen[code] = true
236+ e := errorIR{Const: errorConst(n.Attr["name"]), Code: code}
237+ e.Name = e.Const
238+ if err := m.claim(e.Const, n); err != nil {
239+ return nil, err
240+ }
241+ errs = append(errs, e)
242+ }
243+ return errs, nil
244+}
245+
246+// errorCount is one past the highest error number.
247+func errorCount(m *Module) int {
248+ count := 0
249+ for _, n := range m.Root.Children {
250+ if n.Kind == "error" || n.Kind == "errorcopy" {
251+ code, _ := strconv.Atoi(n.Attr["number"])
252+ count = max(count, code+1)
253+ }
254+ }
255+ return count
256+}
257+
258+// eventCount is one past the highest non-XGE event number.
259+func eventCount(m *Module) (int, error) {
260+ count := 0
261+ for _, n := range m.Root.Children {
262+ if (n.Kind == "event" || n.Kind == "eventcopy") && n.Attr["xge"] != "true" {
263+ code, err := eventCode(n)
264+ if err != nil {
265+ return 0, err
266+ }
267+ count = max(count, code+1)
268+ }
269+ }
270+ return count, nil
271+}
272+
273+func errorsDecl(m *Module, errs []errorIR) string {
274+ var b strings.Builder
275+ what := "wire error codes"
276+ if m.ExtName != "" {
277+ what = "error codes relative to the extension's FirstError (XError.Rel)"
278+ }
279+ fmt.Fprintf(&b, "// Error codes: %s.\nconst (\n", what)
280+ for _, e := range errs {
281+ fmt.Fprintf(&b, "%s uint8 = %d\n", e.Const, e.Code)
282+ }
283+ b.WriteString(");\n")
284+ names := make([]string, errorCount(m))
285+ for _, e := range errs {
286+ names[e.Code] = e.Name
287+ }
288+ b.WriteString("var errorNames = [...]string{")
289+ for _, n := range names {
290+ fmt.Fprintf(&b, "%q,", n)
291+ }
292+ b.WriteString("};\n")
293+ fmt.Fprintf(&b, "// ErrorName returns the name of an error code (%s), or \"\" when unknown.\n", what)
294+ b.WriteString("func ErrorName(code uint8) string { if int(code) < len(errorNames) { return errorNames[code] }; return \"\" };\n")
295+ return b.String()
296+}
297+
298+func emitEvents(modules []*Module) error {
299+ for _, m := range modules {
300+ events, err := resolveEvents(m)
301+ if err != nil {
302+ return err
303+ }
304+ lists := map[string]string{}
305+ var decls []string
306+ for _, e := range events {
307+ d, err := eventDecl(m, e, lists)
308+ if err != nil {
309+ return err
310+ }
311+ decls = append(decls, d)
312+ }
313+ var names []string
314+ for name := range lists {
315+ // Reuse a list view already declared by replies_generated.go.
316+ if old := m.Names[name]; old != nil && old == m.Root {
317+ continue
318+ }
319+ if err := m.claim(name, m.Root); err != nil {
320+ return err
321+ }
322+ names = append(names, name)
323+ }
324+ sort.Strings(names)
325+ for _, name := range names {
326+ decls = append(decls, lists[name])
327+ }
328+ errs, err := resolveErrors(m)
329+ if err != nil {
330+ return err
331+ }
332+ if len(errs) > 0 {
333+ data := struct {
334+ Pkg string
335+ Decls []string
336+ }{Pkg: m.Pkg, Decls: []string{errorsDecl(m, errs)}}
337+ if err := writeTemplate(filepath.Join(m.Pkg, "errors_generated.go"), "errors", data); err != nil {
338+ return err
339+ }
340+ }
341+ if len(decls) == 0 {
342+ continue
343+ }
344+ imports := importsFor(m, strings.Join(decls, "\n")) + "\nimport \"bnema.dev/xturbo\""
345+ decls = append([]string{imports}, decls...)
346+ data := struct {
347+ Pkg string
348+ Decls []string
349+ }{Pkg: m.Pkg, Decls: decls}
350+ if err := writeTemplate(filepath.Join(m.Pkg, "events_generated.go"), "events", data); err != nil {
351+ return err
352+ }
353+ if err := emitEventFuzz(m, events); err != nil {
354+ return err
355+ }
356+ }
357+ return nil
358+}
359+
360+// emitEventFuzz writes FuzzEventViews: every view getter runs on arbitrary
361+// bytes, and for core events EventData built from the getters survives a
362+// Put/view round trip. Seeds are the package's event goldens.
363+func emitEventFuzz(m *Module, events []*eventIR) error {
364+ var b strings.Builder
365+ b.WriteString("func FuzzEventViews(f *testing.F) {\nf.Add(make([]byte, 32));\n")
366+ goldens, _ := filepath.Glob(filepath.Join("testdata/golden", m.Pkg, "*.event.hex"))
367+ sort.Strings(goldens)
368+ for _, g := range goldens {
369+ fmt.Fprintf(&b, "if raw, err := xtest.LoadHex(%q); err != nil { f.Fatal(err) } else { f.Add(raw) };\n", "../"+filepath.ToSlash(g))
370+ }
371+ 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")
372+ for _, e := range events {
373+ if e.Copy != nil {
374+ fmt.Fprintf(&b, "_ = %sOf(&ev);\n", e.Name)
375+ continue
376+ }
377+ v := "v" + e.Name
378+ if e.XGE {
379+ 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)
380+ } else {
381+ fmt.Fprintf(&b, "{ %s := %sOf(&ev);", v, e.Name)
382+ }
383+ touch(&b, e.C, v, 0, nil, `t.Fatal("iterator error")`)
384+ if m.ExtName == "" && !e.XGE {
385+ var fields, checks []string
386+ for _, mem := range e.C.Members {
387+ if mem.Kind == "pad" {
388+ continue
389+ }
390+ name := mem.Method
391+ if name == "Put" {
392+ name += "_"
393+ }
394+ switch {
395+ case mem.Kind == "list" && mem.ListKind == "bytes":
396+ fields = append(fields, fmt.Sprintf("%s: [%d]%s(%s.%s())", name, mem.Size/mem.Type.Size, mem.Elem, v, mem.Method))
397+ checks = append(checks, fmt.Sprintf("if !bytes.Equal(w.%s(), %s.%s()) { t.Fatal(%q) };", mem.Method, v, mem.Method, e.Name+"."+mem.Method))
398+ case mem.Kind == "field" && isScalar(mem.Type):
399+ fields = append(fields, fmt.Sprintf("%s: %s.%s()", name, v, mem.Method))
400+ checks = append(checks, fmt.Sprintf("if w.%s() != %s.%s() { t.Fatal(%q) };", mem.Method, v, mem.Method, e.Name+"."+mem.Method))
401+ case mem.Kind == "field":
402+ fields = append(fields, fmt.Sprintf("%s: %s.%s()", name, v, mem.Method))
403+ 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))
404+ default:
405+ return e.Node.fail("event member %s has no EventData round trip", mem.XName)
406+ }
407+ }
408+ fmt.Fprintf(&b, "d := %sEventData{%s}; var out [32]byte; d.Put(&out);", e.Name, strings.Join(fields, ", "))
409+ fmt.Fprintf(&b, "if out[0] != %d { t.Fatal(\"%s code\") };", e.Code, e.Name)
410+ b.WriteString("back := xturbo.Event{Raw: out};")
411+ fmt.Fprintf(&b, "w := %sOf(&back); %s", e.Name, strings.Join(checks, ""))
412+ }
413+ b.WriteString("};\n")
414+ }
415+ b.WriteString("})\n}\n")
416+ body := b.String()
417+ imports := "import \"testing\"\nimport \"bnema.dev/xturbo\""
418+ if len(goldens) > 0 {
419+ imports += "\nimport \"bnema.dev/xturbo/internal/xtest\""
420+ }
421+ if strings.Contains(body, "bytes.") {
422+ imports += "\nimport \"bytes\""
423+ }
424+ data := struct {
425+ Pkg string
426+ Decls []string
427+ }{Pkg: m.Pkg, Decls: []string{imports, body}}
428+ return writeTemplate(filepath.Join(m.Pkg, "events_fuzz_test.go"), "events", data)
429+}
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 {
3636 Symbols map[string]*Type
3737 Names map[string]*Node
3838 Decls, SizeChecks []string
39+ NumEvents, NumErrors int // extension event/error code ranges
3940 }
M cmd/xtgen/main.go +3 -0
@@ -35,6 +35,9 @@ func main() {
3535 if err == nil {
3636 err = emitReplies(modules)
3737 }
38+ if err == nil {
39+ err = emitEvents(modules)
40+ }
3841 if err != nil {
3942 fmt.Fprintln(os.Stderr, err)
4043 os.Exit(1)
M cmd/xtgen/replies.go +39 -10
@@ -58,14 +58,43 @@ func elemName(goType string) string {
5858 return strings.ToUpper(goType[:1]) + goType[1:]
5959 }
6060
61-// buildContainer lowers XML members. Replies start at byte 1 (the first XML
62-// member occupies the header data byte) and continue at byte 8.
63-func buildContainer(m *Module, name, recv string, nodes []*Node, reply bool, reserved string) (*container, error) {
61+// layout selects where a container's XML members land on the wire.
62+type layout int
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
6489 c := &container{Name: name, Recv: recv, Reply: reply, Dynamic: -1, Module: m}
6590 off := 0
66- if reply {
91+ switch lay {
92+ case layoutReply, layoutEvent, layoutEventNoSeq:
6793 off = 1
94+ case layoutXGE:
95+ off = 10
6896 }
97+ what := map[layout]string{layoutStruct: "struct", layoutReply: "reply", layoutEvent: "event", layoutEventNoSeq: "event", layoutXGE: "event"}[lay]
6998 first := true
7099 methods := map[string]bool{}
71100 for _, n := range nodes {
@@ -138,13 +167,13 @@ func buildContainer(m *Module, name, recv string, nodes []*Node, reply bool, res
138167 c.Dynamic = len(c.Members)
139168 }
140169 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)
142171 }
143- if reply && first {
172+ if jump := lay.headerJump(); jump > 0 && first {
144173 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)
146175 }
147- off = 8
176+ off = jump
148177 first = false
149178 c.Members = append(c.Members, mem)
150179 continue
@@ -340,7 +369,7 @@ func resolveReplies(m *Module) ([]replyIR, []*container, error) {
340369 return nil, nil, err
341370 }
342371 }
343- c, err := buildContainer(m, name+"Reply", "r", r.Children, true, "Raw")
372+ c, err := buildContainer(m, name+"Reply", "r", r.Children, layoutReply, "Raw")
344373 if err != nil {
345374 return nil, nil, err
346375 }
@@ -357,7 +386,7 @@ func resolveReplies(m *Module) ([]replyIR, []*container, error) {
357386 return nil, nil, err
358387 }
359388 }
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")
361390 if err != nil {
362391 return nil, nil, err
363392 }
M cmd/xtgen/replies_test.go +1 -1
@@ -60,7 +60,7 @@ func TestReplyReservedGetter(t *testing.T) {
6060 {Kind: "pad", Attr: map[string]string{"bytes": "1"}},
6161 {Kind: "field", Attr: map[string]string{"name": "raw", "type": "CARD32"}},
6262 }
63- c, err := buildContainer(m, "XReply", "r", nodes, true, "Raw")
63+ c, err := buildContainer(m, "XReply", "r", nodes, layoutReply, "Raw")
6464 if err != nil {
6565 t.Fatal(err)
6666 }
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 (
88 {{end}} extCount
99 )
1010 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 +471 -0
@@ -0,0 +1,471 @@
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+func TestEventCopyAliases(t *testing.T) {
213+ if xproto.KindButtonRelease != 5 || xproto.KindKeyRelease != 3 || xproto.KindLeaveNotify != 8 || xproto.KindFocusOut != 10 || xproto.KindCirculateRequest != 27 {
214+ t.Fatal("eventcopy kinds")
215+ }
216+ var raw [32]byte
217+ raw[0], raw[1] = 5, 3 // ButtonRelease, button 3
218+ binary.LittleEndian.PutUint16(raw[24:], 0x100)
219+ ev := decode(t, raw[:], nil)
220+ var r xproto.ButtonReleaseEvent = xproto.ButtonReleaseOf(&ev)
221+ var p xproto.ButtonPressEvent = r // alias: same type
222+ if p.Detail() != 3 || r.EventX() != 0x100 {
223+ t.Fatal("fields")
224+ }
225+ d := xproto.ButtonReleaseEventData{Detail: 3, EventX: 0x100}
226+ var out [32]byte
227+ d.Put(&out)
228+ raw[2], raw[3] = 0, 0
229+ if out != raw {
230+ t.Fatalf("Put %x", out)
231+ }
232+}
233+
234+func TestShapeEvent(t *testing.T) {
235+ var raw [32]byte
236+ raw[0], raw[1] = 64, 2 // first event 64, SK Input
237+ binary.LittleEndian.PutUint32(raw[4:], 0x400001)
238+ binary.LittleEndian.PutUint16(raw[8:], 0xfffe)
239+ raw[20] = 1 // shaped: after window(4) extents(8) server_time(4)
240+ ev := decode(t, raw[:], goldenExt())
241+ if ev.Kind != shape.KindNotify || ev.Ext != xturbo.ExtShape {
242+ t.Fatalf("kind %x", ev.Kind)
243+ }
244+ e := shape.NotifyOf(&ev)
245+ if e.ShapeKind() != shape.SkInput || e.AffectedWindow() != 0x400001 || e.ExtentsX() != -2 || !e.Shaped() {
246+ t.Fatal("fields")
247+ }
248+}
249+
250+func TestDecodeEventRejects(t *testing.T) {
251+ raw := make([]byte, 64)
252+ for _, code := range []byte{0, 1, 0x80, 0x81} {
253+ raw[0] = code
254+ if _, _, ok := xturbo.DecodeEvent(raw, nil); ok {
255+ t.Fatalf("code %#x accepted", code)
256+ }
257+ }
258+ raw[0] = 2
259+ if _, _, ok := xturbo.DecodeEvent(raw[:31], nil); ok {
260+ t.Fatal("short event accepted")
261+ }
262+ raw[0] = 35
263+ binary.LittleEndian.PutUint32(raw[4:], 9) // 36 tail bytes > 32 available
264+ if _, _, ok := xturbo.DecodeEvent(raw, nil); ok {
265+ t.Fatal("truncated XGE accepted")
266+ }
267+ binary.LittleEndian.PutUint32(raw[4:], 0xffffffff)
268+ if _, _, ok := xturbo.DecodeEvent(raw, nil); ok {
269+ t.Fatal("huge XGE length accepted")
270+ }
271+ raw[0] = 100 // extension range, nothing negotiated
272+ if ev, n, ok := xturbo.DecodeEvent(raw, goldenExt()); !ok || n != 32 || ev.Kind != xturbo.KindUnknown {
273+ t.Fatal("unknown extension event")
274+ }
275+ raw[0] = 40 // core range gap 36..63
276+ if ev, _, ok := xturbo.DecodeEvent(raw, goldenExt()); !ok || ev.Kind != xturbo.KindUnknown {
277+ t.Fatal("unassigned core code")
278+ }
279+ raw[0] = 35 // XGE evtype >= 128 is never a known kind
280+ binary.LittleEndian.PutUint32(raw[4:], 0)
281+ raw[1] = geMajor
282+ binary.LittleEndian.PutUint16(raw[8:], 0x80)
283+ if ev, _, ok := xturbo.DecodeEvent(raw, goldenExt()); !ok || ev.Kind != xturbo.KindUnknown {
284+ t.Fatal("XGE evtype >= 128")
285+ }
286+}
287+
288+func TestExtTableReverseMaps(t *testing.T) {
289+ ext := goldenExt()
290+ if id, rel, ok := ext.ByEvent(xfixesEvent + 1); !ok || id != xturbo.ExtXFixes || rel != 1 {
291+ t.Fatal("ByEvent CursorNotify")
292+ }
293+ if _, _, ok := ext.ByEvent(xfixesEvent + 2); ok {
294+ t.Fatal("XFIXES declares two events")
295+ }
296+ if id, rel, ok := ext.ByError(xfixesError); !ok || id != xturbo.ExtXFixes || rel != xfixes.BadRegion {
297+ t.Fatal("ByError")
298+ }
299+ if id, ok := ext.ByMajor(xfixesMajor); !ok || id != xturbo.ExtXFixes {
300+ t.Fatal("ByMajor")
301+ }
302+ ext.Set(xturbo.ExtXFixes, 140, 90, 150) // re-negotiation drops stale entries
303+ if _, ok := ext.ByMajor(xfixesMajor); ok {
304+ t.Fatal("stale major")
305+ }
306+ if _, _, ok := ext.ByEvent(xfixesEvent); ok {
307+ t.Fatal("stale event")
308+ }
309+ if id, _, ok := ext.ByError(150); !ok || id != xturbo.ExtXFixes {
310+ t.Fatal("new error base")
311+ }
312+ var render xturbo.ExtTable
313+ render.Set(xturbo.ExtRender, 139, 0, 142)
314+ for code := uint8(142); code < 147; code++ {
315+ if id, rel, ok := render.ByError(code); !ok || id != xturbo.ExtRender || render.Info(id).FirstError+rel != code {
316+ t.Fatalf("render error %d", code)
317+ }
318+ }
319+ if _, _, ok := render.ByError(147); ok {
320+ t.Fatal("RENDER declares five errors")
321+ }
322+}
323+
324+func TestErrorNames(t *testing.T) {
325+ if xproto.BadWindow != 3 || xproto.BadAtom != 5 || xproto.BadImplementation != 17 ||
326+ xproto.ErrorName(xproto.BadWindow) != "BadWindow" || xproto.ErrorName(0) != "" || xproto.ErrorName(200) != "" {
327+ t.Fatal("xproto errors")
328+ }
329+ if xfixes.BadRegion != 0 || xfixes.ErrorName(0) != "BadRegion" {
330+ t.Fatal("xfixes errors")
331+ }
332+ for code, name := range []string{"BadPictFormat", "BadPicture", "BadPictOp", "BadGlyphSet", "BadGlyph"} {
333+ if render.ErrorName(uint8(code)) != name {
334+ t.Fatalf("render %d", code)
335+ }
336+ }
337+}
338+
339+// mixedEvents is 10k events: core, xfixes, XGE with a tail and unknown codes.
340+func mixedEvents(t testing.TB) []byte {
341+ var golden [][]byte
342+ for _, name := range []string{"xproto/MapRequest", "xproto/ConfigureRequest", "xproto/PropertyNotify", "xproto/ClientMessage",
343+ "xproto/KeymapNotify", "xfixes/SelectionNotify", "xproto/GeGeneric"} {
344+ golden = append(golden, loadEvent(t, name))
345+ }
346+ unknown := make([]byte, 32)
347+ unknown[0] = 100
348+ golden = append(golden, unknown)
349+ var stream []byte
350+ for i := range 10000 {
351+ stream = append(stream, golden[i%len(golden)]...)
352+ }
353+ return stream
354+}
355+
356+// eventSink touches the per-kind view getters a window manager reads.
357+func eventSink(ev *xturbo.Event) uint64 {
358+ switch ev.Kind {
359+ case xproto.KindMapRequest:
360+ e := xproto.MapRequestOf(ev)
361+ return uint64(e.Window()) + uint64(e.Parent())
362+ case xproto.KindConfigureRequest:
363+ e := xproto.ConfigureRequestOf(ev)
364+ return uint64(e.Window()) + uint64(e.Width()) + uint64(e.ValueMask()) + uint64(int64(e.X()))
365+ case xproto.KindPropertyNotify:
366+ e := xproto.PropertyNotifyOf(ev)
367+ return uint64(e.Atom()) + uint64(e.State())
368+ case xproto.KindClientMessage:
369+ e := xproto.ClientMessageOf(ev)
370+ return uint64(e.Type()) + uint64(e.Data().Data32()[0])
371+ case xproto.KindKeymapNotify:
372+ return uint64(len(xproto.KeymapNotifyOf(ev).Keys()))
373+ case xfixes.KindSelectionNotify:
374+ e := xfixes.SelectionNotifyOf(ev)
375+ return uint64(e.Owner()) + uint64(e.Selection())
376+ case xturbo.EventKind(xturbo.ExtGenericEvent)<<8 | 0x80 | 1:
377+ if g, ok := xproto.GeGenericOf(ev); ok {
378+ return uint64(len(g.Tail()))
379+ }
380+ }
381+ return 0
382+}
383+
384+func decodeAll(t testing.TB, stream []byte, ext *xturbo.ExtTable) (sum uint64) {
385+ for len(stream) > 0 {
386+ ev, n, ok := xturbo.DecodeEvent(stream, ext)
387+ if !ok {
388+ t.Fatal("decode")
389+ }
390+ sum += eventSink(&ev)
391+ stream = stream[n:]
392+ }
393+ return sum
394+}
395+
396+func TestAllocEvents(t *testing.T) {
397+ stream := mixedEvents(t)
398+ ext := goldenExt()
399+ var sink uint64
400+ if n := testing.AllocsPerRun(10, func() { sink += decodeAll(t, stream, ext) }); n != 0 {
401+ t.Fatalf("DecodeEvent x10k + views allocate %v times", n)
402+ }
403+ cm := xproto.ClientMessageEventData{Format: 32, Window: 1, Type: 2}
404+ cfg := xproto.ConfigureNotifyEventData{Window: 1, Width: 640, Height: 480}
405+ var out [32]byte
406+ if n := testing.AllocsPerRun(1000, func() { cm.Put(&out); cfg.Put(&out) }); n != 0 {
407+ t.Fatalf("EventData.Put allocates %v times", n)
408+ }
409+ b := xturbo.NewBuf(0)
410+ if n := testing.AllocsPerRun(1000, func() {
411+ b.Reset()
412+ cm.Put(&out)
413+ xproto.SendEvent(b, false, 1, xproto.EventMaskSubstructureRedirect, &out)
414+ }); n != 0 {
415+ t.Fatalf("SendEvent allocates %v times", n)
416+ }
417+ _ = sink
418+}
419+
420+func BenchmarkDecodeEvents10k(b *testing.B) {
421+ stream := mixedEvents(b)
422+ ext := goldenExt()
423+ var sink uint64
424+ b.SetBytes(int64(len(stream)))
425+ b.ReportAllocs()
426+ for b.Loop() {
427+ sink += decodeAll(b, stream, ext)
428+ }
429+ _ = sink
430+}
431+
432+// FuzzDecodeEvent: no panic; n is 32 or the XGE length; Raw copies the first
433+// 32 bytes; Tail stays within raw; Kind is consistent with Ext. extSeed picks
434+// which extensions are negotiated and where their bases lie.
435+func FuzzDecodeEvent(f *testing.F) {
436+ for _, name := range []string{"xproto/MapRequest", "xproto/ClientMessage", "xproto/KeymapNotify", "xfixes/SelectionNotify", "xproto/GeGeneric"} {
437+ f.Add(loadEvent(f, name), uint32(0x8a578c83))
438+ }
439+ f.Add(make([]byte, 32), uint32(0))
440+ f.Fuzz(func(t *testing.T, raw []byte, extSeed uint32) {
441+ var ext xturbo.ExtTable
442+ ext.Set(xturbo.ExtXFixes, uint8(extSeed>>24), uint8(extSeed>>16), uint8(extSeed>>8))
443+ ext.Set(xturbo.ExtGenericEvent, uint8(extSeed), 0, 0)
444+ ext.Set(xturbo.ExtShape, uint8(extSeed>>4), uint8(extSeed>>12)|64, 0)
445+ table := &ext
446+ if extSeed&1 != 0 {
447+ table = nil
448+ }
449+ ev, n, ok := xturbo.DecodeEvent(raw, table)
450+ if !ok {
451+ if n != 0 {
452+ t.Fatal("n on failure")
453+ }
454+ return
455+ }
456+ if len(raw) < 32 || raw[0]&0x7f < 2 || n > len(raw) || !bytes.Equal(ev.Raw[:], raw[:32]) || ev.SendEvent != (raw[0]&0x80 != 0) {
457+ t.Fatalf("n %d", n)
458+ }
459+ if raw[0]&0x7f == 35 {
460+ if n != 32+4*int(binary.LittleEndian.Uint32(raw[4:])) || len(ev.Tail) != n-32 {
461+ t.Fatal("XGE framing")
462+ }
463+ } else if n != 32 || ev.Tail != nil {
464+ t.Fatal("fixed framing")
465+ }
466+ if ev.Kind != xturbo.KindUnknown && ev.Kind>>8 != xturbo.EventKind(ev.Ext) {
467+ t.Fatal("Kind/Ext mismatch")
468+ }
469+ _ = eventSink(&ev)
470+ })
471+}
M ext.go +66 -4
@@ -7,12 +7,47 @@ type ExtInfo struct {
77 }
88
99 // ExtTable is populated during setup. Fixed arrays avoid lookup allocations.
10-// Reverse mapping is completed with the read-side runtime in Phase 3.
11-type ExtTable struct{ info [extCount]ExtInfo }
10+// Reverse maps store ExtID+1 so the zero value means "none".
11+type ExtTable struct {
12+ info [extCount]ExtInfo
13+ byEvent [128]uint8
14+ byError [256]uint8
15+ byMajor [256]uint8
16+}
1217
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.
1320 func (t *ExtTable) Set(id ExtID, major, firstEvent, firstError uint8) {
14- if id < extCount {
15- t.info[id] = ExtInfo{true, major, firstEvent, firstError}
21+ if id == ExtCore || id >= extCount {
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+ }
1651 }
1752 }
1853 func (t *ExtTable) Info(id ExtID) ExtInfo {
@@ -25,3 +60,30 @@ func (t *ExtTable) Info(id ExtID) ExtInfo {
2560 return t.info[id]
2661 }
2762 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 (
1717 )
1818
1919 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 +11 -0
@@ -38,3 +38,14 @@ adds GetWindowAttributes (child window), QueryTree (root), an unset WM_NAME
3838 GetProperty (zero-length value) and ListExtensions (variable STR list). These
3939 reply-only requests use opcodes outside the request-golden classifier.
4040 Generated `FuzzParse*Reply` targets seed from every matching reply golden.
41+
42+T2.7 adds event and error goldens (`<Name>.event.hex`, `<Name>.error.hex`).
43+The tee client selects SubstructureRedirect/Notify and PropertyChange on the
44+root plus XFIXES selection input; a second raw client (not teed) creates, maps,
45+configures, unmaps and destroys a window, writes a root property, sends a
46+format-32 ClientMessage and takes PRIMARY. ConvertSelection of an unowned
47+SECONDARY yields a core SelectionNotify. GetAtomName, GetWindowAttributes and
48+XFIXES DestroyRegion on bad IDs yield BadAtom, BadWindow and BadRegion. Each
49+batch is fenced by a GetInputFocus reply; unexpected packets fail capture.
50+`KeymapNotify` and `GeGeneric` (XGE framing with a tail) are hand-written
51+(`# hand:`): Xvfb produces neither for these clients.
M internal/xtest/capture_test.go +200 -3
@@ -327,6 +327,187 @@ func TestCaptureRequests(t *testing.T) {
327327 keep("xproto/GetPropertyEmpty", packet())
328328 write([]byte{99, 0, 1, 0}) // ListExtensions: variable STR iterator
329329 keep("xproto/ListExtensions", packet())
330+ // Event and error captures. The tee client acts as a window manager on the
331+ // root; a second raw client (direct socket, not teed) triggers the events.
332+ // Requests sent here on the tee use window/minor values the request-golden
333+ // classifier below ignores.
334+ xfixesEvent, xfixesError := ext[10], ext[11]
335+ events := map[string][]byte{}
336+ xerrors := map[string][]byte{}
337+ // collect reads events and errors until the reply fencing them.
338+ collect := func(names map[byte]string) {
339+ t.Helper()
340+ write([]byte{43, 0, 1, 0}) // GetInputFocus
341+ for {
342+ p := packet()
343+ switch {
344+ case p[0] == 1:
345+ return
346+ case p[0] == 0:
347+ name := names[p[1]]
348+ if name == "" {
349+ t.Fatalf("unexpected error %x", p)
350+ }
351+ xerrors[name] = p
352+ default:
353+ name := names[p[0]]
354+ if name == "" {
355+ t.Fatalf("unexpected event %x", p)
356+ }
357+ events[name] = p
358+ }
359+ }
360+ }
361+ const (
362+ maskSubstructureNotify = 1 << 19
363+ maskSubstructureRedirect = 1 << 20
364+ maskPropertyChange = 1 << 22
365+ atomPrimary, atomSecondary, atomString = 1, 2, 31
366+ )
367+ selectRoot := make([]byte, 16)
368+ selectRoot[0], selectRoot[2] = 2, 4
369+ binary.LittleEndian.PutUint32(selectRoot[4:], root)
370+ binary.LittleEndian.PutUint32(selectRoot[8:], 1<<11) // CWEventMask
371+ binary.LittleEndian.PutUint32(selectRoot[12:], maskSubstructureNotify|maskSubstructureRedirect|maskPropertyChange)
372+ write(selectRoot)
373+ selectSel := make([]byte, 16)
374+ selectSel[0], selectSel[1], selectSel[2] = major, 2, 4
375+ binary.LittleEndian.PutUint32(selectSel[4:], root)
376+ binary.LittleEndian.PutUint32(selectSel[8:], atomPrimary)
377+ binary.LittleEndian.PutUint32(selectSel[12:], 1) // SetSelectionOwner
378+ write(selectSel)
379+ collect(nil)
380+
381+ other, err := net.DialTimeout("unix", fmt.Sprintf("/tmp/.X11-unix/X%d", num), 5*time.Second)
382+ if err != nil {
383+ t.Fatal(err)
384+ }
385+ defer other.Close()
386+ _ = other.SetDeadline(time.Now().Add(5 * time.Second))
387+ send := func(p []byte) {
388+ t.Helper()
389+ if _, err := other.Write(p); err != nil {
390+ t.Fatal(err)
391+ }
392+ }
393+ recv := func(p []byte) {
394+ t.Helper()
395+ if _, err := io.ReadFull(other, p); err != nil {
396+ t.Fatal(err)
397+ }
398+ }
399+ // sync round-trips the second client; any error fails the capture.
400+ sync := func() {
401+ t.Helper()
402+ send([]byte{43, 0, 1, 0})
403+ var p [32]byte
404+ recv(p[:])
405+ if p[0] != 1 {
406+ t.Fatalf("second client: %x", p)
407+ }
408+ }
409+ send([]byte{'l', 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0})
410+ var otherHead [8]byte
411+ recv(otherHead[:])
412+ if otherHead[0] != 1 {
413+ t.Fatalf("second setup failed: %x", otherHead)
414+ }
415+ otherSetup := make([]byte, int(binary.LittleEndian.Uint16(otherHead[6:]))*4)
416+ recv(otherSetup)
417+ client2 := binary.LittleEndian.Uint32(otherSetup[4:]) | 1
418+ win := make([]byte, 32)
419+ win[0], win[2] = 1, 8
420+ binary.LittleEndian.PutUint32(win[4:], client2)
421+ binary.LittleEndian.PutUint32(win[8:], root)
422+ binary.LittleEndian.PutUint16(win[12:], 10)
423+ binary.LittleEndian.PutUint16(win[14:], 20)
424+ binary.LittleEndian.PutUint16(win[16:], 300)
425+ binary.LittleEndian.PutUint16(win[18:], 200)
426+ binary.LittleEndian.PutUint16(win[22:], 1)
427+ send(win) // CreateNotify
428+ mapOther := make([]byte, 8)
429+ mapOther[0], mapOther[2] = 8, 2
430+ binary.LittleEndian.PutUint32(mapOther[4:], client2)
431+ send(mapOther) // MapRequest (redirected)
432+ cfg := make([]byte, 24)
433+ cfg[0], cfg[2] = 12, 6
434+ binary.LittleEndian.PutUint32(cfg[4:], client2)
435+ binary.LittleEndian.PutUint16(cfg[8:], 1|2|4) // x, y, width
436+ binary.LittleEndian.PutUint32(cfg[12:], 0xfffffffb)
437+ binary.LittleEndian.PutUint32(cfg[16:], 40)
438+ binary.LittleEndian.PutUint32(cfg[20:], 640)
439+ send(cfg) // ConfigureRequest (redirected)
440+ prop := make([]byte, 28)
441+ prop[0], prop[2] = 18, 7
442+ binary.LittleEndian.PutUint32(prop[4:], root)
443+ binary.LittleEndian.PutUint32(prop[8:], atom)
444+ binary.LittleEndian.PutUint32(prop[12:], atomString)
445+ prop[16] = 8
446+ binary.LittleEndian.PutUint32(prop[20:], 2)
447+ copy(prop[24:], "hi")
448+ send(prop) // PropertyNotify (NewValue)
449+ msg := make([]byte, 44)
450+ msg[0], msg[2] = 25, 11
451+ binary.LittleEndian.PutUint32(msg[4:], root)
452+ binary.LittleEndian.PutUint32(msg[8:], maskSubstructureNotify|maskSubstructureRedirect)
453+ msg[12], msg[13] = 33, 32 // ClientMessage, format 32
454+ binary.LittleEndian.PutUint32(msg[16:], client2)
455+ binary.LittleEndian.PutUint32(msg[20:], atom)
456+ for i := range 5 {
457+ binary.LittleEndian.PutUint32(msg[24+4*i:], uint32(0x01020304*(i+1)))
458+ }
459+ send(msg) // ClientMessage (SendEvent bit set)
460+ owner := make([]byte, 16)
461+ owner[0], owner[2] = 22, 4
462+ binary.LittleEndian.PutUint32(owner[4:], client2)
463+ binary.LittleEndian.PutUint32(owner[8:], atomPrimary)
464+ send(owner) // xfixes SelectionNotify (SetSelectionOwner)
465+ sync()
466+ collect(map[byte]string{16: "xproto/CreateNotify", 20: "xproto/MapRequest", 23: "xproto/ConfigureRequest",
467+ 28: "xproto/PropertyNotify", 0x80 | 33: "xproto/ClientMessage", xfixesEvent: "xfixes/SelectionNotify"})
468+ // The window manager maps the window itself (MapNotify); the second client
469+ // then unmaps and destroys it.
470+ mapByWM := make([]byte, 8)
471+ mapByWM[0], mapByWM[2] = 8, 2
472+ binary.LittleEndian.PutUint32(mapByWM[4:], client2)
473+ write(mapByWM)
474+ collect(map[byte]string{19: "xproto/MapNotify"})
475+ unmap := []byte{10, 0, 2, 0, 0, 0, 0, 0}
476+ binary.LittleEndian.PutUint32(unmap[4:], client2)
477+ send(unmap)
478+ destroy := []byte{4, 0, 2, 0, 0, 0, 0, 0}
479+ binary.LittleEndian.PutUint32(destroy[4:], client2)
480+ send(destroy)
481+ sync()
482+ collect(map[byte]string{18: "xproto/UnmapNotify", 17: "xproto/DestroyNotify"})
483+ // Core SelectionNotify: SECONDARY has no owner, so the server replies itself.
484+ convert := make([]byte, 24)
485+ convert[0], convert[2] = 24, 6
486+ binary.LittleEndian.PutUint32(convert[4:], child)
487+ binary.LittleEndian.PutUint32(convert[8:], atomSecondary)
488+ binary.LittleEndian.PutUint32(convert[12:], atomString)
489+ binary.LittleEndian.PutUint32(convert[16:], atom)
490+ write(convert)
491+ collect(map[byte]string{31: "xproto/SelectionNotify"})
492+ // Errors: BadAtom, BadWindow and an extension error (XFIXES BadRegion).
493+ badAtom := []byte{17, 0, 2, 0, 0xef, 0xbe, 0xad, 0x0d}
494+ write(badAtom)
495+ badWindow := []byte{3, 0, 2, 0, 0, 0, 0, 0}
496+ binary.LittleEndian.PutUint32(badWindow[4:], base|0x7777)
497+ write(badWindow)
498+ badRegion := []byte{major, 10, 2, 0, 0, 0, 0, 0}
499+ binary.LittleEndian.PutUint32(badRegion[4:], base|0x8888)
500+ write(badRegion)
501+ collect(map[byte]string{5: "xproto/BadAtom", 3: "xproto/BadWindow", xfixesError: "xfixes/BadRegion"})
502+ if len(events) != 10 || len(xerrors) != 3 {
503+ t.Fatalf("captured %d events, %d errors", len(events), len(xerrors))
504+ }
505+ for name, p := range events {
506+ writeGolden(t, name+".event.hex", "# source: Xvfb event via raw socketpair tee; triggered by an independent second client or reference request\n", p)
507+ }
508+ for name, p := range xerrors {
509+ writeGolden(t, name+".error.hex", "# source: Xvfb error via raw socketpair tee to an independent reference request\n", p)
510+ }
330511 // Closing the client drains the request direction; wait before inspecting tee.
331512 client.Close()
332513 select {
@@ -353,13 +534,17 @@ func TestCaptureRequests(t *testing.T) {
353534 captured["xproto/CreateWindow"] = p
354535 }
355536 case 2:
356- captured["xproto/ChangeWindowAttributes"] = p
537+ if binary.LittleEndian.Uint32(p[4:]) == child {
538+ captured["xproto/ChangeWindowAttributes"] = p
539+ }
357540 case 12:
358541 names := []string{"ConfigureWindow", "ConfigureWindowSparse", "ConfigureWindowAll"}
359542 captured["xproto/"+names[configIndex]] = p
360543 configIndex++
361544 case 8:
362- captured["xproto/MapWindow"] = p
545+ if binary.LittleEndian.Uint32(p[4:]) == root {
546+ captured["xproto/MapWindow"] = p
547+ }
363548 case 14:
364549 captured["xproto/GetGeometry"] = p
365550 case 16:
@@ -373,7 +558,7 @@ func TestCaptureRequests(t *testing.T) {
373558 if p[0] == resMajor && p[1] == 4 {
374559 captured["res/QueryClientIds"] = p
375560 }
376- if p[0] == major {
561+ if p[0] == major && p[1] == 0 {
377562 captured["xfixes/QueryVersion"] = p
378563 }
379564 }
@@ -403,3 +588,15 @@ func TestCaptureRequests(t *testing.T) {
403588 }
404589 }
405590 }
591+
592+func writeGolden(t *testing.T, name, source string, p []byte) {
593+ t.Helper()
594+ path := filepath.Join("../../testdata/golden", name)
595+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
596+ t.Fatal(err)
597+ }
598+ text := source + "# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace\n" + hex.EncodeToString(p) + "\n"
599+ if err := os.WriteFile(path, []byte(text), 0644); err != nil {
600+ t.Fatal(err)
601+ }
602+}
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 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 +993 -0
@@ -0,0 +1,993 @@
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+ }
128+ _ = KeyReleaseOf(&ev)
129+ {
130+ vButtonPress := ButtonPressOf(&ev)
131+ _ = vButtonPress.Detail()
132+ _ = vButtonPress.Time()
133+ _ = vButtonPress.Root()
134+ _ = vButtonPress.Event()
135+ _ = vButtonPress.Child()
136+ _ = vButtonPress.RootX()
137+ _ = vButtonPress.RootY()
138+ _ = vButtonPress.EventX()
139+ _ = vButtonPress.EventY()
140+ _ = vButtonPress.State()
141+ _ = vButtonPress.SameScreen()
142+ 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()}
143+ var out [32]byte
144+ d.Put(&out)
145+ if out[0] != 4 {
146+ t.Fatal("ButtonPress code")
147+ }
148+ back := xturbo.Event{Raw: out}
149+ w := ButtonPressOf(&back)
150+ if w.Detail() != vButtonPress.Detail() {
151+ t.Fatal("ButtonPress.Detail")
152+ }
153+ if w.Time() != vButtonPress.Time() {
154+ t.Fatal("ButtonPress.Time")
155+ }
156+ if w.Root() != vButtonPress.Root() {
157+ t.Fatal("ButtonPress.Root")
158+ }
159+ if w.Event() != vButtonPress.Event() {
160+ t.Fatal("ButtonPress.Event")
161+ }
162+ if w.Child() != vButtonPress.Child() {
163+ t.Fatal("ButtonPress.Child")
164+ }
165+ if w.RootX() != vButtonPress.RootX() {
166+ t.Fatal("ButtonPress.RootX")
167+ }
168+ if w.RootY() != vButtonPress.RootY() {
169+ t.Fatal("ButtonPress.RootY")
170+ }
171+ if w.EventX() != vButtonPress.EventX() {
172+ t.Fatal("ButtonPress.EventX")
173+ }
174+ if w.EventY() != vButtonPress.EventY() {
175+ t.Fatal("ButtonPress.EventY")
176+ }
177+ if w.State() != vButtonPress.State() {
178+ t.Fatal("ButtonPress.State")
179+ }
180+ if w.SameScreen() != vButtonPress.SameScreen() {
181+ t.Fatal("ButtonPress.SameScreen")
182+ }
183+ }
184+ _ = ButtonReleaseOf(&ev)
185+ {
186+ vMotionNotify := MotionNotifyOf(&ev)
187+ _ = vMotionNotify.Detail()
188+ _ = vMotionNotify.Time()
189+ _ = vMotionNotify.Root()
190+ _ = vMotionNotify.Event()
191+ _ = vMotionNotify.Child()
192+ _ = vMotionNotify.RootX()
193+ _ = vMotionNotify.RootY()
194+ _ = vMotionNotify.EventX()
195+ _ = vMotionNotify.EventY()
196+ _ = vMotionNotify.State()
197+ _ = vMotionNotify.SameScreen()
198+ 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()}
199+ var out [32]byte
200+ d.Put(&out)
201+ if out[0] != 6 {
202+ t.Fatal("MotionNotify code")
203+ }
204+ back := xturbo.Event{Raw: out}
205+ w := MotionNotifyOf(&back)
206+ if w.Detail() != vMotionNotify.Detail() {
207+ t.Fatal("MotionNotify.Detail")
208+ }
209+ if w.Time() != vMotionNotify.Time() {
210+ t.Fatal("MotionNotify.Time")
211+ }
212+ if w.Root() != vMotionNotify.Root() {
213+ t.Fatal("MotionNotify.Root")
214+ }
215+ if w.Event() != vMotionNotify.Event() {
216+ t.Fatal("MotionNotify.Event")
217+ }
218+ if w.Child() != vMotionNotify.Child() {
219+ t.Fatal("MotionNotify.Child")
220+ }
221+ if w.RootX() != vMotionNotify.RootX() {
222+ t.Fatal("MotionNotify.RootX")
223+ }
224+ if w.RootY() != vMotionNotify.RootY() {
225+ t.Fatal("MotionNotify.RootY")
226+ }
227+ if w.EventX() != vMotionNotify.EventX() {
228+ t.Fatal("MotionNotify.EventX")
229+ }
230+ if w.EventY() != vMotionNotify.EventY() {
231+ t.Fatal("MotionNotify.EventY")
232+ }
233+ if w.State() != vMotionNotify.State() {
234+ t.Fatal("MotionNotify.State")
235+ }
236+ if w.SameScreen() != vMotionNotify.SameScreen() {
237+ t.Fatal("MotionNotify.SameScreen")
238+ }
239+ }
240+ {
241+ vEnterNotify := EnterNotifyOf(&ev)
242+ _ = vEnterNotify.Detail()
243+ _ = vEnterNotify.Time()
244+ _ = vEnterNotify.Root()
245+ _ = vEnterNotify.Event()
246+ _ = vEnterNotify.Child()
247+ _ = vEnterNotify.RootX()
248+ _ = vEnterNotify.RootY()
249+ _ = vEnterNotify.EventX()
250+ _ = vEnterNotify.EventY()
251+ _ = vEnterNotify.State()
252+ _ = vEnterNotify.Mode()
253+ _ = vEnterNotify.SameScreenFocus()
254+ 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()}
255+ var out [32]byte
256+ d.Put(&out)
257+ if out[0] != 7 {
258+ t.Fatal("EnterNotify code")
259+ }
260+ back := xturbo.Event{Raw: out}
261+ w := EnterNotifyOf(&back)
262+ if w.Detail() != vEnterNotify.Detail() {
263+ t.Fatal("EnterNotify.Detail")
264+ }
265+ if w.Time() != vEnterNotify.Time() {
266+ t.Fatal("EnterNotify.Time")
267+ }
268+ if w.Root() != vEnterNotify.Root() {
269+ t.Fatal("EnterNotify.Root")
270+ }
271+ if w.Event() != vEnterNotify.Event() {
272+ t.Fatal("EnterNotify.Event")
273+ }
274+ if w.Child() != vEnterNotify.Child() {
275+ t.Fatal("EnterNotify.Child")
276+ }
277+ if w.RootX() != vEnterNotify.RootX() {
278+ t.Fatal("EnterNotify.RootX")
279+ }
280+ if w.RootY() != vEnterNotify.RootY() {
281+ t.Fatal("EnterNotify.RootY")
282+ }
283+ if w.EventX() != vEnterNotify.EventX() {
284+ t.Fatal("EnterNotify.EventX")
285+ }
286+ if w.EventY() != vEnterNotify.EventY() {
287+ t.Fatal("EnterNotify.EventY")
288+ }
289+ if w.State() != vEnterNotify.State() {
290+ t.Fatal("EnterNotify.State")
291+ }
292+ if w.Mode() != vEnterNotify.Mode() {
293+ t.Fatal("EnterNotify.Mode")
294+ }
295+ if w.SameScreenFocus() != vEnterNotify.SameScreenFocus() {
296+ t.Fatal("EnterNotify.SameScreenFocus")
297+ }
298+ }
299+ _ = LeaveNotifyOf(&ev)
300+ {
301+ vFocusIn := FocusInOf(&ev)
302+ _ = vFocusIn.Detail()
303+ _ = vFocusIn.Event()
304+ _ = vFocusIn.Mode()
305+ d := FocusInEventData{Detail: vFocusIn.Detail(), Event: vFocusIn.Event(), Mode: vFocusIn.Mode()}
306+ var out [32]byte
307+ d.Put(&out)
308+ if out[0] != 9 {
309+ t.Fatal("FocusIn code")
310+ }
311+ back := xturbo.Event{Raw: out}
312+ w := FocusInOf(&back)
313+ if w.Detail() != vFocusIn.Detail() {
314+ t.Fatal("FocusIn.Detail")
315+ }
316+ if w.Event() != vFocusIn.Event() {
317+ t.Fatal("FocusIn.Event")
318+ }
319+ if w.Mode() != vFocusIn.Mode() {
320+ t.Fatal("FocusIn.Mode")
321+ }
322+ }
323+ _ = FocusOutOf(&ev)
324+ {
325+ vKeymapNotify := KeymapNotifyOf(&ev)
326+ _ = len(vKeymapNotify.Keys())
327+ d := KeymapNotifyEventData{Keys: [31]uint8(vKeymapNotify.Keys())}
328+ var out [32]byte
329+ d.Put(&out)
330+ if out[0] != 11 {
331+ t.Fatal("KeymapNotify code")
332+ }
333+ back := xturbo.Event{Raw: out}
334+ w := KeymapNotifyOf(&back)
335+ if !bytes.Equal(w.Keys(), vKeymapNotify.Keys()) {
336+ t.Fatal("KeymapNotify.Keys")
337+ }
338+ }
339+ {
340+ vExpose := ExposeOf(&ev)
341+ _ = vExpose.Window()
342+ _ = vExpose.X()
343+ _ = vExpose.Y()
344+ _ = vExpose.Width()
345+ _ = vExpose.Height()
346+ _ = vExpose.Count()
347+ d := ExposeEventData{Window: vExpose.Window(), X: vExpose.X(), Y: vExpose.Y(), Width: vExpose.Width(), Height: vExpose.Height(), Count: vExpose.Count()}
348+ var out [32]byte
349+ d.Put(&out)
350+ if out[0] != 12 {
351+ t.Fatal("Expose code")
352+ }
353+ back := xturbo.Event{Raw: out}
354+ w := ExposeOf(&back)
355+ if w.Window() != vExpose.Window() {
356+ t.Fatal("Expose.Window")
357+ }
358+ if w.X() != vExpose.X() {
359+ t.Fatal("Expose.X")
360+ }
361+ if w.Y() != vExpose.Y() {
362+ t.Fatal("Expose.Y")
363+ }
364+ if w.Width() != vExpose.Width() {
365+ t.Fatal("Expose.Width")
366+ }
367+ if w.Height() != vExpose.Height() {
368+ t.Fatal("Expose.Height")
369+ }
370+ if w.Count() != vExpose.Count() {
371+ t.Fatal("Expose.Count")
372+ }
373+ }
374+ {
375+ vGraphicsExposure := GraphicsExposureOf(&ev)
376+ _ = vGraphicsExposure.Drawable()
377+ _ = vGraphicsExposure.X()
378+ _ = vGraphicsExposure.Y()
379+ _ = vGraphicsExposure.Width()
380+ _ = vGraphicsExposure.Height()
381+ _ = vGraphicsExposure.MinorOpcode()
382+ _ = vGraphicsExposure.Count()
383+ _ = vGraphicsExposure.MajorOpcode()
384+ 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()}
385+ var out [32]byte
386+ d.Put(&out)
387+ if out[0] != 13 {
388+ t.Fatal("GraphicsExposure code")
389+ }
390+ back := xturbo.Event{Raw: out}
391+ w := GraphicsExposureOf(&back)
392+ if w.Drawable() != vGraphicsExposure.Drawable() {
393+ t.Fatal("GraphicsExposure.Drawable")
394+ }
395+ if w.X() != vGraphicsExposure.X() {
396+ t.Fatal("GraphicsExposure.X")
397+ }
398+ if w.Y() != vGraphicsExposure.Y() {
399+ t.Fatal("GraphicsExposure.Y")
400+ }
401+ if w.Width() != vGraphicsExposure.Width() {
402+ t.Fatal("GraphicsExposure.Width")
403+ }
404+ if w.Height() != vGraphicsExposure.Height() {
405+ t.Fatal("GraphicsExposure.Height")
406+ }
407+ if w.MinorOpcode() != vGraphicsExposure.MinorOpcode() {
408+ t.Fatal("GraphicsExposure.MinorOpcode")
409+ }
410+ if w.Count() != vGraphicsExposure.Count() {
411+ t.Fatal("GraphicsExposure.Count")
412+ }
413+ if w.MajorOpcode() != vGraphicsExposure.MajorOpcode() {
414+ t.Fatal("GraphicsExposure.MajorOpcode")
415+ }
416+ }
417+ {
418+ vNoExposure := NoExposureOf(&ev)
419+ _ = vNoExposure.Drawable()
420+ _ = vNoExposure.MinorOpcode()
421+ _ = vNoExposure.MajorOpcode()
422+ d := NoExposureEventData{Drawable: vNoExposure.Drawable(), MinorOpcode: vNoExposure.MinorOpcode(), MajorOpcode: vNoExposure.MajorOpcode()}
423+ var out [32]byte
424+ d.Put(&out)
425+ if out[0] != 14 {
426+ t.Fatal("NoExposure code")
427+ }
428+ back := xturbo.Event{Raw: out}
429+ w := NoExposureOf(&back)
430+ if w.Drawable() != vNoExposure.Drawable() {
431+ t.Fatal("NoExposure.Drawable")
432+ }
433+ if w.MinorOpcode() != vNoExposure.MinorOpcode() {
434+ t.Fatal("NoExposure.MinorOpcode")
435+ }
436+ if w.MajorOpcode() != vNoExposure.MajorOpcode() {
437+ t.Fatal("NoExposure.MajorOpcode")
438+ }
439+ }
440+ {
441+ vVisibilityNotify := VisibilityNotifyOf(&ev)
442+ _ = vVisibilityNotify.Window()
443+ _ = vVisibilityNotify.State()
444+ d := VisibilityNotifyEventData{Window: vVisibilityNotify.Window(), State: vVisibilityNotify.State()}
445+ var out [32]byte
446+ d.Put(&out)
447+ if out[0] != 15 {
448+ t.Fatal("VisibilityNotify code")
449+ }
450+ back := xturbo.Event{Raw: out}
451+ w := VisibilityNotifyOf(&back)
452+ if w.Window() != vVisibilityNotify.Window() {
453+ t.Fatal("VisibilityNotify.Window")
454+ }
455+ if w.State() != vVisibilityNotify.State() {
456+ t.Fatal("VisibilityNotify.State")
457+ }
458+ }
459+ {
460+ vCreateNotify := CreateNotifyOf(&ev)
461+ _ = vCreateNotify.Parent()
462+ _ = vCreateNotify.Window()
463+ _ = vCreateNotify.X()
464+ _ = vCreateNotify.Y()
465+ _ = vCreateNotify.Width()
466+ _ = vCreateNotify.Height()
467+ _ = vCreateNotify.BorderWidth()
468+ _ = vCreateNotify.OverrideRedirect()
469+ 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()}
470+ var out [32]byte
471+ d.Put(&out)
472+ if out[0] != 16 {
473+ t.Fatal("CreateNotify code")
474+ }
475+ back := xturbo.Event{Raw: out}
476+ w := CreateNotifyOf(&back)
477+ if w.Parent() != vCreateNotify.Parent() {
478+ t.Fatal("CreateNotify.Parent")
479+ }
480+ if w.Window() != vCreateNotify.Window() {
481+ t.Fatal("CreateNotify.Window")
482+ }
483+ if w.X() != vCreateNotify.X() {
484+ t.Fatal("CreateNotify.X")
485+ }
486+ if w.Y() != vCreateNotify.Y() {
487+ t.Fatal("CreateNotify.Y")
488+ }
489+ if w.Width() != vCreateNotify.Width() {
490+ t.Fatal("CreateNotify.Width")
491+ }
492+ if w.Height() != vCreateNotify.Height() {
493+ t.Fatal("CreateNotify.Height")
494+ }
495+ if w.BorderWidth() != vCreateNotify.BorderWidth() {
496+ t.Fatal("CreateNotify.BorderWidth")
497+ }
498+ if w.OverrideRedirect() != vCreateNotify.OverrideRedirect() {
499+ t.Fatal("CreateNotify.OverrideRedirect")
500+ }
501+ }
502+ {
503+ vDestroyNotify := DestroyNotifyOf(&ev)
504+ _ = vDestroyNotify.Event()
505+ _ = vDestroyNotify.Window()
506+ d := DestroyNotifyEventData{Event: vDestroyNotify.Event(), Window: vDestroyNotify.Window()}
507+ var out [32]byte
508+ d.Put(&out)
509+ if out[0] != 17 {
510+ t.Fatal("DestroyNotify code")
511+ }
512+ back := xturbo.Event{Raw: out}
513+ w := DestroyNotifyOf(&back)
514+ if w.Event() != vDestroyNotify.Event() {
515+ t.Fatal("DestroyNotify.Event")
516+ }
517+ if w.Window() != vDestroyNotify.Window() {
518+ t.Fatal("DestroyNotify.Window")
519+ }
520+ }
521+ {
522+ vUnmapNotify := UnmapNotifyOf(&ev)
523+ _ = vUnmapNotify.Event()
524+ _ = vUnmapNotify.Window()
525+ _ = vUnmapNotify.FromConfigure()
526+ d := UnmapNotifyEventData{Event: vUnmapNotify.Event(), Window: vUnmapNotify.Window(), FromConfigure: vUnmapNotify.FromConfigure()}
527+ var out [32]byte
528+ d.Put(&out)
529+ if out[0] != 18 {
530+ t.Fatal("UnmapNotify code")
531+ }
532+ back := xturbo.Event{Raw: out}
533+ w := UnmapNotifyOf(&back)
534+ if w.Event() != vUnmapNotify.Event() {
535+ t.Fatal("UnmapNotify.Event")
536+ }
537+ if w.Window() != vUnmapNotify.Window() {
538+ t.Fatal("UnmapNotify.Window")
539+ }
540+ if w.FromConfigure() != vUnmapNotify.FromConfigure() {
541+ t.Fatal("UnmapNotify.FromConfigure")
542+ }
543+ }
544+ {
545+ vMapNotify := MapNotifyOf(&ev)
546+ _ = vMapNotify.Event()
547+ _ = vMapNotify.Window()
548+ _ = vMapNotify.OverrideRedirect()
549+ d := MapNotifyEventData{Event: vMapNotify.Event(), Window: vMapNotify.Window(), OverrideRedirect: vMapNotify.OverrideRedirect()}
550+ var out [32]byte
551+ d.Put(&out)
552+ if out[0] != 19 {
553+ t.Fatal("MapNotify code")
554+ }
555+ back := xturbo.Event{Raw: out}
556+ w := MapNotifyOf(&back)
557+ if w.Event() != vMapNotify.Event() {
558+ t.Fatal("MapNotify.Event")
559+ }
560+ if w.Window() != vMapNotify.Window() {
561+ t.Fatal("MapNotify.Window")
562+ }
563+ if w.OverrideRedirect() != vMapNotify.OverrideRedirect() {
564+ t.Fatal("MapNotify.OverrideRedirect")
565+ }
566+ }
567+ {
568+ vMapRequest := MapRequestOf(&ev)
569+ _ = vMapRequest.Parent()
570+ _ = vMapRequest.Window()
571+ d := MapRequestEventData{Parent: vMapRequest.Parent(), Window: vMapRequest.Window()}
572+ var out [32]byte
573+ d.Put(&out)
574+ if out[0] != 20 {
575+ t.Fatal("MapRequest code")
576+ }
577+ back := xturbo.Event{Raw: out}
578+ w := MapRequestOf(&back)
579+ if w.Parent() != vMapRequest.Parent() {
580+ t.Fatal("MapRequest.Parent")
581+ }
582+ if w.Window() != vMapRequest.Window() {
583+ t.Fatal("MapRequest.Window")
584+ }
585+ }
586+ {
587+ vReparentNotify := ReparentNotifyOf(&ev)
588+ _ = vReparentNotify.Event()
589+ _ = vReparentNotify.Window()
590+ _ = vReparentNotify.Parent()
591+ _ = vReparentNotify.X()
592+ _ = vReparentNotify.Y()
593+ _ = vReparentNotify.OverrideRedirect()
594+ d := ReparentNotifyEventData{Event: vReparentNotify.Event(), Window: vReparentNotify.Window(), Parent: vReparentNotify.Parent(), X: vReparentNotify.X(), Y: vReparentNotify.Y(), OverrideRedirect: vReparentNotify.OverrideRedirect()}
595+ var out [32]byte
596+ d.Put(&out)
597+ if out[0] != 21 {
598+ t.Fatal("ReparentNotify code")
599+ }
600+ back := xturbo.Event{Raw: out}
601+ w := ReparentNotifyOf(&back)
602+ if w.Event() != vReparentNotify.Event() {
603+ t.Fatal("ReparentNotify.Event")
604+ }
605+ if w.Window() != vReparentNotify.Window() {
606+ t.Fatal("ReparentNotify.Window")
607+ }
608+ if w.Parent() != vReparentNotify.Parent() {
609+ t.Fatal("ReparentNotify.Parent")
610+ }
611+ if w.X() != vReparentNotify.X() {
612+ t.Fatal("ReparentNotify.X")
613+ }
614+ if w.Y() != vReparentNotify.Y() {
615+ t.Fatal("ReparentNotify.Y")
616+ }
617+ if w.OverrideRedirect() != vReparentNotify.OverrideRedirect() {
618+ t.Fatal("ReparentNotify.OverrideRedirect")
619+ }
620+ }
621+ {
622+ vConfigureNotify := ConfigureNotifyOf(&ev)
623+ _ = vConfigureNotify.Event()
624+ _ = vConfigureNotify.Window()
625+ _ = vConfigureNotify.AboveSibling()
626+ _ = vConfigureNotify.X()
627+ _ = vConfigureNotify.Y()
628+ _ = vConfigureNotify.Width()
629+ _ = vConfigureNotify.Height()
630+ _ = vConfigureNotify.BorderWidth()
631+ _ = vConfigureNotify.OverrideRedirect()
632+ 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()}
633+ var out [32]byte
634+ d.Put(&out)
635+ if out[0] != 22 {
636+ t.Fatal("ConfigureNotify code")
637+ }
638+ back := xturbo.Event{Raw: out}
639+ w := ConfigureNotifyOf(&back)
640+ if w.Event() != vConfigureNotify.Event() {
641+ t.Fatal("ConfigureNotify.Event")
642+ }
643+ if w.Window() != vConfigureNotify.Window() {
644+ t.Fatal("ConfigureNotify.Window")
645+ }
646+ if w.AboveSibling() != vConfigureNotify.AboveSibling() {
647+ t.Fatal("ConfigureNotify.AboveSibling")
648+ }
649+ if w.X() != vConfigureNotify.X() {
650+ t.Fatal("ConfigureNotify.X")
651+ }
652+ if w.Y() != vConfigureNotify.Y() {
653+ t.Fatal("ConfigureNotify.Y")
654+ }
655+ if w.Width() != vConfigureNotify.Width() {
656+ t.Fatal("ConfigureNotify.Width")
657+ }
658+ if w.Height() != vConfigureNotify.Height() {
659+ t.Fatal("ConfigureNotify.Height")
660+ }
661+ if w.BorderWidth() != vConfigureNotify.BorderWidth() {
662+ t.Fatal("ConfigureNotify.BorderWidth")
663+ }
664+ if w.OverrideRedirect() != vConfigureNotify.OverrideRedirect() {
665+ t.Fatal("ConfigureNotify.OverrideRedirect")
666+ }
667+ }
668+ {
669+ vConfigureRequest := ConfigureRequestOf(&ev)
670+ _ = vConfigureRequest.StackMode()
671+ _ = vConfigureRequest.Parent()
672+ _ = vConfigureRequest.Window()
673+ _ = vConfigureRequest.Sibling()
674+ _ = vConfigureRequest.X()
675+ _ = vConfigureRequest.Y()
676+ _ = vConfigureRequest.Width()
677+ _ = vConfigureRequest.Height()
678+ _ = vConfigureRequest.BorderWidth()
679+ _ = vConfigureRequest.ValueMask()
680+ 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()}
681+ var out [32]byte
682+ d.Put(&out)
683+ if out[0] != 23 {
684+ t.Fatal("ConfigureRequest code")
685+ }
686+ back := xturbo.Event{Raw: out}
687+ w := ConfigureRequestOf(&back)
688+ if w.StackMode() != vConfigureRequest.StackMode() {
689+ t.Fatal("ConfigureRequest.StackMode")
690+ }
691+ if w.Parent() != vConfigureRequest.Parent() {
692+ t.Fatal("ConfigureRequest.Parent")
693+ }
694+ if w.Window() != vConfigureRequest.Window() {
695+ t.Fatal("ConfigureRequest.Window")
696+ }
697+ if w.Sibling() != vConfigureRequest.Sibling() {
698+ t.Fatal("ConfigureRequest.Sibling")
699+ }
700+ if w.X() != vConfigureRequest.X() {
701+ t.Fatal("ConfigureRequest.X")
702+ }
703+ if w.Y() != vConfigureRequest.Y() {
704+ t.Fatal("ConfigureRequest.Y")
705+ }
706+ if w.Width() != vConfigureRequest.Width() {
707+ t.Fatal("ConfigureRequest.Width")
708+ }
709+ if w.Height() != vConfigureRequest.Height() {
710+ t.Fatal("ConfigureRequest.Height")
711+ }
712+ if w.BorderWidth() != vConfigureRequest.BorderWidth() {
713+ t.Fatal("ConfigureRequest.BorderWidth")
714+ }
715+ if w.ValueMask() != vConfigureRequest.ValueMask() {
716+ t.Fatal("ConfigureRequest.ValueMask")
717+ }
718+ }
719+ {
720+ vGravityNotify := GravityNotifyOf(&ev)
721+ _ = vGravityNotify.Event()
722+ _ = vGravityNotify.Window()
723+ _ = vGravityNotify.X()
724+ _ = vGravityNotify.Y()
725+ d := GravityNotifyEventData{Event: vGravityNotify.Event(), Window: vGravityNotify.Window(), X: vGravityNotify.X(), Y: vGravityNotify.Y()}
726+ var out [32]byte
727+ d.Put(&out)
728+ if out[0] != 24 {
729+ t.Fatal("GravityNotify code")
730+ }
731+ back := xturbo.Event{Raw: out}
732+ w := GravityNotifyOf(&back)
733+ if w.Event() != vGravityNotify.Event() {
734+ t.Fatal("GravityNotify.Event")
735+ }
736+ if w.Window() != vGravityNotify.Window() {
737+ t.Fatal("GravityNotify.Window")
738+ }
739+ if w.X() != vGravityNotify.X() {
740+ t.Fatal("GravityNotify.X")
741+ }
742+ if w.Y() != vGravityNotify.Y() {
743+ t.Fatal("GravityNotify.Y")
744+ }
745+ }
746+ {
747+ vResizeRequest := ResizeRequestOf(&ev)
748+ _ = vResizeRequest.Window()
749+ _ = vResizeRequest.Width()
750+ _ = vResizeRequest.Height()
751+ d := ResizeRequestEventData{Window: vResizeRequest.Window(), Width: vResizeRequest.Width(), Height: vResizeRequest.Height()}
752+ var out [32]byte
753+ d.Put(&out)
754+ if out[0] != 25 {
755+ t.Fatal("ResizeRequest code")
756+ }
757+ back := xturbo.Event{Raw: out}
758+ w := ResizeRequestOf(&back)
759+ if w.Window() != vResizeRequest.Window() {
760+ t.Fatal("ResizeRequest.Window")
761+ }
762+ if w.Width() != vResizeRequest.Width() {
763+ t.Fatal("ResizeRequest.Width")
764+ }
765+ if w.Height() != vResizeRequest.Height() {
766+ t.Fatal("ResizeRequest.Height")
767+ }
768+ }
769+ {
770+ vCirculateNotify := CirculateNotifyOf(&ev)
771+ _ = vCirculateNotify.Event()
772+ _ = vCirculateNotify.Window()
773+ _ = vCirculateNotify.Place()
774+ d := CirculateNotifyEventData{Event: vCirculateNotify.Event(), Window: vCirculateNotify.Window(), Place: vCirculateNotify.Place()}
775+ var out [32]byte
776+ d.Put(&out)
777+ if out[0] != 26 {
778+ t.Fatal("CirculateNotify code")
779+ }
780+ back := xturbo.Event{Raw: out}
781+ w := CirculateNotifyOf(&back)
782+ if w.Event() != vCirculateNotify.Event() {
783+ t.Fatal("CirculateNotify.Event")
784+ }
785+ if w.Window() != vCirculateNotify.Window() {
786+ t.Fatal("CirculateNotify.Window")
787+ }
788+ if w.Place() != vCirculateNotify.Place() {
789+ t.Fatal("CirculateNotify.Place")
790+ }
791+ }
792+ _ = CirculateRequestOf(&ev)
793+ {
794+ vPropertyNotify := PropertyNotifyOf(&ev)
795+ _ = vPropertyNotify.Window()
796+ _ = vPropertyNotify.Atom()
797+ _ = vPropertyNotify.Time()
798+ _ = vPropertyNotify.State()
799+ d := PropertyNotifyEventData{Window: vPropertyNotify.Window(), Atom: vPropertyNotify.Atom(), Time: vPropertyNotify.Time(), State: vPropertyNotify.State()}
800+ var out [32]byte
801+ d.Put(&out)
802+ if out[0] != 28 {
803+ t.Fatal("PropertyNotify code")
804+ }
805+ back := xturbo.Event{Raw: out}
806+ w := PropertyNotifyOf(&back)
807+ if w.Window() != vPropertyNotify.Window() {
808+ t.Fatal("PropertyNotify.Window")
809+ }
810+ if w.Atom() != vPropertyNotify.Atom() {
811+ t.Fatal("PropertyNotify.Atom")
812+ }
813+ if w.Time() != vPropertyNotify.Time() {
814+ t.Fatal("PropertyNotify.Time")
815+ }
816+ if w.State() != vPropertyNotify.State() {
817+ t.Fatal("PropertyNotify.State")
818+ }
819+ }
820+ {
821+ vSelectionClear := SelectionClearOf(&ev)
822+ _ = vSelectionClear.Time()
823+ _ = vSelectionClear.Owner()
824+ _ = vSelectionClear.Selection()
825+ d := SelectionClearEventData{Time: vSelectionClear.Time(), Owner: vSelectionClear.Owner(), Selection: vSelectionClear.Selection()}
826+ var out [32]byte
827+ d.Put(&out)
828+ if out[0] != 29 {
829+ t.Fatal("SelectionClear code")
830+ }
831+ back := xturbo.Event{Raw: out}
832+ w := SelectionClearOf(&back)
833+ if w.Time() != vSelectionClear.Time() {
834+ t.Fatal("SelectionClear.Time")
835+ }
836+ if w.Owner() != vSelectionClear.Owner() {
837+ t.Fatal("SelectionClear.Owner")
838+ }
839+ if w.Selection() != vSelectionClear.Selection() {
840+ t.Fatal("SelectionClear.Selection")
841+ }
842+ }
843+ {
844+ vSelectionRequest := SelectionRequestOf(&ev)
845+ _ = vSelectionRequest.Time()
846+ _ = vSelectionRequest.Owner()
847+ _ = vSelectionRequest.Requestor()
848+ _ = vSelectionRequest.Selection()
849+ _ = vSelectionRequest.Target()
850+ _ = vSelectionRequest.Property()
851+ d := SelectionRequestEventData{Time: vSelectionRequest.Time(), Owner: vSelectionRequest.Owner(), Requestor: vSelectionRequest.Requestor(), Selection: vSelectionRequest.Selection(), Target: vSelectionRequest.Target(), Property: vSelectionRequest.Property()}
852+ var out [32]byte
853+ d.Put(&out)
854+ if out[0] != 30 {
855+ t.Fatal("SelectionRequest code")
856+ }
857+ back := xturbo.Event{Raw: out}
858+ w := SelectionRequestOf(&back)
859+ if w.Time() != vSelectionRequest.Time() {
860+ t.Fatal("SelectionRequest.Time")
861+ }
862+ if w.Owner() != vSelectionRequest.Owner() {
863+ t.Fatal("SelectionRequest.Owner")
864+ }
865+ if w.Requestor() != vSelectionRequest.Requestor() {
866+ t.Fatal("SelectionRequest.Requestor")
867+ }
868+ if w.Selection() != vSelectionRequest.Selection() {
869+ t.Fatal("SelectionRequest.Selection")
870+ }
871+ if w.Target() != vSelectionRequest.Target() {
872+ t.Fatal("SelectionRequest.Target")
873+ }
874+ if w.Property() != vSelectionRequest.Property() {
875+ t.Fatal("SelectionRequest.Property")
876+ }
877+ }
878+ {
879+ vSelectionNotify := SelectionNotifyOf(&ev)
880+ _ = vSelectionNotify.Time()
881+ _ = vSelectionNotify.Requestor()
882+ _ = vSelectionNotify.Selection()
883+ _ = vSelectionNotify.Target()
884+ _ = vSelectionNotify.Property()
885+ d := SelectionNotifyEventData{Time: vSelectionNotify.Time(), Requestor: vSelectionNotify.Requestor(), Selection: vSelectionNotify.Selection(), Target: vSelectionNotify.Target(), Property: vSelectionNotify.Property()}
886+ var out [32]byte
887+ d.Put(&out)
888+ if out[0] != 31 {
889+ t.Fatal("SelectionNotify code")
890+ }
891+ back := xturbo.Event{Raw: out}
892+ w := SelectionNotifyOf(&back)
893+ if w.Time() != vSelectionNotify.Time() {
894+ t.Fatal("SelectionNotify.Time")
895+ }
896+ if w.Requestor() != vSelectionNotify.Requestor() {
897+ t.Fatal("SelectionNotify.Requestor")
898+ }
899+ if w.Selection() != vSelectionNotify.Selection() {
900+ t.Fatal("SelectionNotify.Selection")
901+ }
902+ if w.Target() != vSelectionNotify.Target() {
903+ t.Fatal("SelectionNotify.Target")
904+ }
905+ if w.Property() != vSelectionNotify.Property() {
906+ t.Fatal("SelectionNotify.Property")
907+ }
908+ }
909+ {
910+ vColormapNotify := ColormapNotifyOf(&ev)
911+ _ = vColormapNotify.Window()
912+ _ = vColormapNotify.Colormap()
913+ _ = vColormapNotify.New()
914+ _ = vColormapNotify.State()
915+ d := ColormapNotifyEventData{Window: vColormapNotify.Window(), Colormap: vColormapNotify.Colormap(), New: vColormapNotify.New(), State: vColormapNotify.State()}
916+ var out [32]byte
917+ d.Put(&out)
918+ if out[0] != 32 {
919+ t.Fatal("ColormapNotify code")
920+ }
921+ back := xturbo.Event{Raw: out}
922+ w := ColormapNotifyOf(&back)
923+ if w.Window() != vColormapNotify.Window() {
924+ t.Fatal("ColormapNotify.Window")
925+ }
926+ if w.Colormap() != vColormapNotify.Colormap() {
927+ t.Fatal("ColormapNotify.Colormap")
928+ }
929+ if w.New() != vColormapNotify.New() {
930+ t.Fatal("ColormapNotify.New")
931+ }
932+ if w.State() != vColormapNotify.State() {
933+ t.Fatal("ColormapNotify.State")
934+ }
935+ }
936+ {
937+ vClientMessage := ClientMessageOf(&ev)
938+ _ = vClientMessage.Format()
939+ _ = vClientMessage.Window()
940+ _ = vClientMessage.Type()
941+ _ = vClientMessage.Data()
942+ d := ClientMessageEventData{Format: vClientMessage.Format(), Window: vClientMessage.Window(), Type: vClientMessage.Type(), Data: vClientMessage.Data()}
943+ var out [32]byte
944+ d.Put(&out)
945+ if out[0] != 33 {
946+ t.Fatal("ClientMessage code")
947+ }
948+ back := xturbo.Event{Raw: out}
949+ w := ClientMessageOf(&back)
950+ if w.Format() != vClientMessage.Format() {
951+ t.Fatal("ClientMessage.Format")
952+ }
953+ if w.Window() != vClientMessage.Window() {
954+ t.Fatal("ClientMessage.Window")
955+ }
956+ if w.Type() != vClientMessage.Type() {
957+ t.Fatal("ClientMessage.Type")
958+ }
959+ if !bytes.Equal(out[12:32], ev.Raw[12:32]) {
960+ t.Fatal("ClientMessage.Data")
961+ }
962+ }
963+ {
964+ vMappingNotify := MappingNotifyOf(&ev)
965+ _ = vMappingNotify.Request()
966+ _ = vMappingNotify.FirstKeycode()
967+ _ = vMappingNotify.Count()
968+ d := MappingNotifyEventData{Request: vMappingNotify.Request(), FirstKeycode: vMappingNotify.FirstKeycode(), Count: vMappingNotify.Count()}
969+ var out [32]byte
970+ d.Put(&out)
971+ if out[0] != 34 {
972+ t.Fatal("MappingNotify code")
973+ }
974+ back := xturbo.Event{Raw: out}
975+ w := MappingNotifyOf(&back)
976+ if w.Request() != vMappingNotify.Request() {
977+ t.Fatal("MappingNotify.Request")
978+ }
979+ if w.FirstKeycode() != vMappingNotify.FirstKeycode() {
980+ t.Fatal("MappingNotify.FirstKeycode")
981+ }
982+ if w.Count() != vMappingNotify.Count() {
983+ t.Fatal("MappingNotify.Count")
984+ }
985+ }
986+ if vGeGeneric, ok := GeGenericOf(&ev); ok {
987+ _ = vGeGeneric.Extension()
988+ _ = vGeGeneric.Length()
989+ _ = vGeGeneric.EventType()
990+ _ = len(vGeGeneric.Tail())
991+ }
992+ })
993+}
A xproto/events_generated.go +1201 -0
@@ -0,0 +1,1201 @@
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.
44+func (d *KeyPressEventData) Put(dst *[32]byte) {
45+ clear(dst[:])
46+ dst[0] = 2
47+ dst[1:][0] = byte(d.Detail)
48+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Time))
49+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Root))
50+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Event))
51+ binary.LittleEndian.PutUint32(dst[16:], uint32(d.Child))
52+ binary.LittleEndian.PutUint16(dst[20:], uint16(d.RootX))
53+ binary.LittleEndian.PutUint16(dst[22:], uint16(d.RootY))
54+ binary.LittleEndian.PutUint16(dst[24:], uint16(d.EventX))
55+ binary.LittleEndian.PutUint16(dst[26:], uint16(d.EventY))
56+ binary.LittleEndian.PutUint16(dst[28:], uint16(d.State))
57+ if d.SameScreen {
58+ dst[30:][0] = 1
59+ } else {
60+ dst[30:][0] = 0
61+ }
62+}
63+
64+// KindKeyRelease identifies KeyRelease events in xturbo.Event.Kind.
65+const KindKeyRelease = xturbo.EventKind(3)
66+
67+// KeyReleaseEvent shares KeyPress's layout (eventcopy).
68+type KeyReleaseEvent = KeyPressEvent
69+
70+// KeyReleaseOf views ev as KeyRelease without checking ev.Kind.
71+func KeyReleaseOf(ev *xturbo.Event) KeyReleaseEvent { return KeyPressOf(ev) }
72+
73+// KeyReleaseEventData encodes a KeyRelease event for SendEvent with KeyPress's fields.
74+type KeyReleaseEventData KeyPressEventData
75+
76+// Put writes the event (code 3, pads and sequence zeroed) into dst.
77+func (d *KeyReleaseEventData) Put(dst *[32]byte) { (*KeyPressEventData)(d).Put(dst); dst[0] = 3 }
78+
79+// KindButtonPress identifies ButtonPress events in xturbo.Event.Kind.
80+const KindButtonPress = xturbo.EventKind(4)
81+
82+// 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.
83+type ButtonPressEvent struct{ raw *[32]byte }
84+
85+// ButtonPressOf views ev as ButtonPress without checking ev.Kind: a wrong kind yields garbage, never a panic.
86+func ButtonPressOf(ev *xturbo.Event) ButtonPressEvent { return ButtonPressEvent{raw: &ev.Raw} }
87+func (e ButtonPressEvent) Raw() *[32]byte { return e.raw }
88+func (e ButtonPressEvent) Detail() Button { return Button(e.raw[1:][0]) }
89+func (e ButtonPressEvent) Time() Timestamp { return Timestamp(binary.LittleEndian.Uint32(e.raw[4:])) }
90+func (e ButtonPressEvent) Root() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
91+func (e ButtonPressEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[12:])) }
92+func (e ButtonPressEvent) Child() Window { return Window(binary.LittleEndian.Uint32(e.raw[16:])) }
93+func (e ButtonPressEvent) RootX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[20:])) }
94+func (e ButtonPressEvent) RootY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[22:])) }
95+func (e ButtonPressEvent) EventX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[24:])) }
96+func (e ButtonPressEvent) EventY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[26:])) }
97+func (e ButtonPressEvent) State() KeyButMask {
98+ return KeyButMask(binary.LittleEndian.Uint16(e.raw[28:]))
99+}
100+func (e ButtonPressEvent) SameScreen() bool { return (e.raw[30:][0] != 0) }
101+
102+// ButtonPressEventData encodes a ButtonPress event for SendEvent.
103+type ButtonPressEventData struct {
104+ Detail Button
105+ Time Timestamp
106+ Root Window
107+ Event Window
108+ Child Window
109+ RootX int16
110+ RootY int16
111+ EventX int16
112+ EventY int16
113+ State KeyButMask
114+ SameScreen bool
115+}
116+
117+// Put writes the event (code 4, pads and sequence zeroed) into dst.
118+func (d *ButtonPressEventData) Put(dst *[32]byte) {
119+ clear(dst[:])
120+ dst[0] = 4
121+ dst[1:][0] = byte(d.Detail)
122+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Time))
123+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Root))
124+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Event))
125+ binary.LittleEndian.PutUint32(dst[16:], uint32(d.Child))
126+ binary.LittleEndian.PutUint16(dst[20:], uint16(d.RootX))
127+ binary.LittleEndian.PutUint16(dst[22:], uint16(d.RootY))
128+ binary.LittleEndian.PutUint16(dst[24:], uint16(d.EventX))
129+ binary.LittleEndian.PutUint16(dst[26:], uint16(d.EventY))
130+ binary.LittleEndian.PutUint16(dst[28:], uint16(d.State))
131+ if d.SameScreen {
132+ dst[30:][0] = 1
133+ } else {
134+ dst[30:][0] = 0
135+ }
136+}
137+
138+// KindButtonRelease identifies ButtonRelease events in xturbo.Event.Kind.
139+const KindButtonRelease = xturbo.EventKind(5)
140+
141+// ButtonReleaseEvent shares ButtonPress's layout (eventcopy).
142+type ButtonReleaseEvent = ButtonPressEvent
143+
144+// ButtonReleaseOf views ev as ButtonRelease without checking ev.Kind.
145+func ButtonReleaseOf(ev *xturbo.Event) ButtonReleaseEvent { return ButtonPressOf(ev) }
146+
147+// ButtonReleaseEventData encodes a ButtonRelease event for SendEvent with ButtonPress's fields.
148+type ButtonReleaseEventData ButtonPressEventData
149+
150+// Put writes the event (code 5, pads and sequence zeroed) into dst.
151+func (d *ButtonReleaseEventData) Put(dst *[32]byte) { (*ButtonPressEventData)(d).Put(dst); dst[0] = 5 }
152+
153+// KindMotionNotify identifies MotionNotify events in xturbo.Event.Kind.
154+const KindMotionNotify = xturbo.EventKind(6)
155+
156+// 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.
157+type MotionNotifyEvent struct{ raw *[32]byte }
158+
159+// MotionNotifyOf views ev as MotionNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
160+func MotionNotifyOf(ev *xturbo.Event) MotionNotifyEvent { return MotionNotifyEvent{raw: &ev.Raw} }
161+func (e MotionNotifyEvent) Raw() *[32]byte { return e.raw }
162+func (e MotionNotifyEvent) Detail() Motion { return Motion(e.raw[1:][0]) }
163+func (e MotionNotifyEvent) Time() Timestamp { return Timestamp(binary.LittleEndian.Uint32(e.raw[4:])) }
164+func (e MotionNotifyEvent) Root() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
165+func (e MotionNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[12:])) }
166+func (e MotionNotifyEvent) Child() Window { return Window(binary.LittleEndian.Uint32(e.raw[16:])) }
167+func (e MotionNotifyEvent) RootX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[20:])) }
168+func (e MotionNotifyEvent) RootY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[22:])) }
169+func (e MotionNotifyEvent) EventX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[24:])) }
170+func (e MotionNotifyEvent) EventY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[26:])) }
171+func (e MotionNotifyEvent) State() KeyButMask {
172+ return KeyButMask(binary.LittleEndian.Uint16(e.raw[28:]))
173+}
174+func (e MotionNotifyEvent) SameScreen() bool { return (e.raw[30:][0] != 0) }
175+
176+// MotionNotifyEventData encodes a MotionNotify event for SendEvent.
177+type MotionNotifyEventData struct {
178+ Detail Motion
179+ Time Timestamp
180+ Root Window
181+ Event Window
182+ Child Window
183+ RootX int16
184+ RootY int16
185+ EventX int16
186+ EventY int16
187+ State KeyButMask
188+ SameScreen bool
189+}
190+
191+// Put writes the event (code 6, pads and sequence zeroed) into dst.
192+func (d *MotionNotifyEventData) Put(dst *[32]byte) {
193+ clear(dst[:])
194+ dst[0] = 6
195+ dst[1:][0] = byte(d.Detail)
196+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Time))
197+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Root))
198+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Event))
199+ binary.LittleEndian.PutUint32(dst[16:], uint32(d.Child))
200+ binary.LittleEndian.PutUint16(dst[20:], uint16(d.RootX))
201+ binary.LittleEndian.PutUint16(dst[22:], uint16(d.RootY))
202+ binary.LittleEndian.PutUint16(dst[24:], uint16(d.EventX))
203+ binary.LittleEndian.PutUint16(dst[26:], uint16(d.EventY))
204+ binary.LittleEndian.PutUint16(dst[28:], uint16(d.State))
205+ if d.SameScreen {
206+ dst[30:][0] = 1
207+ } else {
208+ dst[30:][0] = 0
209+ }
210+}
211+
212+// KindEnterNotify identifies EnterNotify events in xturbo.Event.Kind.
213+const KindEnterNotify = xturbo.EventKind(7)
214+
215+// 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.
216+type EnterNotifyEvent struct{ raw *[32]byte }
217+
218+// EnterNotifyOf views ev as EnterNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
219+func EnterNotifyOf(ev *xturbo.Event) EnterNotifyEvent { return EnterNotifyEvent{raw: &ev.Raw} }
220+func (e EnterNotifyEvent) Raw() *[32]byte { return e.raw }
221+func (e EnterNotifyEvent) Detail() NotifyDetail { return NotifyDetail(e.raw[1:][0]) }
222+func (e EnterNotifyEvent) Time() Timestamp { return Timestamp(binary.LittleEndian.Uint32(e.raw[4:])) }
223+func (e EnterNotifyEvent) Root() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
224+func (e EnterNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[12:])) }
225+func (e EnterNotifyEvent) Child() Window { return Window(binary.LittleEndian.Uint32(e.raw[16:])) }
226+func (e EnterNotifyEvent) RootX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[20:])) }
227+func (e EnterNotifyEvent) RootY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[22:])) }
228+func (e EnterNotifyEvent) EventX() int16 { return int16(binary.LittleEndian.Uint16(e.raw[24:])) }
229+func (e EnterNotifyEvent) EventY() int16 { return int16(binary.LittleEndian.Uint16(e.raw[26:])) }
230+func (e EnterNotifyEvent) State() KeyButMask {
231+ return KeyButMask(binary.LittleEndian.Uint16(e.raw[28:]))
232+}
233+func (e EnterNotifyEvent) Mode() NotifyMode { return NotifyMode(e.raw[30:][0]) }
234+func (e EnterNotifyEvent) SameScreenFocus() byte { return byte(e.raw[31:][0]) }
235+
236+// EnterNotifyEventData encodes a EnterNotify event for SendEvent.
237+type EnterNotifyEventData struct {
238+ Detail NotifyDetail
239+ Time Timestamp
240+ Root Window
241+ Event Window
242+ Child Window
243+ RootX int16
244+ RootY int16
245+ EventX int16
246+ EventY int16
247+ State KeyButMask
248+ Mode NotifyMode
249+ SameScreenFocus byte
250+}
251+
252+// Put writes the event (code 7, pads and sequence zeroed) into dst.
253+func (d *EnterNotifyEventData) Put(dst *[32]byte) {
254+ clear(dst[:])
255+ dst[0] = 7
256+ dst[1:][0] = byte(d.Detail)
257+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Time))
258+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Root))
259+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Event))
260+ binary.LittleEndian.PutUint32(dst[16:], uint32(d.Child))
261+ binary.LittleEndian.PutUint16(dst[20:], uint16(d.RootX))
262+ binary.LittleEndian.PutUint16(dst[22:], uint16(d.RootY))
263+ binary.LittleEndian.PutUint16(dst[24:], uint16(d.EventX))
264+ binary.LittleEndian.PutUint16(dst[26:], uint16(d.EventY))
265+ binary.LittleEndian.PutUint16(dst[28:], uint16(d.State))
266+ dst[30:][0] = byte(d.Mode)
267+ dst[31:][0] = byte(d.SameScreenFocus)
268+}
269+
270+// KindLeaveNotify identifies LeaveNotify events in xturbo.Event.Kind.
271+const KindLeaveNotify = xturbo.EventKind(8)
272+
273+// LeaveNotifyEvent shares EnterNotify's layout (eventcopy).
274+type LeaveNotifyEvent = EnterNotifyEvent
275+
276+// LeaveNotifyOf views ev as LeaveNotify without checking ev.Kind.
277+func LeaveNotifyOf(ev *xturbo.Event) LeaveNotifyEvent { return EnterNotifyOf(ev) }
278+
279+// LeaveNotifyEventData encodes a LeaveNotify event for SendEvent with EnterNotify's fields.
280+type LeaveNotifyEventData EnterNotifyEventData
281+
282+// Put writes the event (code 8, pads and sequence zeroed) into dst.
283+func (d *LeaveNotifyEventData) Put(dst *[32]byte) { (*EnterNotifyEventData)(d).Put(dst); dst[0] = 8 }
284+
285+// KindFocusIn identifies FocusIn events in xturbo.Event.Kind.
286+const KindFocusIn = xturbo.EventKind(9)
287+
288+// 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.
289+type FocusInEvent struct{ raw *[32]byte }
290+
291+// FocusInOf views ev as FocusIn without checking ev.Kind: a wrong kind yields garbage, never a panic.
292+func FocusInOf(ev *xturbo.Event) FocusInEvent { return FocusInEvent{raw: &ev.Raw} }
293+func (e FocusInEvent) Raw() *[32]byte { return e.raw }
294+func (e FocusInEvent) Detail() NotifyDetail { return NotifyDetail(e.raw[1:][0]) }
295+func (e FocusInEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
296+func (e FocusInEvent) Mode() NotifyMode { return NotifyMode(e.raw[8:][0]) }
297+
298+// FocusInEventData encodes a FocusIn event for SendEvent.
299+type FocusInEventData struct {
300+ Detail NotifyDetail
301+ Event Window
302+ Mode NotifyMode
303+}
304+
305+// Put writes the event (code 9, pads and sequence zeroed) into dst.
306+func (d *FocusInEventData) Put(dst *[32]byte) {
307+ clear(dst[:])
308+ dst[0] = 9
309+ dst[1:][0] = byte(d.Detail)
310+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Event))
311+ dst[8:][0] = byte(d.Mode)
312+}
313+
314+// KindFocusOut identifies FocusOut events in xturbo.Event.Kind.
315+const KindFocusOut = xturbo.EventKind(10)
316+
317+// FocusOutEvent shares FocusIn's layout (eventcopy).
318+type FocusOutEvent = FocusInEvent
319+
320+// FocusOutOf views ev as FocusOut without checking ev.Kind.
321+func FocusOutOf(ev *xturbo.Event) FocusOutEvent { return FocusInOf(ev) }
322+
323+// FocusOutEventData encodes a FocusOut event for SendEvent with FocusIn's fields.
324+type FocusOutEventData FocusInEventData
325+
326+// Put writes the event (code 10, pads and sequence zeroed) into dst.
327+func (d *FocusOutEventData) Put(dst *[32]byte) { (*FocusInEventData)(d).Put(dst); dst[0] = 10 }
328+
329+// KindKeymapNotify identifies KeymapNotify events in xturbo.Event.Kind.
330+const KindKeymapNotify = xturbo.EventKind(11)
331+
332+// 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.
333+type KeymapNotifyEvent struct{ raw *[32]byte }
334+
335+// KeymapNotifyOf views ev as KeymapNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
336+func KeymapNotifyOf(ev *xturbo.Event) KeymapNotifyEvent { return KeymapNotifyEvent{raw: &ev.Raw} }
337+func (e KeymapNotifyEvent) Raw() *[32]byte { return e.raw }
338+func (e KeymapNotifyEvent) Keys() []byte { off := 1; end := off + 31; return e.raw[off:end:end] }
339+
340+// KeymapNotifyEventData encodes a KeymapNotify event for SendEvent.
341+type KeymapNotifyEventData struct {
342+ Keys [31]uint8
343+}
344+
345+// Put writes the event (code 11, pads and sequence zeroed) into dst.
346+func (d *KeymapNotifyEventData) Put(dst *[32]byte) {
347+ clear(dst[:])
348+ dst[0] = 11
349+ for i := range d.Keys {
350+ dst[1+i*1:][0] = byte(d.Keys[i])
351+ }
352+}
353+
354+// KindExpose identifies Expose events in xturbo.Event.Kind.
355+const KindExpose = xturbo.EventKind(12)
356+
357+// 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.
358+type ExposeEvent struct{ raw *[32]byte }
359+
360+// ExposeOf views ev as Expose without checking ev.Kind: a wrong kind yields garbage, never a panic.
361+func ExposeOf(ev *xturbo.Event) ExposeEvent { return ExposeEvent{raw: &ev.Raw} }
362+func (e ExposeEvent) Raw() *[32]byte { return e.raw }
363+func (e ExposeEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
364+func (e ExposeEvent) X() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[8:])) }
365+func (e ExposeEvent) Y() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[10:])) }
366+func (e ExposeEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[12:])) }
367+func (e ExposeEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[14:])) }
368+func (e ExposeEvent) Count() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[16:])) }
369+
370+// ExposeEventData encodes a Expose event for SendEvent.
371+type ExposeEventData struct {
372+ Window Window
373+ X uint16
374+ Y uint16
375+ Width uint16
376+ Height uint16
377+ Count uint16
378+}
379+
380+// Put writes the event (code 12, pads and sequence zeroed) into dst.
381+func (d *ExposeEventData) Put(dst *[32]byte) {
382+ clear(dst[:])
383+ dst[0] = 12
384+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Window))
385+ binary.LittleEndian.PutUint16(dst[8:], uint16(d.X))
386+ binary.LittleEndian.PutUint16(dst[10:], uint16(d.Y))
387+ binary.LittleEndian.PutUint16(dst[12:], uint16(d.Width))
388+ binary.LittleEndian.PutUint16(dst[14:], uint16(d.Height))
389+ binary.LittleEndian.PutUint16(dst[16:], uint16(d.Count))
390+}
391+
392+// KindGraphicsExposure identifies GraphicsExposure events in xturbo.Event.Kind.
393+const KindGraphicsExposure = xturbo.EventKind(13)
394+
395+// 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.
396+type GraphicsExposureEvent struct{ raw *[32]byte }
397+
398+// GraphicsExposureOf views ev as GraphicsExposure without checking ev.Kind: a wrong kind yields garbage, never a panic.
399+func GraphicsExposureOf(ev *xturbo.Event) GraphicsExposureEvent {
400+ return GraphicsExposureEvent{raw: &ev.Raw}
401+}
402+func (e GraphicsExposureEvent) Raw() *[32]byte { return e.raw }
403+func (e GraphicsExposureEvent) Drawable() Drawable {
404+ return Drawable(binary.LittleEndian.Uint32(e.raw[4:]))
405+}
406+func (e GraphicsExposureEvent) X() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[8:])) }
407+func (e GraphicsExposureEvent) Y() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[10:])) }
408+func (e GraphicsExposureEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[12:])) }
409+func (e GraphicsExposureEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[14:])) }
410+func (e GraphicsExposureEvent) MinorOpcode() uint16 {
411+ return uint16(binary.LittleEndian.Uint16(e.raw[16:]))
412+}
413+func (e GraphicsExposureEvent) Count() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[18:])) }
414+func (e GraphicsExposureEvent) MajorOpcode() uint8 { return uint8(e.raw[20:][0]) }
415+
416+// GraphicsExposureEventData encodes a GraphicsExposure event for SendEvent.
417+type GraphicsExposureEventData struct {
418+ Drawable Drawable
419+ X uint16
420+ Y uint16
421+ Width uint16
422+ Height uint16
423+ MinorOpcode uint16
424+ Count uint16
425+ MajorOpcode uint8
426+}
427+
428+// Put writes the event (code 13, pads and sequence zeroed) into dst.
429+func (d *GraphicsExposureEventData) Put(dst *[32]byte) {
430+ clear(dst[:])
431+ dst[0] = 13
432+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Drawable))
433+ binary.LittleEndian.PutUint16(dst[8:], uint16(d.X))
434+ binary.LittleEndian.PutUint16(dst[10:], uint16(d.Y))
435+ binary.LittleEndian.PutUint16(dst[12:], uint16(d.Width))
436+ binary.LittleEndian.PutUint16(dst[14:], uint16(d.Height))
437+ binary.LittleEndian.PutUint16(dst[16:], uint16(d.MinorOpcode))
438+ binary.LittleEndian.PutUint16(dst[18:], uint16(d.Count))
439+ dst[20:][0] = byte(d.MajorOpcode)
440+}
441+
442+// KindNoExposure identifies NoExposure events in xturbo.Event.Kind.
443+const KindNoExposure = xturbo.EventKind(14)
444+
445+// 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.
446+type NoExposureEvent struct{ raw *[32]byte }
447+
448+// NoExposureOf views ev as NoExposure without checking ev.Kind: a wrong kind yields garbage, never a panic.
449+func NoExposureOf(ev *xturbo.Event) NoExposureEvent { return NoExposureEvent{raw: &ev.Raw} }
450+func (e NoExposureEvent) Raw() *[32]byte { return e.raw }
451+func (e NoExposureEvent) Drawable() Drawable { return Drawable(binary.LittleEndian.Uint32(e.raw[4:])) }
452+func (e NoExposureEvent) MinorOpcode() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[8:])) }
453+func (e NoExposureEvent) MajorOpcode() uint8 { return uint8(e.raw[10:][0]) }
454+
455+// NoExposureEventData encodes a NoExposure event for SendEvent.
456+type NoExposureEventData struct {
457+ Drawable Drawable
458+ MinorOpcode uint16
459+ MajorOpcode uint8
460+}
461+
462+// Put writes the event (code 14, pads and sequence zeroed) into dst.
463+func (d *NoExposureEventData) Put(dst *[32]byte) {
464+ clear(dst[:])
465+ dst[0] = 14
466+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Drawable))
467+ binary.LittleEndian.PutUint16(dst[8:], uint16(d.MinorOpcode))
468+ dst[10:][0] = byte(d.MajorOpcode)
469+}
470+
471+// KindVisibilityNotify identifies VisibilityNotify events in xturbo.Event.Kind.
472+const KindVisibilityNotify = xturbo.EventKind(15)
473+
474+// 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.
475+type VisibilityNotifyEvent struct{ raw *[32]byte }
476+
477+// VisibilityNotifyOf views ev as VisibilityNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
478+func VisibilityNotifyOf(ev *xturbo.Event) VisibilityNotifyEvent {
479+ return VisibilityNotifyEvent{raw: &ev.Raw}
480+}
481+func (e VisibilityNotifyEvent) Raw() *[32]byte { return e.raw }
482+func (e VisibilityNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
483+func (e VisibilityNotifyEvent) State() Visibility { return Visibility(e.raw[8:][0]) }
484+
485+// VisibilityNotifyEventData encodes a VisibilityNotify event for SendEvent.
486+type VisibilityNotifyEventData struct {
487+ Window Window
488+ State Visibility
489+}
490+
491+// Put writes the event (code 15, pads and sequence zeroed) into dst.
492+func (d *VisibilityNotifyEventData) Put(dst *[32]byte) {
493+ clear(dst[:])
494+ dst[0] = 15
495+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Window))
496+ dst[8:][0] = byte(d.State)
497+}
498+
499+// KindCreateNotify identifies CreateNotify events in xturbo.Event.Kind.
500+const KindCreateNotify = xturbo.EventKind(16)
501+
502+// 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.
503+type CreateNotifyEvent struct{ raw *[32]byte }
504+
505+// CreateNotifyOf views ev as CreateNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
506+func CreateNotifyOf(ev *xturbo.Event) CreateNotifyEvent { return CreateNotifyEvent{raw: &ev.Raw} }
507+func (e CreateNotifyEvent) Raw() *[32]byte { return e.raw }
508+func (e CreateNotifyEvent) Parent() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
509+func (e CreateNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
510+func (e CreateNotifyEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[12:])) }
511+func (e CreateNotifyEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[14:])) }
512+func (e CreateNotifyEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[16:])) }
513+func (e CreateNotifyEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[18:])) }
514+func (e CreateNotifyEvent) BorderWidth() uint16 {
515+ return uint16(binary.LittleEndian.Uint16(e.raw[20:]))
516+}
517+func (e CreateNotifyEvent) OverrideRedirect() bool { return (e.raw[22:][0] != 0) }
518+
519+// CreateNotifyEventData encodes a CreateNotify event for SendEvent.
520+type CreateNotifyEventData struct {
521+ Parent Window
522+ Window Window
523+ X int16
524+ Y int16
525+ Width uint16
526+ Height uint16
527+ BorderWidth uint16
528+ OverrideRedirect bool
529+}
530+
531+// Put writes the event (code 16, pads and sequence zeroed) into dst.
532+func (d *CreateNotifyEventData) Put(dst *[32]byte) {
533+ clear(dst[:])
534+ dst[0] = 16
535+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Parent))
536+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
537+ binary.LittleEndian.PutUint16(dst[12:], uint16(d.X))
538+ binary.LittleEndian.PutUint16(dst[14:], uint16(d.Y))
539+ binary.LittleEndian.PutUint16(dst[16:], uint16(d.Width))
540+ binary.LittleEndian.PutUint16(dst[18:], uint16(d.Height))
541+ binary.LittleEndian.PutUint16(dst[20:], uint16(d.BorderWidth))
542+ if d.OverrideRedirect {
543+ dst[22:][0] = 1
544+ } else {
545+ dst[22:][0] = 0
546+ }
547+}
548+
549+// KindDestroyNotify identifies DestroyNotify events in xturbo.Event.Kind.
550+const KindDestroyNotify = xturbo.EventKind(17)
551+
552+// 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.
553+type DestroyNotifyEvent struct{ raw *[32]byte }
554+
555+// DestroyNotifyOf views ev as DestroyNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
556+func DestroyNotifyOf(ev *xturbo.Event) DestroyNotifyEvent { return DestroyNotifyEvent{raw: &ev.Raw} }
557+func (e DestroyNotifyEvent) Raw() *[32]byte { return e.raw }
558+func (e DestroyNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
559+func (e DestroyNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
560+
561+// DestroyNotifyEventData encodes a DestroyNotify event for SendEvent.
562+type DestroyNotifyEventData struct {
563+ Event Window
564+ Window Window
565+}
566+
567+// Put writes the event (code 17, pads and sequence zeroed) into dst.
568+func (d *DestroyNotifyEventData) Put(dst *[32]byte) {
569+ clear(dst[:])
570+ dst[0] = 17
571+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Event))
572+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
573+}
574+
575+// KindUnmapNotify identifies UnmapNotify events in xturbo.Event.Kind.
576+const KindUnmapNotify = xturbo.EventKind(18)
577+
578+// 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.
579+type UnmapNotifyEvent struct{ raw *[32]byte }
580+
581+// UnmapNotifyOf views ev as UnmapNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
582+func UnmapNotifyOf(ev *xturbo.Event) UnmapNotifyEvent { return UnmapNotifyEvent{raw: &ev.Raw} }
583+func (e UnmapNotifyEvent) Raw() *[32]byte { return e.raw }
584+func (e UnmapNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
585+func (e UnmapNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
586+func (e UnmapNotifyEvent) FromConfigure() bool { return (e.raw[12:][0] != 0) }
587+
588+// UnmapNotifyEventData encodes a UnmapNotify event for SendEvent.
589+type UnmapNotifyEventData struct {
590+ Event Window
591+ Window Window
592+ FromConfigure bool
593+}
594+
595+// Put writes the event (code 18, pads and sequence zeroed) into dst.
596+func (d *UnmapNotifyEventData) Put(dst *[32]byte) {
597+ clear(dst[:])
598+ dst[0] = 18
599+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Event))
600+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
601+ if d.FromConfigure {
602+ dst[12:][0] = 1
603+ } else {
604+ dst[12:][0] = 0
605+ }
606+}
607+
608+// KindMapNotify identifies MapNotify events in xturbo.Event.Kind.
609+const KindMapNotify = xturbo.EventKind(19)
610+
611+// 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.
612+type MapNotifyEvent struct{ raw *[32]byte }
613+
614+// MapNotifyOf views ev as MapNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
615+func MapNotifyOf(ev *xturbo.Event) MapNotifyEvent { return MapNotifyEvent{raw: &ev.Raw} }
616+func (e MapNotifyEvent) Raw() *[32]byte { return e.raw }
617+func (e MapNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
618+func (e MapNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
619+func (e MapNotifyEvent) OverrideRedirect() bool { return (e.raw[12:][0] != 0) }
620+
621+// MapNotifyEventData encodes a MapNotify event for SendEvent.
622+type MapNotifyEventData struct {
623+ Event Window
624+ Window Window
625+ OverrideRedirect bool
626+}
627+
628+// Put writes the event (code 19, pads and sequence zeroed) into dst.
629+func (d *MapNotifyEventData) Put(dst *[32]byte) {
630+ clear(dst[:])
631+ dst[0] = 19
632+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Event))
633+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
634+ if d.OverrideRedirect {
635+ dst[12:][0] = 1
636+ } else {
637+ dst[12:][0] = 0
638+ }
639+}
640+
641+// KindMapRequest identifies MapRequest events in xturbo.Event.Kind.
642+const KindMapRequest = xturbo.EventKind(20)
643+
644+// 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.
645+type MapRequestEvent struct{ raw *[32]byte }
646+
647+// MapRequestOf views ev as MapRequest without checking ev.Kind: a wrong kind yields garbage, never a panic.
648+func MapRequestOf(ev *xturbo.Event) MapRequestEvent { return MapRequestEvent{raw: &ev.Raw} }
649+func (e MapRequestEvent) Raw() *[32]byte { return e.raw }
650+func (e MapRequestEvent) Parent() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
651+func (e MapRequestEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
652+
653+// MapRequestEventData encodes a MapRequest event for SendEvent.
654+type MapRequestEventData struct {
655+ Parent Window
656+ Window Window
657+}
658+
659+// Put writes the event (code 20, pads and sequence zeroed) into dst.
660+func (d *MapRequestEventData) Put(dst *[32]byte) {
661+ clear(dst[:])
662+ dst[0] = 20
663+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Parent))
664+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
665+}
666+
667+// KindReparentNotify identifies ReparentNotify events in xturbo.Event.Kind.
668+const KindReparentNotify = xturbo.EventKind(21)
669+
670+// 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.
671+type ReparentNotifyEvent struct{ raw *[32]byte }
672+
673+// ReparentNotifyOf views ev as ReparentNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
674+func ReparentNotifyOf(ev *xturbo.Event) ReparentNotifyEvent { return ReparentNotifyEvent{raw: &ev.Raw} }
675+func (e ReparentNotifyEvent) Raw() *[32]byte { return e.raw }
676+func (e ReparentNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
677+func (e ReparentNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
678+func (e ReparentNotifyEvent) Parent() Window { return Window(binary.LittleEndian.Uint32(e.raw[12:])) }
679+func (e ReparentNotifyEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[16:])) }
680+func (e ReparentNotifyEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[18:])) }
681+func (e ReparentNotifyEvent) OverrideRedirect() bool { return (e.raw[20:][0] != 0) }
682+
683+// ReparentNotifyEventData encodes a ReparentNotify event for SendEvent.
684+type ReparentNotifyEventData struct {
685+ Event Window
686+ Window Window
687+ Parent Window
688+ X int16
689+ Y int16
690+ OverrideRedirect bool
691+}
692+
693+// Put writes the event (code 21, pads and sequence zeroed) into dst.
694+func (d *ReparentNotifyEventData) Put(dst *[32]byte) {
695+ clear(dst[:])
696+ dst[0] = 21
697+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Event))
698+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
699+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Parent))
700+ binary.LittleEndian.PutUint16(dst[16:], uint16(d.X))
701+ binary.LittleEndian.PutUint16(dst[18:], uint16(d.Y))
702+ if d.OverrideRedirect {
703+ dst[20:][0] = 1
704+ } else {
705+ dst[20:][0] = 0
706+ }
707+}
708+
709+// KindConfigureNotify identifies ConfigureNotify events in xturbo.Event.Kind.
710+const KindConfigureNotify = xturbo.EventKind(22)
711+
712+// 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.
713+type ConfigureNotifyEvent struct{ raw *[32]byte }
714+
715+// ConfigureNotifyOf views ev as ConfigureNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
716+func ConfigureNotifyOf(ev *xturbo.Event) ConfigureNotifyEvent {
717+ return ConfigureNotifyEvent{raw: &ev.Raw}
718+}
719+func (e ConfigureNotifyEvent) Raw() *[32]byte { return e.raw }
720+func (e ConfigureNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
721+func (e ConfigureNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
722+func (e ConfigureNotifyEvent) AboveSibling() Window {
723+ return Window(binary.LittleEndian.Uint32(e.raw[12:]))
724+}
725+func (e ConfigureNotifyEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[16:])) }
726+func (e ConfigureNotifyEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[18:])) }
727+func (e ConfigureNotifyEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[20:])) }
728+func (e ConfigureNotifyEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[22:])) }
729+func (e ConfigureNotifyEvent) BorderWidth() uint16 {
730+ return uint16(binary.LittleEndian.Uint16(e.raw[24:]))
731+}
732+func (e ConfigureNotifyEvent) OverrideRedirect() bool { return (e.raw[26:][0] != 0) }
733+
734+// ConfigureNotifyEventData encodes a ConfigureNotify event for SendEvent.
735+type ConfigureNotifyEventData struct {
736+ Event Window
737+ Window Window
738+ AboveSibling Window
739+ X int16
740+ Y int16
741+ Width uint16
742+ Height uint16
743+ BorderWidth uint16
744+ OverrideRedirect bool
745+}
746+
747+// Put writes the event (code 22, pads and sequence zeroed) into dst.
748+func (d *ConfigureNotifyEventData) Put(dst *[32]byte) {
749+ clear(dst[:])
750+ dst[0] = 22
751+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Event))
752+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
753+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.AboveSibling))
754+ binary.LittleEndian.PutUint16(dst[16:], uint16(d.X))
755+ binary.LittleEndian.PutUint16(dst[18:], uint16(d.Y))
756+ binary.LittleEndian.PutUint16(dst[20:], uint16(d.Width))
757+ binary.LittleEndian.PutUint16(dst[22:], uint16(d.Height))
758+ binary.LittleEndian.PutUint16(dst[24:], uint16(d.BorderWidth))
759+ if d.OverrideRedirect {
760+ dst[26:][0] = 1
761+ } else {
762+ dst[26:][0] = 0
763+ }
764+}
765+
766+// KindConfigureRequest identifies ConfigureRequest events in xturbo.Event.Kind.
767+const KindConfigureRequest = xturbo.EventKind(23)
768+
769+// 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.
770+type ConfigureRequestEvent struct{ raw *[32]byte }
771+
772+// ConfigureRequestOf views ev as ConfigureRequest without checking ev.Kind: a wrong kind yields garbage, never a panic.
773+func ConfigureRequestOf(ev *xturbo.Event) ConfigureRequestEvent {
774+ return ConfigureRequestEvent{raw: &ev.Raw}
775+}
776+func (e ConfigureRequestEvent) Raw() *[32]byte { return e.raw }
777+func (e ConfigureRequestEvent) StackMode() StackMode { return StackMode(e.raw[1:][0]) }
778+func (e ConfigureRequestEvent) Parent() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
779+func (e ConfigureRequestEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
780+func (e ConfigureRequestEvent) Sibling() Window {
781+ return Window(binary.LittleEndian.Uint32(e.raw[12:]))
782+}
783+func (e ConfigureRequestEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[16:])) }
784+func (e ConfigureRequestEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[18:])) }
785+func (e ConfigureRequestEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[20:])) }
786+func (e ConfigureRequestEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[22:])) }
787+func (e ConfigureRequestEvent) BorderWidth() uint16 {
788+ return uint16(binary.LittleEndian.Uint16(e.raw[24:]))
789+}
790+func (e ConfigureRequestEvent) ValueMask() ConfigWindow {
791+ return ConfigWindow(binary.LittleEndian.Uint16(e.raw[26:]))
792+}
793+
794+// ConfigureRequestEventData encodes a ConfigureRequest event for SendEvent.
795+type ConfigureRequestEventData struct {
796+ StackMode StackMode
797+ Parent Window
798+ Window Window
799+ Sibling Window
800+ X int16
801+ Y int16
802+ Width uint16
803+ Height uint16
804+ BorderWidth uint16
805+ ValueMask ConfigWindow
806+}
807+
808+// Put writes the event (code 23, pads and sequence zeroed) into dst.
809+func (d *ConfigureRequestEventData) Put(dst *[32]byte) {
810+ clear(dst[:])
811+ dst[0] = 23
812+ dst[1:][0] = byte(d.StackMode)
813+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Parent))
814+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
815+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Sibling))
816+ binary.LittleEndian.PutUint16(dst[16:], uint16(d.X))
817+ binary.LittleEndian.PutUint16(dst[18:], uint16(d.Y))
818+ binary.LittleEndian.PutUint16(dst[20:], uint16(d.Width))
819+ binary.LittleEndian.PutUint16(dst[22:], uint16(d.Height))
820+ binary.LittleEndian.PutUint16(dst[24:], uint16(d.BorderWidth))
821+ binary.LittleEndian.PutUint16(dst[26:], uint16(d.ValueMask))
822+}
823+
824+// KindGravityNotify identifies GravityNotify events in xturbo.Event.Kind.
825+const KindGravityNotify = xturbo.EventKind(24)
826+
827+// 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.
828+type GravityNotifyEvent struct{ raw *[32]byte }
829+
830+// GravityNotifyOf views ev as GravityNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
831+func GravityNotifyOf(ev *xturbo.Event) GravityNotifyEvent { return GravityNotifyEvent{raw: &ev.Raw} }
832+func (e GravityNotifyEvent) Raw() *[32]byte { return e.raw }
833+func (e GravityNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
834+func (e GravityNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
835+func (e GravityNotifyEvent) X() int16 { return int16(binary.LittleEndian.Uint16(e.raw[12:])) }
836+func (e GravityNotifyEvent) Y() int16 { return int16(binary.LittleEndian.Uint16(e.raw[14:])) }
837+
838+// GravityNotifyEventData encodes a GravityNotify event for SendEvent.
839+type GravityNotifyEventData struct {
840+ Event Window
841+ Window Window
842+ X int16
843+ Y int16
844+}
845+
846+// Put writes the event (code 24, pads and sequence zeroed) into dst.
847+func (d *GravityNotifyEventData) Put(dst *[32]byte) {
848+ clear(dst[:])
849+ dst[0] = 24
850+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Event))
851+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
852+ binary.LittleEndian.PutUint16(dst[12:], uint16(d.X))
853+ binary.LittleEndian.PutUint16(dst[14:], uint16(d.Y))
854+}
855+
856+// KindResizeRequest identifies ResizeRequest events in xturbo.Event.Kind.
857+const KindResizeRequest = xturbo.EventKind(25)
858+
859+// 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.
860+type ResizeRequestEvent struct{ raw *[32]byte }
861+
862+// ResizeRequestOf views ev as ResizeRequest without checking ev.Kind: a wrong kind yields garbage, never a panic.
863+func ResizeRequestOf(ev *xturbo.Event) ResizeRequestEvent { return ResizeRequestEvent{raw: &ev.Raw} }
864+func (e ResizeRequestEvent) Raw() *[32]byte { return e.raw }
865+func (e ResizeRequestEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
866+func (e ResizeRequestEvent) Width() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[8:])) }
867+func (e ResizeRequestEvent) Height() uint16 { return uint16(binary.LittleEndian.Uint16(e.raw[10:])) }
868+
869+// ResizeRequestEventData encodes a ResizeRequest event for SendEvent.
870+type ResizeRequestEventData struct {
871+ Window Window
872+ Width uint16
873+ Height uint16
874+}
875+
876+// Put writes the event (code 25, pads and sequence zeroed) into dst.
877+func (d *ResizeRequestEventData) Put(dst *[32]byte) {
878+ clear(dst[:])
879+ dst[0] = 25
880+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Window))
881+ binary.LittleEndian.PutUint16(dst[8:], uint16(d.Width))
882+ binary.LittleEndian.PutUint16(dst[10:], uint16(d.Height))
883+}
884+
885+// KindCirculateNotify identifies CirculateNotify events in xturbo.Event.Kind.
886+const KindCirculateNotify = xturbo.EventKind(26)
887+
888+// 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.
889+type CirculateNotifyEvent struct{ raw *[32]byte }
890+
891+// CirculateNotifyOf views ev as CirculateNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
892+func CirculateNotifyOf(ev *xturbo.Event) CirculateNotifyEvent {
893+ return CirculateNotifyEvent{raw: &ev.Raw}
894+}
895+func (e CirculateNotifyEvent) Raw() *[32]byte { return e.raw }
896+func (e CirculateNotifyEvent) Event() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
897+func (e CirculateNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
898+func (e CirculateNotifyEvent) Place() Place { return Place(e.raw[16:][0]) }
899+
900+// CirculateNotifyEventData encodes a CirculateNotify event for SendEvent.
901+type CirculateNotifyEventData struct {
902+ Event Window
903+ Window Window
904+ Place Place
905+}
906+
907+// Put writes the event (code 26, pads and sequence zeroed) into dst.
908+func (d *CirculateNotifyEventData) Put(dst *[32]byte) {
909+ clear(dst[:])
910+ dst[0] = 26
911+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Event))
912+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Window))
913+ dst[16:][0] = byte(d.Place)
914+}
915+
916+// KindCirculateRequest identifies CirculateRequest events in xturbo.Event.Kind.
917+const KindCirculateRequest = xturbo.EventKind(27)
918+
919+// CirculateRequestEvent shares CirculateNotify's layout (eventcopy).
920+type CirculateRequestEvent = CirculateNotifyEvent
921+
922+// CirculateRequestOf views ev as CirculateRequest without checking ev.Kind.
923+func CirculateRequestOf(ev *xturbo.Event) CirculateRequestEvent { return CirculateNotifyOf(ev) }
924+
925+// CirculateRequestEventData encodes a CirculateRequest event for SendEvent with CirculateNotify's fields.
926+type CirculateRequestEventData CirculateNotifyEventData
927+
928+// Put writes the event (code 27, pads and sequence zeroed) into dst.
929+func (d *CirculateRequestEventData) Put(dst *[32]byte) {
930+ (*CirculateNotifyEventData)(d).Put(dst)
931+ dst[0] = 27
932+}
933+
934+// KindPropertyNotify identifies PropertyNotify events in xturbo.Event.Kind.
935+const KindPropertyNotify = xturbo.EventKind(28)
936+
937+// 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.
938+type PropertyNotifyEvent struct{ raw *[32]byte }
939+
940+// PropertyNotifyOf views ev as PropertyNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
941+func PropertyNotifyOf(ev *xturbo.Event) PropertyNotifyEvent { return PropertyNotifyEvent{raw: &ev.Raw} }
942+func (e PropertyNotifyEvent) Raw() *[32]byte { return e.raw }
943+func (e PropertyNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
944+func (e PropertyNotifyEvent) Atom() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[8:])) }
945+func (e PropertyNotifyEvent) Time() Timestamp {
946+ return Timestamp(binary.LittleEndian.Uint32(e.raw[12:]))
947+}
948+func (e PropertyNotifyEvent) State() Property { return Property(e.raw[16:][0]) }
949+
950+// PropertyNotifyEventData encodes a PropertyNotify event for SendEvent.
951+type PropertyNotifyEventData struct {
952+ Window Window
953+ Atom Atom
954+ Time Timestamp
955+ State Property
956+}
957+
958+// Put writes the event (code 28, pads and sequence zeroed) into dst.
959+func (d *PropertyNotifyEventData) Put(dst *[32]byte) {
960+ clear(dst[:])
961+ dst[0] = 28
962+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Window))
963+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Atom))
964+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Time))
965+ dst[16:][0] = byte(d.State)
966+}
967+
968+// KindSelectionClear identifies SelectionClear events in xturbo.Event.Kind.
969+const KindSelectionClear = xturbo.EventKind(29)
970+
971+// 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.
972+type SelectionClearEvent struct{ raw *[32]byte }
973+
974+// SelectionClearOf views ev as SelectionClear without checking ev.Kind: a wrong kind yields garbage, never a panic.
975+func SelectionClearOf(ev *xturbo.Event) SelectionClearEvent { return SelectionClearEvent{raw: &ev.Raw} }
976+func (e SelectionClearEvent) Raw() *[32]byte { return e.raw }
977+func (e SelectionClearEvent) Time() Timestamp {
978+ return Timestamp(binary.LittleEndian.Uint32(e.raw[4:]))
979+}
980+func (e SelectionClearEvent) Owner() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
981+func (e SelectionClearEvent) Selection() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[12:])) }
982+
983+// SelectionClearEventData encodes a SelectionClear event for SendEvent.
984+type SelectionClearEventData struct {
985+ Time Timestamp
986+ Owner Window
987+ Selection Atom
988+}
989+
990+// Put writes the event (code 29, pads and sequence zeroed) into dst.
991+func (d *SelectionClearEventData) Put(dst *[32]byte) {
992+ clear(dst[:])
993+ dst[0] = 29
994+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Time))
995+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Owner))
996+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Selection))
997+}
998+
999+// KindSelectionRequest identifies SelectionRequest events in xturbo.Event.Kind.
1000+const KindSelectionRequest = xturbo.EventKind(30)
1001+
1002+// 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.
1003+type SelectionRequestEvent struct{ raw *[32]byte }
1004+
1005+// SelectionRequestOf views ev as SelectionRequest without checking ev.Kind: a wrong kind yields garbage, never a panic.
1006+func SelectionRequestOf(ev *xturbo.Event) SelectionRequestEvent {
1007+ return SelectionRequestEvent{raw: &ev.Raw}
1008+}
1009+func (e SelectionRequestEvent) Raw() *[32]byte { return e.raw }
1010+func (e SelectionRequestEvent) Time() Timestamp {
1011+ return Timestamp(binary.LittleEndian.Uint32(e.raw[4:]))
1012+}
1013+func (e SelectionRequestEvent) Owner() Window { return Window(binary.LittleEndian.Uint32(e.raw[8:])) }
1014+func (e SelectionRequestEvent) Requestor() Window {
1015+ return Window(binary.LittleEndian.Uint32(e.raw[12:]))
1016+}
1017+func (e SelectionRequestEvent) Selection() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[16:])) }
1018+func (e SelectionRequestEvent) Target() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[20:])) }
1019+func (e SelectionRequestEvent) Property() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[24:])) }
1020+
1021+// SelectionRequestEventData encodes a SelectionRequest event for SendEvent.
1022+type SelectionRequestEventData struct {
1023+ Time Timestamp
1024+ Owner Window
1025+ Requestor Window
1026+ Selection Atom
1027+ Target Atom
1028+ Property Atom
1029+}
1030+
1031+// Put writes the event (code 30, pads and sequence zeroed) into dst.
1032+func (d *SelectionRequestEventData) Put(dst *[32]byte) {
1033+ clear(dst[:])
1034+ dst[0] = 30
1035+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Time))
1036+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Owner))
1037+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Requestor))
1038+ binary.LittleEndian.PutUint32(dst[16:], uint32(d.Selection))
1039+ binary.LittleEndian.PutUint32(dst[20:], uint32(d.Target))
1040+ binary.LittleEndian.PutUint32(dst[24:], uint32(d.Property))
1041+}
1042+
1043+// KindSelectionNotify identifies SelectionNotify events in xturbo.Event.Kind.
1044+const KindSelectionNotify = xturbo.EventKind(31)
1045+
1046+// 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.
1047+type SelectionNotifyEvent struct{ raw *[32]byte }
1048+
1049+// SelectionNotifyOf views ev as SelectionNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
1050+func SelectionNotifyOf(ev *xturbo.Event) SelectionNotifyEvent {
1051+ return SelectionNotifyEvent{raw: &ev.Raw}
1052+}
1053+func (e SelectionNotifyEvent) Raw() *[32]byte { return e.raw }
1054+func (e SelectionNotifyEvent) Time() Timestamp {
1055+ return Timestamp(binary.LittleEndian.Uint32(e.raw[4:]))
1056+}
1057+func (e SelectionNotifyEvent) Requestor() Window {
1058+ return Window(binary.LittleEndian.Uint32(e.raw[8:]))
1059+}
1060+func (e SelectionNotifyEvent) Selection() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[12:])) }
1061+func (e SelectionNotifyEvent) Target() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[16:])) }
1062+func (e SelectionNotifyEvent) Property() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[20:])) }
1063+
1064+// SelectionNotifyEventData encodes a SelectionNotify event for SendEvent.
1065+type SelectionNotifyEventData struct {
1066+ Time Timestamp
1067+ Requestor Window
1068+ Selection Atom
1069+ Target Atom
1070+ Property Atom
1071+}
1072+
1073+// Put writes the event (code 31, pads and sequence zeroed) into dst.
1074+func (d *SelectionNotifyEventData) Put(dst *[32]byte) {
1075+ clear(dst[:])
1076+ dst[0] = 31
1077+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Time))
1078+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Requestor))
1079+ binary.LittleEndian.PutUint32(dst[12:], uint32(d.Selection))
1080+ binary.LittleEndian.PutUint32(dst[16:], uint32(d.Target))
1081+ binary.LittleEndian.PutUint32(dst[20:], uint32(d.Property))
1082+}
1083+
1084+// KindColormapNotify identifies ColormapNotify events in xturbo.Event.Kind.
1085+const KindColormapNotify = xturbo.EventKind(32)
1086+
1087+// 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.
1088+type ColormapNotifyEvent struct{ raw *[32]byte }
1089+
1090+// ColormapNotifyOf views ev as ColormapNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
1091+func ColormapNotifyOf(ev *xturbo.Event) ColormapNotifyEvent { return ColormapNotifyEvent{raw: &ev.Raw} }
1092+func (e ColormapNotifyEvent) Raw() *[32]byte { return e.raw }
1093+func (e ColormapNotifyEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
1094+func (e ColormapNotifyEvent) Colormap() Colormap {
1095+ return Colormap(binary.LittleEndian.Uint32(e.raw[8:]))
1096+}
1097+func (e ColormapNotifyEvent) New() bool { return (e.raw[12:][0] != 0) }
1098+func (e ColormapNotifyEvent) State() ColormapState { return ColormapState(e.raw[13:][0]) }
1099+
1100+// ColormapNotifyEventData encodes a ColormapNotify event for SendEvent.
1101+type ColormapNotifyEventData struct {
1102+ Window Window
1103+ Colormap Colormap
1104+ New bool
1105+ State ColormapState
1106+}
1107+
1108+// Put writes the event (code 32, pads and sequence zeroed) into dst.
1109+func (d *ColormapNotifyEventData) Put(dst *[32]byte) {
1110+ clear(dst[:])
1111+ dst[0] = 32
1112+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Window))
1113+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Colormap))
1114+ if d.New {
1115+ dst[12:][0] = 1
1116+ } else {
1117+ dst[12:][0] = 0
1118+ }
1119+ dst[13:][0] = byte(d.State)
1120+}
1121+
1122+// KindClientMessage identifies ClientMessage events in xturbo.Event.Kind.
1123+const KindClientMessage = xturbo.EventKind(33)
1124+
1125+// 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.
1126+type ClientMessageEvent struct{ raw *[32]byte }
1127+
1128+// ClientMessageOf views ev as ClientMessage without checking ev.Kind: a wrong kind yields garbage, never a panic.
1129+func ClientMessageOf(ev *xturbo.Event) ClientMessageEvent { return ClientMessageEvent{raw: &ev.Raw} }
1130+func (e ClientMessageEvent) Raw() *[32]byte { return e.raw }
1131+func (e ClientMessageEvent) Format() uint8 { return uint8(e.raw[1:][0]) }
1132+func (e ClientMessageEvent) Window() Window { return Window(binary.LittleEndian.Uint32(e.raw[4:])) }
1133+func (e ClientMessageEvent) Type() Atom { return Atom(binary.LittleEndian.Uint32(e.raw[8:])) }
1134+func (e ClientMessageEvent) Data() ClientMessageData { return GetClientMessageData(e.raw[12:]) }
1135+
1136+// ClientMessageEventData encodes a ClientMessage event for SendEvent.
1137+type ClientMessageEventData struct {
1138+ Format uint8
1139+ Window Window
1140+ Type Atom
1141+ Data ClientMessageData
1142+}
1143+
1144+// Put writes the event (code 33, pads and sequence zeroed) into dst.
1145+func (d *ClientMessageEventData) Put(dst *[32]byte) {
1146+ clear(dst[:])
1147+ dst[0] = 33
1148+ dst[1:][0] = byte(d.Format)
1149+ binary.LittleEndian.PutUint32(dst[4:], uint32(d.Window))
1150+ binary.LittleEndian.PutUint32(dst[8:], uint32(d.Type))
1151+ d.Data.Put(dst[12:])
1152+}
1153+
1154+// KindMappingNotify identifies MappingNotify events in xturbo.Event.Kind.
1155+const KindMappingNotify = xturbo.EventKind(34)
1156+
1157+// 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.
1158+type MappingNotifyEvent struct{ raw *[32]byte }
1159+
1160+// MappingNotifyOf views ev as MappingNotify without checking ev.Kind: a wrong kind yields garbage, never a panic.
1161+func MappingNotifyOf(ev *xturbo.Event) MappingNotifyEvent { return MappingNotifyEvent{raw: &ev.Raw} }
1162+func (e MappingNotifyEvent) Raw() *[32]byte { return e.raw }
1163+func (e MappingNotifyEvent) Request() Mapping { return Mapping(e.raw[4:][0]) }
1164+func (e MappingNotifyEvent) FirstKeycode() Keycode { return Keycode(e.raw[5:][0]) }
1165+func (e MappingNotifyEvent) Count() uint8 { return uint8(e.raw[6:][0]) }
1166+
1167+// MappingNotifyEventData encodes a MappingNotify event for SendEvent.
1168+type MappingNotifyEventData struct {
1169+ Request Mapping
1170+ FirstKeycode Keycode
1171+ Count uint8
1172+}
1173+
1174+// Put writes the event (code 34, pads and sequence zeroed) into dst.
1175+func (d *MappingNotifyEventData) Put(dst *[32]byte) {
1176+ clear(dst[:])
1177+ dst[0] = 34
1178+ dst[4:][0] = byte(d.Request)
1179+ dst[5:][0] = byte(d.FirstKeycode)
1180+ dst[6:][0] = byte(d.Count)
1181+}
1182+
1183+// GeGenericEvent is a view over a GeGeneric generic event: the copied Event.Raw plus the borrowed Event.Tail.
1184+// Tail is valid only until the Handler callback returns; copy what you keep.
1185+type GeGenericEvent struct {
1186+ raw *[32]byte
1187+ tail []byte
1188+}
1189+
1190+// GeGenericOf views ev as GeGeneric; false when ev is not a generic event or Tail does not match its length.
1191+func GeGenericOf(ev *xturbo.Event) (GeGenericEvent, bool) {
1192+ if ev.Raw[0]&0x7f != 35 || len(ev.Tail) != 4*int(binary.LittleEndian.Uint32(ev.Raw[4:])) {
1193+ return GeGenericEvent{}, false
1194+ }
1195+ return GeGenericEvent{raw: &ev.Raw, tail: ev.Tail}, true
1196+}
1197+func (e GeGenericEvent) Extension() uint8 { return e.raw[1] }
1198+func (e GeGenericEvent) Length() uint32 { return binary.LittleEndian.Uint32(e.raw[4:]) }
1199+func (e GeGenericEvent) EventType() uint16 { return binary.LittleEndian.Uint16(e.raw[8:]) }
1200+func (e GeGenericEvent) Tail() []byte { return e.tail }
1201+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