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