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 Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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 { | |||
| 241 | 241 | var extensions []*Module | |
| 242 | 242 | for _, m := range modules { | |
| 243 | 243 | if m.ExtName != "" { | |
| 244 | + | count, err := eventCount(m) | |
| 245 | + | if err != nil { | |
| 246 | + | return err | |
| 247 | + | } | |
| 248 | + | m.NumEvents, m.NumErrors = count, errorCount(m) | |
| 244 | 249 | extensions = append(extensions, m) | |
| 245 | 250 | } | |
| 246 | 251 | } | |
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 { | |||
| 36 | 36 | Symbols map[string]*Type | |
| 37 | 37 | Names map[string]*Node | |
| 38 | 38 | Decls, SizeChecks []string | |
| 39 | + | NumEvents, NumErrors int // extension event/error code ranges | |
| 39 | 40 | } | |
M cmd/xtgen/main.go +3 -0
| @@ -35,6 +35,9 @@ func main() { | |||
| 35 | 35 | if err == nil { | |
| 36 | 36 | err = emitReplies(modules) | |
| 37 | 37 | } | |
| 38 | + | if err == nil { | |
| 39 | + | err = emitEvents(modules) | |
| 40 | + | } | |
| 38 | 41 | if err != nil { | |
| 39 | 42 | fmt.Fprintln(os.Stderr, err) | |
| 40 | 43 | os.Exit(1) | |
M cmd/xtgen/replies.go +39 -10
| @@ -58,14 +58,43 @@ func elemName(goType string) string { | |||
| 58 | 58 | return strings.ToUpper(goType[:1]) + goType[1:] | |
| 59 | 59 | } | |
| 60 | 60 | ||
| 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 | |
| 64 | 89 | c := &container{Name: name, Recv: recv, Reply: reply, Dynamic: -1, Module: m} | |
| 65 | 90 | off := 0 | |
| 66 | - | if reply { | |
| 91 | + | switch lay { | |
| 92 | + | case layoutReply, layoutEvent, layoutEventNoSeq: | |
| 67 | 93 | off = 1 | |
| 94 | + | case layoutXGE: | |
| 95 | + | off = 10 | |
| 68 | 96 | } | |
| 97 | + | what := map[layout]string{layoutStruct: "struct", layoutReply: "reply", layoutEvent: "event", layoutEventNoSeq: "event", layoutXGE: "event"}[lay] | |
| 69 | 98 | first := true | |
| 70 | 99 | methods := map[string]bool{} | |
| 71 | 100 | for _, n := range nodes { | |
| @@ -138,13 +167,13 @@ func buildContainer(m *Module, name, recv string, nodes []*Node, reply bool, res | |||
| 138 | 167 | c.Dynamic = len(c.Members) | |
| 139 | 168 | } | |
| 140 | 169 | default: | |
| 141 | - | return nil, n.fail("unsupported %s member %s", map[bool]string{true: "reply", false: "struct"}[reply], n.Kind) | |
| 170 | + | return nil, n.fail("unsupported %s member %s", what, n.Kind) | |
| 142 | 171 | } | |
| 143 | - | if reply && first { | |
| 172 | + | if jump := lay.headerJump(); jump > 0 && first { | |
| 144 | 173 | if mem.Size != 1 { | |
| 145 | - | return nil, n.fail("first reply member must occupy the header data byte") | |
| 174 | + | return nil, n.fail("first %s member must occupy the header data byte", what) | |
| 146 | 175 | } | |
| 147 | - | off = 8 | |
| 176 | + | off = jump | |
| 148 | 177 | first = false | |
| 149 | 178 | c.Members = append(c.Members, mem) | |
| 150 | 179 | continue | |
| @@ -340,7 +369,7 @@ func resolveReplies(m *Module) ([]replyIR, []*container, error) { | |||
| 340 | 369 | return nil, nil, err | |
| 341 | 370 | } | |
| 342 | 371 | } | |
| 343 | - | c, err := buildContainer(m, name+"Reply", "r", r.Children, true, "Raw") | |
| 372 | + | c, err := buildContainer(m, name+"Reply", "r", r.Children, layoutReply, "Raw") | |
| 344 | 373 | if err != nil { | |
| 345 | 374 | return nil, nil, err | |
| 346 | 375 | } | |
| @@ -357,7 +386,7 @@ func resolveReplies(m *Module) ([]replyIR, []*container, error) { | |||
| 357 | 386 | return nil, nil, err | |
| 358 | 387 | } | |
| 359 | 388 | } | |
| 360 | - | c, err := buildContainer(m, t.GoType+"View", "v", t.Node.Children, false, "Raw Size") | |
| 389 | + | c, err := buildContainer(m, t.GoType+"View", "v", t.Node.Children, layoutStruct, "Raw Size") | |
| 361 | 390 | if err != nil { | |
| 362 | 391 | return nil, nil, err | |
| 363 | 392 | } | |
M cmd/xtgen/replies_test.go +1 -1
| @@ -60,7 +60,7 @@ func TestReplyReservedGetter(t *testing.T) { | |||
| 60 | 60 | {Kind: "pad", Attr: map[string]string{"bytes": "1"}}, | |
| 61 | 61 | {Kind: "field", Attr: map[string]string{"name": "raw", "type": "CARD32"}}, | |
| 62 | 62 | } | |
| 63 | - | c, err := buildContainer(m, "XReply", "r", nodes, true, "Raw") | |
| 63 | + | c, err := buildContainer(m, "XReply", "r", nodes, layoutReply, "Raw") | |
| 64 | 64 | if err != nil { | |
| 65 | 65 | t.Fatal(err) | |
| 66 | 66 | } | |
A cmd/xtgen/templates/errors.tmpl +5 -0
| @@ -0,0 +1,5 @@ | |||
| 1 | + | // Code generated by xtgen; DO NOT EDIT. | |
| 2 | + | package {{.Pkg}} | |
| 3 | + | {{range .Decls}} | |
| 4 | + | {{.}} | |
| 5 | + | {{end}} | |
A cmd/xtgen/templates/events.tmpl +5 -0
| @@ -0,0 +1,5 @@ | |||
| 1 | + | // Code generated by xtgen; DO NOT EDIT. | |
| 2 | + | package {{.Pkg}} | |
| 3 | + | {{range .Decls}} | |
| 4 | + | {{.}} | |
| 5 | + | {{end}} | |
M cmd/xtgen/templates/ext.tmpl +4 -0
| @@ -8,3 +8,7 @@ const ( | |||
| 8 | 8 | {{end}} extCount | |
| 9 | 9 | ) | |
| 10 | 10 | var ExtXName = [extCount]string{"", {{range .}}{{printf "%q" .XName}},{{end}}} | |
| 11 | + | // extEvents and extErrors are the event and error code ranges each extension | |
| 12 | + | // declares in the pinned XML; ExtTable.Set uses them to fill reverse maps. | |
| 13 | + | var extEvents = [extCount]uint8{0, {{range .}}{{.NumEvents}},{{end}}} | |
| 14 | + | var extErrors = [extCount]uint8{0, {{range .}}{{.NumErrors}},{{end}}} | |
A event.go +77 -0
| @@ -0,0 +1,77 @@ | |||
| 1 | + | package xturbo | |
| 2 | + | ||
| 3 | + | import "encoding/binary" | |
| 4 | + | ||
| 5 | + | // EventKind identifies a decoded event: uint16(ExtID)<<8 | code. Core events | |
| 6 | + | // use the wire code (2..34); extension events the code relative to the | |
| 7 | + | // extension's first event; XGE events 0x80|evtype. | |
| 8 | + | type EventKind uint16 | |
| 9 | + | ||
| 10 | + | // KindUnknown marks an event whose code or XGE extension is not known. | |
| 11 | + | const KindUnknown EventKind = 0xFFFF | |
| 12 | + | ||
| 13 | + | const ( | |
| 14 | + | codeGenericEvent = 35 | |
| 15 | + | codeKeymapNotify = 11 | |
| 16 | + | ) | |
| 17 | + | ||
| 18 | + | // Event is a decoded event value. Raw is a copy and survives Dispatch; Tail | |
| 19 | + | // borrows the read buffer (XGE bytes beyond 32) and is valid only until the | |
| 20 | + | // Handler callback returns. Copy what you keep. | |
| 21 | + | type Event struct { | |
| 22 | + | Kind EventKind | |
| 23 | + | SendEvent bool | |
| 24 | + | Ext ExtID | |
| 25 | + | Seq Seq // wire sequence; Conn widens it. 0 for KeymapNotify. | |
| 26 | + | Raw [32]byte | |
| 27 | + | Tail []byte | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | // DecodeEvent decodes one event packet at the start of raw. n is the number | |
| 31 | + | // of bytes consumed (32, or 32+4*length for XGE). ok is false for error and | |
| 32 | + | // reply packets and for short input. Unknown codes decode with | |
| 33 | + | // Kind == KindUnknown and ok == true. A nil ext decodes core events only. | |
| 34 | + | // The returned Seq is the 16-bit wire value; Conn widens it. | |
| 35 | + | func DecodeEvent(raw []byte, ext *ExtTable) (ev Event, n int, ok bool) { | |
| 36 | + | if len(raw) < 32 { | |
| 37 | + | return Event{}, 0, false | |
| 38 | + | } | |
| 39 | + | code := raw[0] & 0x7f | |
| 40 | + | if code < 2 { | |
| 41 | + | return Event{}, 0, false | |
| 42 | + | } | |
| 43 | + | n = 32 | |
| 44 | + | if code == codeGenericEvent { | |
| 45 | + | length := uint64(binary.LittleEndian.Uint32(raw[4:])) | |
| 46 | + | if uint64(len(raw)-32)/4 < length { | |
| 47 | + | return Event{}, 0, false | |
| 48 | + | } | |
| 49 | + | n = 32 + int(length)*4 | |
| 50 | + | ev.Tail = raw[32:n:n] | |
| 51 | + | } | |
| 52 | + | copy(ev.Raw[:], raw[:32]) | |
| 53 | + | ev.SendEvent = raw[0]&0x80 != 0 | |
| 54 | + | if code != codeKeymapNotify { | |
| 55 | + | ev.Seq = Seq(binary.LittleEndian.Uint16(raw[2:])) | |
| 56 | + | } | |
| 57 | + | ev.Kind = KindUnknown | |
| 58 | + | switch { | |
| 59 | + | case code < codeGenericEvent: | |
| 60 | + | ev.Kind = EventKind(code) | |
| 61 | + | case code == codeGenericEvent: | |
| 62 | + | if ext == nil { | |
| 63 | + | break | |
| 64 | + | } | |
| 65 | + | evtype := binary.LittleEndian.Uint16(raw[8:]) | |
| 66 | + | if id, found := ext.ByMajor(raw[1]); found && evtype < 0x80 { | |
| 67 | + | ev.Ext = id | |
| 68 | + | ev.Kind = EventKind(id)<<8 | 0x80 | EventKind(evtype) | |
| 69 | + | } | |
| 70 | + | case code >= 64 && ext != nil: | |
| 71 | + | if id, rel, found := ext.ByEvent(code); found { | |
| 72 | + | ev.Ext = id | |
| 73 | + | ev.Kind = EventKind(id)<<8 | EventKind(rel) | |
| 74 | + | } | |
| 75 | + | } | |
| 76 | + | return ev, n, true | |
| 77 | + | } | |
A events_test.go +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 { | |||
| 7 | 7 | } | |
| 8 | 8 | ||
| 9 | 9 | // ExtTable is populated during setup. Fixed arrays avoid lookup allocations. | |
| 10 | - | // Reverse mapping is completed with the read-side runtime in Phase 3. | |
| 11 | - | type ExtTable struct{ info [extCount]ExtInfo } | |
| 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 | + | } | |
| 12 | 17 | ||
| 18 | + | // Set records an extension's negotiated opcodes and fills the reverse maps | |
| 19 | + | // for the event and error codes the extension declares in the pinned XML. | |
| 13 | 20 | 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 | + | } | |
| 16 | 51 | } | |
| 17 | 52 | } | |
| 18 | 53 | func (t *ExtTable) Info(id ExtID) ExtInfo { | |
| @@ -25,3 +60,30 @@ func (t *ExtTable) Info(id ExtID) ExtInfo { | |||
| 25 | 60 | return t.info[id] | |
| 26 | 61 | } | |
| 27 | 62 | func (t *ExtTable) Major(id ExtID) uint8 { return t.Info(id).Major } | |
| 63 | + | ||
| 64 | + | // ByEvent maps an event code (raw[0]&0x7f) to its extension and the code | |
| 65 | + | // relative to the extension's first event. | |
| 66 | + | func (t *ExtTable) ByEvent(code uint8) (id ExtID, rel uint8, ok bool) { | |
| 67 | + | if code >= 128 || t.byEvent[code] == 0 { | |
| 68 | + | return ExtCore, 0, false | |
| 69 | + | } | |
| 70 | + | id = ExtID(t.byEvent[code] - 1) | |
| 71 | + | return id, code - t.info[id].FirstEvent, true | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | // ByError maps a wire error code to its extension and relative error code. | |
| 75 | + | func (t *ExtTable) ByError(code uint8) (id ExtID, rel uint8, ok bool) { | |
| 76 | + | if t.byError[code] == 0 { | |
| 77 | + | return ExtCore, 0, false | |
| 78 | + | } | |
| 79 | + | id = ExtID(t.byError[code] - 1) | |
| 80 | + | return id, code - t.info[id].FirstError, true | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | // ByMajor maps a major opcode to its extension. | |
| 84 | + | func (t *ExtTable) ByMajor(major uint8) (id ExtID, ok bool) { | |
| 85 | + | if t.byMajor[major] == 0 { | |
| 86 | + | return ExtCore, false | |
| 87 | + | } | |
| 88 | + | return ExtID(t.byMajor[major] - 1), true | |
| 89 | + | } | |
M ext_generated.go +5 -0
| @@ -17,3 +17,8 @@ const ( | |||
| 17 | 17 | ) | |
| 18 | 18 | ||
| 19 | 19 | var ExtXName = [extCount]string{"", "BIG-REQUESTS", "Generic Event Extension", "XC-MISC", "Composite", "XFIXES", "X-Resource", "RENDER", "SHAPE"} | |
| 20 | + | ||
| 21 | + | // extEvents and extErrors are the event and error code ranges each extension | |
| 22 | + | // declares in the pinned XML; ExtTable.Set uses them to fill reverse maps. | |
| 23 | + | var extEvents = [extCount]uint8{0, 0, 0, 0, 0, 2, 0, 0, 1} | |
| 24 | + | var extErrors = [extCount]uint8{0, 0, 0, 0, 0, 1, 0, 5, 0} | |
M internal/xtest/README.md +11 -0
| @@ -38,3 +38,14 @@ adds GetWindowAttributes (child window), QueryTree (root), an unset WM_NAME | |||
| 38 | 38 | GetProperty (zero-length value) and ListExtensions (variable STR list). These | |
| 39 | 39 | reply-only requests use opcodes outside the request-golden classifier. | |
| 40 | 40 | 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) { | |||
| 327 | 327 | keep("xproto/GetPropertyEmpty", packet()) | |
| 328 | 328 | write([]byte{99, 0, 1, 0}) // ListExtensions: variable STR iterator | |
| 329 | 329 | 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 | + | } | |
| 330 | 511 | // Closing the client drains the request direction; wait before inspecting tee. | |
| 331 | 512 | client.Close() | |
| 332 | 513 | select { | |
| @@ -353,13 +534,17 @@ func TestCaptureRequests(t *testing.T) { | |||
| 353 | 534 | captured["xproto/CreateWindow"] = p | |
| 354 | 535 | } | |
| 355 | 536 | case 2: | |
| 356 | - | captured["xproto/ChangeWindowAttributes"] = p | |
| 537 | + | if binary.LittleEndian.Uint32(p[4:]) == child { | |
| 538 | + | captured["xproto/ChangeWindowAttributes"] = p | |
| 539 | + | } | |
| 357 | 540 | case 12: | |
| 358 | 541 | names := []string{"ConfigureWindow", "ConfigureWindowSparse", "ConfigureWindowAll"} | |
| 359 | 542 | captured["xproto/"+names[configIndex]] = p | |
| 360 | 543 | configIndex++ | |
| 361 | 544 | case 8: | |
| 362 | - | captured["xproto/MapWindow"] = p | |
| 545 | + | if binary.LittleEndian.Uint32(p[4:]) == root { | |
| 546 | + | captured["xproto/MapWindow"] = p | |
| 547 | + | } | |
| 363 | 548 | case 14: | |
| 364 | 549 | captured["xproto/GetGeometry"] = p | |
| 365 | 550 | case 16: | |
| @@ -373,7 +558,7 @@ func TestCaptureRequests(t *testing.T) { | |||
| 373 | 558 | if p[0] == resMajor && p[1] == 4 { | |
| 374 | 559 | captured["res/QueryClientIds"] = p | |
| 375 | 560 | } | |
| 376 | - | if p[0] == major { | |
| 561 | + | if p[0] == major && p[1] == 0 { | |
| 377 | 562 | captured["xfixes/QueryVersion"] = p | |
| 378 | 563 | } | |
| 379 | 564 | } | |
| @@ -403,3 +588,15 @@ func TestCaptureRequests(t *testing.T) { | |||
| 403 | 588 | } | |
| 404 | 589 | } | |
| 405 | 590 | } | |
| 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 } | |