aboutsummaryrefslogtreecommitdiff
path: root/nexgb/xgbgen
diff options
context:
space:
mode:
Diffstat (limited to 'nexgb/xgbgen')
-rw-r--r--nexgb/xgbgen/COPYING13
-rw-r--r--nexgb/xgbgen/context.go168
-rw-r--r--nexgb/xgbgen/doc.go74
-rw-r--r--nexgb/xgbgen/expression.go436
-rw-r--r--nexgb/xgbgen/field.go398
-rw-r--r--nexgb/xgbgen/go.go220
-rw-r--r--nexgb/xgbgen/go_error.go179
-rw-r--r--nexgb/xgbgen/go_event.go209
-rw-r--r--nexgb/xgbgen/go_list.go107
-rw-r--r--nexgb/xgbgen/go_request_reply.go242
-rw-r--r--nexgb/xgbgen/go_single_field.go166
-rw-r--r--nexgb/xgbgen/go_struct.go118
-rw-r--r--nexgb/xgbgen/go_union.go147
-rw-r--r--nexgb/xgbgen/main.go64
-rw-r--r--nexgb/xgbgen/misc.go49
-rw-r--r--nexgb/xgbgen/protocol.go70
-rw-r--r--nexgb/xgbgen/request_reply.go152
-rw-r--r--nexgb/xgbgen/size.go31
-rw-r--r--nexgb/xgbgen/translation.go427
-rw-r--r--nexgb/xgbgen/type.go390
-rw-r--r--nexgb/xgbgen/xml.go138
-rw-r--r--nexgb/xgbgen/xml_fields.go86
22 files changed, 3884 insertions, 0 deletions
diff --git a/nexgb/xgbgen/COPYING b/nexgb/xgbgen/COPYING
new file mode 100644
index 0000000..5c93f45
--- /dev/null
+++ b/nexgb/xgbgen/COPYING
@@ -0,0 +1,13 @@
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
diff --git a/nexgb/xgbgen/context.go b/nexgb/xgbgen/context.go
new file mode 100644
index 0000000..0728b64
--- /dev/null
+++ b/nexgb/xgbgen/context.go
@@ -0,0 +1,168 @@
+package main
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "log"
+ "sort"
+)
+
+// Context represents the protocol we're converting to Go, and a writer
+// buffer to write the Go source to.
+type Context struct {
+ protocol *Protocol
+ out *bytes.Buffer
+}
+
+func newContext() *Context {
+ return &Context{
+ out: bytes.NewBuffer([]byte{}),
+ }
+}
+
+// Putln calls put and adds a new line to the end of 'format'.
+func (c *Context) Putln(format string, v ...interface{}) {
+ c.Put(format+"\n", v...)
+}
+
+// Put is a short alias to write to 'out'.
+func (c *Context) Put(format string, v ...interface{}) {
+ _, err := c.out.WriteString(fmt.Sprintf(format, v...))
+ if err != nil {
+ log.Fatalf("There was an error writing to context buffer: %s", err)
+ }
+}
+
+// Morph is the big daddy of them all. It takes in an XML byte slice,
+// parse it, transforms the XML types into more usable types,
+// and writes Go code to the 'out' buffer.
+func (c *Context) Morph(xmlBytes []byte) {
+ parsedXml := &XML{}
+ err := xml.Unmarshal(xmlBytes, parsedXml)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Parse all imports
+ parsedXml.Imports.Eval()
+
+ // Translate XML types to nice types
+ c.protocol = parsedXml.Translate(nil)
+
+ // For backwards compatibility we patch the type of the send_event field of
+ // PutImage to be byte
+ if c.protocol.Name == "shm" {
+ for _, req := range c.protocol.Requests {
+ if req.xmlName != "PutImage" {
+ continue
+ }
+ for _, ifield := range req.Fields {
+ field, ok := ifield.(*SingleField)
+ if !ok || field.xmlName != "send_event" {
+ continue
+ }
+ field.Type = &Base{ srcName: "byte", xmlName: "CARD8", size: newFixedSize(1, true) }
+ }
+ }
+ }
+
+ // Start with Go header.
+ c.Putln("// Package %s is the X client API for the %s extension.",
+ c.protocol.PkgName(), c.protocol.ExtXName)
+ c.Putln("package %s", c.protocol.PkgName())
+ c.Putln("")
+ c.Putln("// This file is automatically generated from %s.xml. "+
+ "Edit at your peril!", c.protocol.Name)
+ c.Putln("")
+
+ // Write imports. We always need to import at least xgb.
+ // We also need to import xproto if it's an extension.
+ c.Putln("import (")
+ c.Putln("\"github.com/BurntSushi/xgb\"")
+ c.Putln("")
+ if c.protocol.isExt() {
+ c.Putln("\"github.com/BurntSushi/xgb/xproto\"")
+ }
+
+ sort.Sort(Protocols(c.protocol.Imports))
+ for _, imp := range c.protocol.Imports {
+ // We always import xproto, so skip it if it's explicitly imported
+ if imp.Name == "xproto" {
+ continue
+ }
+ c.Putln("\"github.com/BurntSushi/xgb/%s\"", imp.Name)
+ }
+ c.Putln(")")
+ c.Putln("")
+
+ // If this is an extension, create a function to initialize the extension
+ // before it can be used.
+ if c.protocol.isExt() {
+ xname := c.protocol.ExtXName
+
+ c.Putln("// Init must be called before using the %s extension.",
+ xname)
+ c.Putln("func Init(c *xgb.Conn) error {")
+ c.Putln("reply, err := xproto.QueryExtension(c, %d, \"%s\").Reply()",
+ len(xname), xname)
+ c.Putln("switch {")
+ c.Putln("case err != nil:")
+ c.Putln("return err")
+ c.Putln("case !reply.Present:")
+ c.Putln("return xgb.Errorf(\"No extension named %s could be found on "+
+ "on the server.\")", xname)
+ c.Putln("}")
+ c.Putln("")
+ c.Putln("c.ExtLock.Lock()")
+ c.Putln("c.Extensions[\"%s\"] = reply.MajorOpcode", xname)
+ c.Putln("c.ExtLock.Unlock()")
+ c.Putln("for evNum, fun := range xgb.NewExtEventFuncs[\"%s\"] {",
+ xname)
+ c.Putln("xgb.NewEventFuncs[int(reply.FirstEvent) + evNum] = fun")
+ c.Putln("}")
+ c.Putln("for errNum, fun := range xgb.NewExtErrorFuncs[\"%s\"] {",
+ xname)
+ c.Putln("xgb.NewErrorFuncs[int(reply.FirstError) + errNum] = fun")
+ c.Putln("}")
+ c.Putln("return nil")
+ c.Putln("}")
+ c.Putln("")
+
+ // Make sure newExtEventFuncs["EXT_NAME"] map is initialized.
+ // Same deal for newExtErrorFuncs["EXT_NAME"]
+ c.Putln("func init() {")
+ c.Putln("xgb.NewExtEventFuncs[\"%s\"] = make(map[int]xgb.NewEventFun)",
+ xname)
+ c.Putln("xgb.NewExtErrorFuncs[\"%s\"] = make(map[int]xgb.NewErrorFun)",
+ xname)
+ c.Putln("}")
+ c.Putln("")
+ } else {
+ // In the xproto package, we must provide a Setup function that uses
+ // SetupBytes in xgb.Conn to return a SetupInfo structure.
+ c.Putln("// Setup parses the setup bytes retrieved when")
+ c.Putln("// connecting into a SetupInfo struct.")
+ c.Putln("func Setup(c *xgb.Conn) *SetupInfo {")
+ c.Putln("setup := new(SetupInfo)")
+ c.Putln("SetupInfoRead(c.SetupBytes, setup)")
+ c.Putln("return setup")
+ c.Putln("}")
+ c.Putln("")
+ c.Putln("// DefaultScreen gets the default screen info from SetupInfo.")
+ c.Putln("func (s *SetupInfo) DefaultScreen(c *xgb.Conn) *ScreenInfo {")
+ c.Putln("return &s.Roots[c.DefaultScreen]")
+ c.Putln("}")
+ c.Putln("")
+ }
+
+ // Now write Go source code
+ sort.Sort(Types(c.protocol.Types))
+ sort.Sort(Requests(c.protocol.Requests))
+ for _, typ := range c.protocol.Types {
+ typ.Define(c)
+ }
+ for _, req := range c.protocol.Requests {
+ req.Define(c)
+ }
+}
diff --git a/nexgb/xgbgen/doc.go b/nexgb/xgbgen/doc.go
new file mode 100644
index 0000000..4ba6145
--- /dev/null
+++ b/nexgb/xgbgen/doc.go
@@ -0,0 +1,74 @@
+/*
+xgbgen constructs Go source files from xproto XML description files. xgbgen
+accomplishes the same task as the Python code generator for XCB and xpyb.
+
+Usage:
+ xgbgen [flags] some-protocol.xml
+
+The flags are:
+ --proto-path path
+ The path to a directory containing xproto XML description files.
+ This is only necessary when 'some-protocol.xml' imports other
+ protocol files.
+ --gofmt=true
+ When false, the outputted Go code will not be gofmt'd. And it won't
+ be very pretty at all. This is typically useful if there are syntax
+ errors that need to be debugged in code generation. gofmt will hiccup;
+ this will allow you to see the raw code.
+
+How it works
+
+xgbgen works by parsing the input XML file using Go's encoding/xml package.
+The majority of this work is done in xml.go and xml_fields.go, where the
+appropriate types are declared.
+
+Due to the nature of the XML in the protocol description files, the types
+required to parse the XML are not well suited to reasoning about code
+generation. Because of this, all data parsed in the XML types is translated
+into more reasonable types. This translation is done in translation.go,
+and is mainly grunt work. (The only interesting tidbits are the translation
+of XML names to Go source names, and connecting fields with their
+appropriate types.)
+
+The organization of these types is greatly
+inspired by the description of the XML found here:
+http://cgit.freedesktop.org/xcb/proto/tree/doc/xml-xcb.txt
+
+These types come with a lot of supporting methods to make their use in
+code generation easier. They can be found in expression.go, field.go,
+protocol.go, request_reply.go and type.go. Of particular interest are
+expression evaluation and size calculation (in bytes).
+
+These types also come with supporting methods that convert their
+representation into Go source code. I've quartered such methods in
+go.go, go_error.go, go_event.go, go_list.go, go_request_reply.go,
+go_single_field.go, go_struct.go and go_union.go. The idea is to keep
+as much of the Go specific code generation in one area as possible. Namely,
+while not *all* Go related code is found in the 'go*.go' files, *most*
+of it is. (If there's any interest in using xgbgen for other languages,
+I'd be happy to try and make xgbgen a little more friendly in this regard.
+I did, however, design xgbgen with this in mind, so it shouldn't involve
+anything as serious as a re-design.)
+
+Why
+
+I wrote xgbgen because I found the existing code generator that was written in
+Python to be unwieldy. In particular, static and strong typing greatly helped
+me reason better about the code generation task.
+
+What does not work
+
+The core X protocol should be completely working. As far as I know, most
+extensions should work too, although I've only tested (and not much) the
+Xinerama and RandR extensions.
+
+XKB does not work. I don't have any real plans of working on this unless there
+is demand and I have some test cases to work with. (i.e., even if I could get
+something generated for XKB, I don't have the inclination to understand it
+enough to verify that it works.) XKB poses several extremely difficult
+problems that XCB also has trouble with. More info on that can be found at
+http://cgit.freedesktop.org/xcb/libxcb/tree/doc/xkb_issues and
+http://cgit.freedesktop.org/xcb/libxcb/tree/doc/xkb_internals.
+
+*/
+package main
diff --git a/nexgb/xgbgen/expression.go b/nexgb/xgbgen/expression.go
new file mode 100644
index 0000000..3e2235d
--- /dev/null
+++ b/nexgb/xgbgen/expression.go
@@ -0,0 +1,436 @@
+package main
+
+import (
+ "fmt"
+ "log"
+)
+
+// Expression represents all the different forms of expressions possible in
+// side an XML protocol description file. It's also received a few custom
+// addendums to make applying special functions (like padding) easier.
+type Expression interface {
+ // Concrete determines whether this particular expression can be computed
+ // to some constant value inside xgbgen. (The alternative is that the
+ // expression can only be computed with values at run time of the
+ // generated code.)
+ Concrete() bool
+
+ // Eval evaluates a concrete expression. It is an error to call Eval
+ // on any expression that is not concrete (or contains any sub-expression
+ // that is not concrete).
+ Eval() int
+
+ // Reduce attempts to evaluate any concrete sub-expressions.
+ // i.e., (1 + 2 * (5 + 1 + someSizeOfStruct) reduces to
+ // (3 * (6 + someSizeOfStruct)).
+ // 'prefix' is used preprended to any field reference name.
+ Reduce(prefix string) string
+
+ // String is an alias for Reduce("")
+ String() string
+
+ // Initialize makes sure all names in this expression and any subexpressions
+ // have been translated to Go source names.
+ Initialize(p *Protocol)
+
+ // Makes all field references relative to path
+ Specialize(path string) Expression
+}
+
+// Function is a custom expression not found in the XML. It's simply used
+// to apply a function named in 'Name' to the Expr expression.
+type Function struct {
+ Name string
+ Expr Expression
+}
+
+func (e *Function) Concrete() bool {
+ return false
+}
+
+func (e *Function) Eval() int {
+ log.Fatalf("Cannot evaluate a 'Function'. It is not concrete.")
+ panic("unreachable")
+}
+
+func (e *Function) Reduce(prefix string) string {
+ return fmt.Sprintf("%s(%s)", e.Name, e.Expr.Reduce(prefix))
+}
+
+func (e *Function) String() string {
+ return e.Reduce("")
+}
+
+func (e *Function) Initialize(p *Protocol) {
+ e.Expr.Initialize(p)
+}
+
+func (e *Function) Specialize(path string) Expression {
+ r := *e
+ r.Expr = r.Expr.Specialize(path)
+ return &r
+}
+
+// BinaryOp is an expression that performs some operation (defined in the XML
+// file) with Expr1 and Expr2 as operands.
+type BinaryOp struct {
+ Op string
+ Expr1 Expression
+ Expr2 Expression
+}
+
+// newBinaryOp constructs a new binary expression when both expr1 and expr2
+// are not nil. If one or both are nil, then the non-nil expression is
+// returned unchanged or nil is returned.
+func newBinaryOp(op string, expr1, expr2 Expression) Expression {
+ switch {
+ case expr1 != nil && expr2 != nil:
+ return &BinaryOp{
+ Op: op,
+ Expr1: expr1,
+ Expr2: expr2,
+ }
+ case expr1 != nil && expr2 == nil:
+ return expr1
+ case expr1 == nil && expr2 != nil:
+ return expr2
+ case expr1 == nil && expr2 == nil:
+ return nil
+ }
+ panic("unreachable")
+}
+
+func (e *BinaryOp) Concrete() bool {
+ return e.Expr1.Concrete() && e.Expr2.Concrete()
+}
+
+func (e *BinaryOp) Eval() int {
+ switch e.Op {
+ case "+":
+ return e.Expr1.Eval() + e.Expr2.Eval()
+ case "-":
+ return e.Expr1.Eval() - e.Expr2.Eval()
+ case "*":
+ return e.Expr1.Eval() * e.Expr2.Eval()
+ case "/":
+ return e.Expr1.Eval() / e.Expr2.Eval()
+ case "&amp;":
+ return e.Expr1.Eval() & e.Expr2.Eval()
+ case "&lt;&lt;":
+ return int(uint(e.Expr1.Eval()) << uint(e.Expr2.Eval()))
+ }
+
+ log.Fatalf("Invalid binary operator '%s' for expression.", e.Op)
+ panic("unreachable")
+}
+
+func (e *BinaryOp) Reduce(prefix string) string {
+ if e.Concrete() {
+ return fmt.Sprintf("%d", e.Eval())
+ }
+
+ // An incredibly dirty hack to make sure any time we perform an operation
+ // on a field, we're dealing with ints...
+ expr1, expr2 := e.Expr1, e.Expr2
+ switch expr1.(type) {
+ case *FieldRef:
+ expr1 = &Function{
+ Name: "int",
+ Expr: expr1,
+ }
+ }
+ switch expr2.(type) {
+ case *FieldRef:
+ expr2 = &Function{
+ Name: "int",
+ Expr: expr2,
+ }
+ }
+ return fmt.Sprintf("(%s %s %s)",
+ expr1.Reduce(prefix), e.Op, expr2.Reduce(prefix))
+}
+
+func (e *BinaryOp) String() string {
+ return e.Reduce("")
+}
+
+func (e *BinaryOp) Initialize(p *Protocol) {
+ e.Expr1.Initialize(p)
+ e.Expr2.Initialize(p)
+}
+
+func (e *BinaryOp) Specialize(path string) Expression {
+ r := *e
+ r.Expr1 = r.Expr1.Specialize(path)
+ r.Expr2 = r.Expr2.Specialize(path)
+ return &r
+}
+
+// UnaryOp is the same as BinaryOp, except it's a unary operator with only
+// one sub-expression.
+type UnaryOp struct {
+ Op string
+ Expr Expression
+}
+
+func (e *UnaryOp) Concrete() bool {
+ return e.Expr.Concrete()
+}
+
+func (e *UnaryOp) Eval() int {
+ switch e.Op {
+ case "~":
+ return ^e.Expr.Eval()
+ }
+
+ log.Fatalf("Invalid unary operator '%s' for expression.", e.Op)
+ panic("unreachable")
+}
+
+func (e *UnaryOp) Reduce(prefix string) string {
+ if e.Concrete() {
+ return fmt.Sprintf("%d", e.Eval())
+ }
+ return fmt.Sprintf("(%s (%s))", e.Op, e.Expr.Reduce(prefix))
+}
+
+func (e *UnaryOp) String() string {
+ return e.Reduce("")
+}
+
+func (e *UnaryOp) Initialize(p *Protocol) {
+ e.Expr.Initialize(p)
+}
+
+func (e *UnaryOp) Specialize(path string) Expression {
+ r := *e
+ r.Expr = r.Expr.Specialize(path)
+ return &r
+}
+
+// Padding represents the application of the 'pad' function to some
+// sub-expression.
+type Padding struct {
+ Expr Expression
+}
+
+func (e *Padding) Concrete() bool {
+ return e.Expr.Concrete()
+}
+
+func (e *Padding) Eval() int {
+ return pad(e.Expr.Eval())
+}
+
+func (e *Padding) Reduce(prefix string) string {
+ if e.Concrete() {
+ return fmt.Sprintf("%d", e.Eval())
+ }
+ return fmt.Sprintf("xgb.Pad(%s)", e.Expr.Reduce(prefix))
+}
+
+func (e *Padding) String() string {
+ return e.Reduce("")
+}
+
+func (e *Padding) Initialize(p *Protocol) {
+ e.Expr.Initialize(p)
+}
+
+func (e *Padding) Specialize(path string) Expression {
+ r := *e
+ r.Expr = r.Expr.Specialize(path)
+ return &r
+}
+
+// PopCount represents the application of the 'PopCount' function to
+// some sub-expression.
+type PopCount struct {
+ Expr Expression
+}
+
+func (e *PopCount) Concrete() bool {
+ return e.Expr.Concrete()
+}
+
+func (e *PopCount) Eval() int {
+ return int(popCount(uint(e.Expr.Eval())))
+}
+
+func (e *PopCount) Reduce(prefix string) string {
+ if e.Concrete() {
+ return fmt.Sprintf("%d", e.Eval())
+ }
+ return fmt.Sprintf("xgb.PopCount(%s)", e.Expr.Reduce(prefix))
+}
+
+func (e *PopCount) String() string {
+ return e.Reduce("")
+}
+
+func (e *PopCount) Initialize(p *Protocol) {
+ e.Expr.Initialize(p)
+}
+
+func (e *PopCount) Specialize(path string) Expression {
+ r := *e
+ r.Expr = r.Expr.Specialize(path)
+ return &r
+}
+
+// Value represents some constant integer.
+type Value struct {
+ v int
+}
+
+func (e *Value) Concrete() bool {
+ return true
+}
+
+func (e *Value) Eval() int {
+ return e.v
+}
+
+func (e *Value) Reduce(prefix string) string {
+ return fmt.Sprintf("%d", e.v)
+}
+
+func (e *Value) String() string {
+ return e.Reduce("")
+}
+
+func (e *Value) Initialize(p *Protocol) {}
+
+func (e *Value) Specialize(path string) Expression {
+ return e
+}
+
+// Bit represents some bit whose value is computed by '1 << bit'.
+type Bit struct {
+ b int
+}
+
+func (e *Bit) Concrete() bool {
+ return true
+}
+
+func (e *Bit) Eval() int {
+ return int(1 << uint(e.b))
+}
+
+func (e *Bit) Reduce(prefix string) string {
+ return fmt.Sprintf("%d", e.Eval())
+}
+
+func (e *Bit) String() string {
+ return e.Reduce("")
+}
+
+func (e *Bit) Initialize(p *Protocol) {}
+
+func (e *Bit) Specialize(path string) Expression {
+ return e
+}
+
+// FieldRef represents a reference to some variable in the generated code
+// with name Name.
+type FieldRef struct {
+ Name string
+}
+
+func (e *FieldRef) Concrete() bool {
+ return false
+}
+
+func (e *FieldRef) Eval() int {
+ log.Fatalf("Cannot evaluate a 'FieldRef'. It is not concrete.")
+ panic("unreachable")
+}
+
+func (e *FieldRef) Reduce(prefix string) string {
+ val := e.Name
+ if len(prefix) > 0 {
+ val = fmt.Sprintf("%s%s", prefix, val)
+ }
+ return val
+}
+
+func (e *FieldRef) String() string {
+ return e.Reduce("")
+}
+
+func (e *FieldRef) Initialize(p *Protocol) {
+ e.Name = SrcName(p, e.Name)
+}
+
+func (e *FieldRef) Specialize(path string) Expression {
+ return &FieldRef{Name: path + "." + e.Name}
+}
+
+// EnumRef represents a reference to some enumeration field.
+// EnumKind is the "group" an EnumItem is the name of the specific enumeration
+// value inside that group.
+type EnumRef struct {
+ EnumKind Type
+ EnumItem string
+}
+
+func (e *EnumRef) Concrete() bool {
+ return false
+}
+
+func (e *EnumRef) Eval() int {
+ log.Fatalf("Cannot evaluate an 'EnumRef'. It is not concrete.")
+ panic("unreachable")
+}
+
+func (e *EnumRef) Reduce(prefix string) string {
+ return fmt.Sprintf("%s%s", e.EnumKind, e.EnumItem)
+}
+
+func (e *EnumRef) String() string {
+ return e.Reduce("")
+}
+
+func (e *EnumRef) Initialize(p *Protocol) {
+ e.EnumKind = e.EnumKind.(*Translation).RealType(p)
+ e.EnumItem = SrcName(p, e.EnumItem)
+}
+
+func (e *EnumRef) Specialize(path string) Expression {
+ return e
+}
+
+// SumOf represents a summation of the variable in the generated code named by
+// Name. It is not currently used. (It's XKB voodoo.)
+type SumOf struct {
+ Name string
+}
+
+func (e *SumOf) Concrete() bool {
+ return false
+}
+
+func (e *SumOf) Eval() int {
+ log.Fatalf("Cannot evaluate a 'SumOf'. It is not concrete.")
+ panic("unreachable")
+}
+
+func (e *SumOf) Reduce(prefix string) string {
+ if len(prefix) > 0 {
+ return fmt.Sprintf("sum(%s%s)", prefix, e.Name)
+ }
+ return fmt.Sprintf("sum(%s)", e.Name)
+}
+
+func (e *SumOf) String() string {
+ return e.Reduce("")
+}
+
+func (e *SumOf) Initialize(p *Protocol) {
+ e.Name = SrcName(p, e.Name)
+}
+
+func (e *SumOf) Specialize(path string) Expression {
+ return e
+}
diff --git a/nexgb/xgbgen/field.go b/nexgb/xgbgen/field.go
new file mode 100644
index 0000000..d6957b7
--- /dev/null
+++ b/nexgb/xgbgen/field.go
@@ -0,0 +1,398 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "strings"
+)
+
+// Field corresponds to any field described in an XML protocol description
+// file. This includes struct fields, union fields, request fields,
+// reply fields and so on.
+// To make code generation easier, fields that have types are also stored.
+// Note that not all fields support all methods defined in this interface.
+// For instance, a padding field does not have a source name.
+type Field interface {
+ // Initialize sets up the source name of this field.
+ Initialize(p *Protocol)
+
+ // SrcName is the Go source name of this field.
+ SrcName() string
+
+ // XmlName is the name of this field from the XML file.
+ XmlName() string
+
+ // SrcType is the Go source type name of this field.
+ SrcType() string
+
+ // Size returns an expression that computes the size (in bytes)
+ // of this field.
+ Size() Size
+
+ // Define writes the Go code to declare this field (in a struct definition).
+ Define(c *Context)
+
+ // Read writes the Go code to convert a byte slice to a Go value
+ // of this field.
+ // 'prefix' is the prefix of the name of the Go value.
+ Read(c *Context, prefix string)
+
+ // Write writes the Go code to convert a Go value to a byte slice of
+ // this field.
+ // 'prefix' is the prefix of the name of the Go value.
+ Write(c *Context, prefix string)
+}
+
+func (pad *PadField) Initialize(p *Protocol) {}
+
+// PadField represents any type of padding. It is omitted from
+// definitions, but is used in Read/Write to increment the buffer index.
+// It is also used in size calculation.
+type PadField struct {
+ Bytes uint
+ Align uint16
+}
+
+func (p *PadField) SrcName() string {
+ panic("illegal to take source name of a pad field")
+}
+
+func (p *PadField) XmlName() string {
+ panic("illegal to take XML name of a pad field")
+}
+
+func (f *PadField) SrcType() string {
+ panic("it is illegal to call SrcType on a PadField field")
+}
+
+func (p *PadField) Size() Size {
+ if p.Align > 0 {
+ return newFixedSize(uint(p.Align), false)
+ } else {
+ return newFixedSize(p.Bytes, true)
+ }
+}
+
+type RequiredStartAlign struct {
+}
+
+func (f *RequiredStartAlign) Initialize(p *Protocol) {}
+
+func (f *RequiredStartAlign) SrcName() string {
+ panic("illegal to take source name of a required_start_align field")
+}
+
+func (f *RequiredStartAlign) XmlName() string {
+ panic("illegal to take XML name of a required_start_align field")
+}
+
+func (f *RequiredStartAlign) SrcType() string {
+ panic("it is illegal to call SrcType on a required_start_align field")
+}
+
+func (f *RequiredStartAlign) Size() Size {
+ return newFixedSize(0, true)
+}
+
+func (f *RequiredStartAlign) Define(c *Context) {}
+
+func (f *RequiredStartAlign) Read(c *Context, prefix string) {}
+func (f *RequiredStartAlign) Write(c *Context, prefix string) {}
+
+// SingleField represents most of the fields in an XML protocol description.
+// It corresponds to any single value.
+type SingleField struct {
+ srcName string
+ xmlName string
+ Type Type
+}
+
+func (f *SingleField) Initialize(p *Protocol) {
+ f.srcName = SrcName(p, f.XmlName())
+ f.Type = f.Type.(*Translation).RealType(p)
+}
+
+func (f *SingleField) SrcName() string {
+ if f.srcName == "Bytes" {
+ return "Bytes_"
+ }
+ return f.srcName
+}
+
+func (f *SingleField) XmlName() string {
+ return f.xmlName
+}
+
+func (f *SingleField) SrcType() string {
+ return f.Type.SrcName()
+}
+
+func (f *SingleField) Size() Size {
+ return f.Type.Size()
+}
+
+// ListField represents a list of values.
+type ListField struct {
+ srcName string
+ xmlName string
+ Type Type
+ LengthExpr Expression
+}
+
+func (f *ListField) SrcName() string {
+ return f.srcName
+}
+
+func (f *ListField) XmlName() string {
+ return f.xmlName
+}
+
+func (f *ListField) SrcType() string {
+ if strings.ToLower(f.Type.XmlName()) == "char" {
+ return fmt.Sprintf("string")
+ }
+ return fmt.Sprintf("[]%s", f.Type.SrcName())
+}
+
+// Length computes the *number* of values in a list.
+// If this ListField does not have any length expression, we throw our hands
+// up and simply compute the 'len' of the field name of this list.
+func (f *ListField) Length() Size {
+ if f.LengthExpr == nil {
+ return newExpressionSize(&Function{
+ Name: "len",
+ Expr: &FieldRef{
+ Name: f.SrcName(),
+ },
+ }, true)
+ }
+ return newExpressionSize(f.LengthExpr, true)
+}
+
+// Size computes the *size* of a list (in bytes).
+// It it typically a simple matter of multiplying the length of the list by
+// the size of the type of the list.
+// But if it's a list of struct where the struct has a list field, we use a
+// special function written in go_struct.go to compute the size (since the
+// size in this case can only be computed recursively).
+func (f *ListField) Size() Size {
+ elsz := f.Type.Size()
+ simpleLen := &Padding{
+ Expr: newBinaryOp("*", f.Length().Expression, elsz.Expression),
+ }
+
+ switch field := f.Type.(type) {
+ case *Struct:
+ if field.HasList() {
+ sizeFun := &Function{
+ Name: fmt.Sprintf("%sListSize", f.Type.SrcName()),
+ Expr: &FieldRef{Name: f.SrcName()},
+ }
+ return newExpressionSize(sizeFun, elsz.exact)
+ } else {
+ return newExpressionSize(simpleLen, elsz.exact)
+ }
+ case *Union:
+ return newExpressionSize(simpleLen, elsz.exact)
+ case *Base:
+ return newExpressionSize(simpleLen, elsz.exact)
+ case *Resource:
+ return newExpressionSize(simpleLen, elsz.exact)
+ case *TypeDef:
+ return newExpressionSize(simpleLen, elsz.exact)
+ default:
+ log.Panicf("Cannot compute list size with type '%T'.", f.Type)
+ }
+ panic("unreachable")
+}
+
+func (f *ListField) Initialize(p *Protocol) {
+ f.srcName = SrcName(p, f.XmlName())
+ f.Type = f.Type.(*Translation).RealType(p)
+ if f.LengthExpr != nil {
+ f.LengthExpr.Initialize(p)
+ }
+}
+
+// LocalField is exactly the same as a regular SingleField, except it isn't
+// sent over the wire. (i.e., it's probably used to compute an ExprField).
+type LocalField struct {
+ *SingleField
+}
+
+// ExprField is a field that is not parameterized, but is computed from values
+// of other fields.
+type ExprField struct {
+ srcName string
+ xmlName string
+ Type Type
+ Expr Expression
+}
+
+func (f *ExprField) SrcName() string {
+ return f.srcName
+}
+
+func (f *ExprField) XmlName() string {
+ return f.xmlName
+}
+
+func (f *ExprField) SrcType() string {
+ return f.Type.SrcName()
+}
+
+func (f *ExprField) Size() Size {
+ return f.Type.Size()
+}
+
+func (f *ExprField) Initialize(p *Protocol) {
+ f.srcName = SrcName(p, f.XmlName())
+ f.Type = f.Type.(*Translation).RealType(p)
+ f.Expr.Initialize(p)
+}
+
+// ValueField represents two fields in one: a mask and a list of 4-byte
+// integers. The mask specifies which kinds of values are in the list.
+// (i.e., See ConfigureWindow, CreateWindow, ChangeWindowAttributes, etc.)
+type ValueField struct {
+ Parent interface{}
+ MaskType Type
+ MaskName string
+ ListName string
+}
+
+func (f *ValueField) SrcName() string {
+ panic("it is illegal to call SrcName on a ValueField field")
+}
+
+func (f *ValueField) XmlName() string {
+ panic("it is illegal to call XmlName on a ValueField field")
+}
+
+func (f *ValueField) SrcType() string {
+ return f.MaskType.SrcName()
+}
+
+// Size computes the size in bytes of the combination of the mask and list
+// in this value field.
+// The expression to compute this looks complicated, but it's really just
+// the number of bits set in the mask multiplied 4 (and padded of course).
+func (f *ValueField) Size() Size {
+ maskSize := f.MaskType.Size()
+ listSize := newExpressionSize(&Function{
+ Name: "xgb.Pad",
+ Expr: &BinaryOp{
+ Op: "*",
+ Expr1: &Value{v: 4},
+ Expr2: &PopCount{
+ Expr: &Function{
+ Name: "int",
+ Expr: &FieldRef{
+ Name: f.MaskName,
+ },
+ },
+ },
+ },
+ }, true)
+ return maskSize.Add(listSize)
+}
+
+func (f *ValueField) ListLength() Size {
+ return newExpressionSize(&PopCount{
+ Expr: &Function{
+ Name: "int",
+ Expr: &FieldRef{
+ Name: f.MaskName,
+ },
+ },
+ }, true)
+}
+
+func (f *ValueField) Initialize(p *Protocol) {
+ f.MaskType = f.MaskType.(*Translation).RealType(p)
+ f.MaskName = SrcName(p, f.MaskName)
+ f.ListName = SrcName(p, f.ListName)
+}
+
+// SwitchField represents a 'switch' element in the XML protocol description
+// file.
+// Currently we translate this to a slice of uint32 and let the user sort
+// through it.
+type SwitchField struct {
+ xmlName string
+ Name string
+ MaskName string
+ Expr Expression
+ Bitcases []*Bitcase
+}
+
+func (f *SwitchField) SrcName() string {
+ return f.Name
+}
+
+func (f *SwitchField) XmlName() string {
+ return f.xmlName
+}
+
+func (f *SwitchField) SrcType() string {
+ return "[]uint32"
+}
+
+func (f *SwitchField) Size() Size {
+ // TODO: size expression used here is not correct unless every element of
+ // the switch is 32 bit long. This assumption holds for xproto but may not
+ // hold for other protocols (xkb?)
+
+ listSize := newExpressionSize(&Function{
+ Name: "xgb.Pad",
+ Expr: &BinaryOp{
+ Op: "*",
+ Expr1: &Value{v: 4},
+ Expr2: &PopCount{
+ Expr: &Function{
+ Name: "int",
+ Expr: &FieldRef{
+ Name: f.MaskName,
+ },
+ },
+ },
+ },
+ }, true)
+
+ return listSize
+}
+
+func (f *SwitchField) ListLength() Size {
+ return newExpressionSize(&PopCount{
+ Expr: &Function{
+ Name: "int",
+ Expr: &FieldRef{
+ Name: f.MaskName,
+ },
+ },
+ }, true)
+}
+
+func (f *SwitchField) Initialize(p *Protocol) {
+ f.xmlName = f.Name
+ f.Name = SrcName(p, f.Name)
+ f.Expr.Initialize(p)
+ fieldref, ok := f.Expr.(*FieldRef)
+ if !ok {
+ panic("switch field's expression not a fieldref")
+ }
+ f.MaskName = SrcName(p, fieldref.Name)
+ for _, bitcase := range f.Bitcases {
+ bitcase.Expr.Initialize(p)
+ for _, field := range bitcase.Fields {
+ field.Initialize(p)
+ }
+ }
+}
+
+// Bitcase represents a single bitcase inside a switch expression.
+// It is not currently used. (i.e., it's XKB voodoo.)
+type Bitcase struct {
+ Fields []Field
+ Expr Expression
+}
diff --git a/nexgb/xgbgen/go.go b/nexgb/xgbgen/go.go
new file mode 100644
index 0000000..87b5028
--- /dev/null
+++ b/nexgb/xgbgen/go.go
@@ -0,0 +1,220 @@
+package main
+
+import (
+ "fmt"
+)
+
+// BaseTypeMap is a map from X base types to Go types.
+// X base types should correspond to the smallest set of X types
+// that can be used to rewrite ALL X types in terms of Go types.
+// That is, if you remove any of the following types, at least one
+// XML protocol description will produce an invalid Go program.
+// The types on the left *never* show themselves in the source.
+var BaseTypeMap = map[string]string{
+ "CARD8": "byte",
+ "CARD16": "uint16",
+ "CARD32": "uint32",
+ "INT8": "int8",
+ "INT16": "int16",
+ "INT32": "int32",
+ "BYTE": "byte",
+ "BOOL": "bool",
+ "float": "float64",
+ "double": "float64",
+ "char": "byte",
+ "void": "byte",
+}
+
+// BaseTypeSizes should have precisely the same keys as in BaseTypeMap,
+// and the values should correspond to the size of the type in bytes.
+var BaseTypeSizes = map[string]uint{
+ "CARD8": 1,
+ "CARD16": 2,
+ "CARD32": 4,
+ "INT8": 1,
+ "INT16": 2,
+ "INT32": 4,
+ "BYTE": 1,
+ "BOOL": 1,
+ "float": 4,
+ "double": 8,
+ "char": 1,
+ "void": 1,
+
+ // Id is a special type used to determine the size of all Xid types.
+ // "Id" is not actually written in the source.
+ "Id": 4,
+}
+
+// TypeMap is a map from types in the XML to type names that is used
+// in the functions that follow. Basically, every occurrence of the key
+// type is replaced with the value type.
+var TypeMap = map[string]string{
+ "VISUALTYPE": "VisualInfo",
+ "DEPTH": "DepthInfo",
+ "SCREEN": "ScreenInfo",
+ "Setup": "SetupInfo",
+}
+
+// NameMap is the same as TypeMap, but for names.
+var NameMap = map[string]string{}
+
+// Reading, writing and defining...
+
+// Base types
+func (b *Base) Define(c *Context) {
+ c.Putln("// Skipping definition for base type '%s'",
+ SrcName(c.protocol, b.XmlName()))
+ c.Putln("")
+}
+
+// Enum types
+func (enum *Enum) Define(c *Context) {
+ c.Putln("const (")
+ for _, item := range enum.Items {
+ c.Putln("%s%s = %d", enum.SrcName(), item.srcName, item.Expr.Eval())
+ }
+ c.Putln(")")
+ c.Putln("")
+}
+
+// Resource types
+func (res *Resource) Define(c *Context) {
+ c.Putln("type %s uint32", res.SrcName())
+ c.Putln("")
+ c.Putln("func New%sId(c *xgb.Conn) (%s, error) {",
+ res.SrcName(), res.SrcName())
+ c.Putln("id, err := c.NewId()")
+ c.Putln("if err != nil {")
+ c.Putln("return 0, err")
+ c.Putln("}")
+ c.Putln("return %s(id), nil", res.SrcName())
+ c.Putln("}")
+ c.Putln("")
+}
+
+// TypeDef types
+func (td *TypeDef) Define(c *Context) {
+ c.Putln("type %s %s", td.srcName, td.Old.SrcName())
+ c.Putln("")
+}
+
+// Field definitions, reads and writes.
+
+// Pad fields
+func (f *PadField) Define(c *Context) {
+ if f.Align > 0 {
+ c.Putln("// alignment gap to multiple of %d", f.Align)
+ } else {
+ c.Putln("// padding: %d bytes", f.Bytes)
+ }
+}
+
+func (f *PadField) Read(c *Context, prefix string) {
+ if f.Align > 0 {
+ c.Putln("b = (b + %d) & ^%d // alignment gap", f.Align-1, f.Align-1)
+ } else {
+ c.Putln("b += %s // padding", f.Size())
+ }
+}
+
+func (f *PadField) Write(c *Context, prefix string) {
+ if f.Align > 0 {
+ c.Putln("b = (b + %d) & ^%d // alignment gap", f.Align-1, f.Align-1)
+ } else {
+ c.Putln("b += %s // padding", f.Size())
+ }
+}
+
+// Local fields
+func (f *LocalField) Define(c *Context) {
+ c.Putln("// local field: %s %s", f.SrcName(), f.Type.SrcName())
+ panic("unreachable")
+}
+
+func (f *LocalField) Read(c *Context, prefix string) {
+ c.Putln("// reading local field: %s (%s) :: %s",
+ f.SrcName(), f.Size(), f.Type.SrcName())
+ panic("unreachable")
+}
+
+func (f *LocalField) Write(c *Context, prefix string) {
+ c.Putln("// skip writing local field: %s (%s) :: %s",
+ f.SrcName(), f.Size(), f.Type.SrcName())
+}
+
+// Expr fields
+func (f *ExprField) Define(c *Context) {
+ c.Putln("// expression field: %s %s (%s)",
+ f.SrcName(), f.Type.SrcName(), f.Expr)
+ panic("unreachable")
+}
+
+func (f *ExprField) Read(c *Context, prefix string) {
+ c.Putln("// reading expression field: %s (%s) (%s) :: %s",
+ f.SrcName(), f.Size(), f.Expr, f.Type.SrcName())
+ panic("unreachable")
+}
+
+func (f *ExprField) Write(c *Context, prefix string) {
+ // Special case for bools, grrr.
+ if f.Type.SrcName() == "bool" {
+ c.Putln("buf[b] = byte(%s)", f.Expr.Reduce(prefix))
+ c.Putln("b += 1")
+ } else {
+ WriteSimpleSingleField(c, f.Expr.Reduce(prefix), f.Type)
+ }
+}
+
+// Value field
+func (f *ValueField) Define(c *Context) {
+ c.Putln("%s %s", f.MaskName, f.SrcType())
+ c.Putln("%s []uint32", f.ListName)
+}
+
+func (f *ValueField) Read(c *Context, prefix string) {
+ ReadSimpleSingleField(c,
+ fmt.Sprintf("%s%s", prefix, f.MaskName), f.MaskType)
+ c.Putln("")
+ c.Putln("%s%s = make([]uint32, %s)",
+ prefix, f.ListName, f.ListLength().Reduce(prefix))
+ c.Putln("for i := 0; i < %s; i++ {", f.ListLength().Reduce(prefix))
+ c.Putln("%s%s[i] = xgb.Get32(buf[b:])", prefix, f.ListName)
+ c.Putln("b += 4")
+ c.Putln("}")
+ c.Putln("b = xgb.Pad(b)")
+}
+
+func (f *ValueField) Write(c *Context, prefix string) {
+ WriteSimpleSingleField(c,
+ fmt.Sprintf("%s%s", prefix, f.MaskName), f.MaskType)
+ c.Putln("for i := 0; i < %s; i++ {", f.ListLength().Reduce(prefix))
+ c.Putln("xgb.Put32(buf[b:], %s%s[i])", prefix, f.ListName)
+ c.Putln("b += 4")
+ c.Putln("}")
+ c.Putln("b = xgb.Pad(b)")
+}
+
+// Switch field
+func (f *SwitchField) Define(c *Context) {
+ c.Putln("%s []uint32", f.Name)
+}
+
+func (f *SwitchField) Read(c *Context, prefix string) {
+ c.Putln("")
+ c.Putln("%s%s = make([]uint32, %s)",
+ prefix, f.Name, f.ListLength().Reduce(prefix))
+ c.Putln("for i := 0; i < %s; i++ {", f.ListLength().Reduce(prefix))
+ c.Putln("%s%s[i] = xgb.Get32(buf[b:])", prefix, f.Name)
+ c.Putln("b += 4")
+ c.Putln("}")
+ c.Putln("b = xgb.Pad(b)")
+}
+
+func (f *SwitchField) Write(c *Context, prefix string) {
+ c.Putln("for i := 0; i < %s; i++ {", f.ListLength().Reduce(prefix))
+ c.Putln("xgb.Put32(buf[b:], %s%s[i])", prefix, f.Name)
+ c.Putln("b += 4")
+ c.Putln("}")
+ c.Putln("b = xgb.Pad(b)")
+}
diff --git a/nexgb/xgbgen/go_error.go b/nexgb/xgbgen/go_error.go
new file mode 100644
index 0000000..55fd28b
--- /dev/null
+++ b/nexgb/xgbgen/go_error.go
@@ -0,0 +1,179 @@
+package main
+
+import (
+ "fmt"
+)
+
+// Error types
+func (e *Error) Define(c *Context) {
+ c.Putln("// %s is the error number for a %s.", e.ErrConst(), e.ErrConst())
+ c.Putln("const %s = %d", e.ErrConst(), e.Number)
+ c.Putln("")
+ c.Putln("type %s struct {", e.ErrType())
+ c.Putln("Sequence uint16")
+ c.Putln("NiceName string")
+ for _, field := range e.Fields {
+ field.Define(c)
+ }
+ c.Putln("}")
+ c.Putln("")
+
+ // Read defines a function that transforms a byte slice into this
+ // error struct.
+ e.Read(c)
+
+ // Makes sure this error type implements the xgb.Error interface.
+ e.ImplementsError(c)
+
+ // Let's the XGB event loop read this error.
+ c.Putln("func init() {")
+ if c.protocol.isExt() {
+ c.Putln("xgb.NewExtErrorFuncs[\"%s\"][%d] = %sNew",
+ c.protocol.ExtXName, e.Number, e.ErrType())
+ } else {
+ c.Putln("xgb.NewErrorFuncs[%d] = %sNew", e.Number, e.ErrType())
+ }
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (e *Error) Read(c *Context) {
+ c.Putln("// %sNew constructs a %s value that implements xgb.Error from "+
+ "a byte slice.", e.ErrType(), e.ErrType())
+ c.Putln("func %sNew(buf []byte) xgb.Error {", e.ErrType())
+ c.Putln("v := %s{}", e.ErrType())
+ c.Putln("v.NiceName = \"%s\"", e.SrcName())
+ c.Putln("")
+ c.Putln("b := 1 // skip error determinant")
+ c.Putln("b += 1 // don't read error number")
+ c.Putln("")
+ c.Putln("v.Sequence = xgb.Get16(buf[b:])")
+ c.Putln("b += 2")
+ c.Putln("")
+ for _, field := range e.Fields {
+ field.Read(c, "v.")
+ c.Putln("")
+ }
+ c.Putln("return v")
+ c.Putln("}")
+ c.Putln("")
+}
+
+// ImplementsError writes functions to implement the XGB Error interface.
+func (e *Error) ImplementsError(c *Context) {
+ c.Putln("// SequenceId returns the sequence id attached to the %s error.",
+ e.ErrConst())
+ c.Putln("// This is mostly used internally.")
+ c.Putln("func (err %s) SequenceId() uint16 {", e.ErrType())
+ c.Putln("return err.Sequence")
+ c.Putln("}")
+ c.Putln("")
+ c.Putln("// BadId returns the 'BadValue' number if one exists for the "+
+ "%s error. If no bad value exists, 0 is returned.", e.ErrConst())
+ c.Putln("func (err %s) BadId() uint32 {", e.ErrType())
+ if !c.protocol.isExt() {
+ c.Putln("return err.BadValue")
+ } else {
+ c.Putln("return 0")
+ }
+ c.Putln("}")
+ c.Putln("// Error returns a rudimentary string representation of the %s "+
+ "error.", e.ErrConst())
+ c.Putln("")
+ c.Putln("func (err %s) Error() string {", e.ErrType())
+ ErrorFieldString(c, e.Fields, e.ErrConst())
+ c.Putln("}")
+ c.Putln("")
+}
+
+// ErrorCopy types
+func (e *ErrorCopy) Define(c *Context) {
+ c.Putln("// %s is the error number for a %s.", e.ErrConst(), e.ErrConst())
+ c.Putln("const %s = %d", e.ErrConst(), e.Number)
+ c.Putln("")
+ c.Putln("type %s %s", e.ErrType(), e.Old.(*Error).ErrType())
+ c.Putln("")
+
+ // Read defines a function that transforms a byte slice into this
+ // error struct.
+ e.Read(c)
+
+ // Makes sure this error type implements the xgb.Error interface.
+ e.ImplementsError(c)
+
+ // Let's the XGB know how to read this error.
+ c.Putln("func init() {")
+ if c.protocol.isExt() {
+ c.Putln("xgb.NewExtErrorFuncs[\"%s\"][%d] = %sNew",
+ c.protocol.ExtXName, e.Number, e.ErrType())
+ } else {
+ c.Putln("xgb.NewErrorFuncs[%d] = %sNew", e.Number, e.ErrType())
+ }
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (e *ErrorCopy) Read(c *Context) {
+ c.Putln("// %sNew constructs a %s value that implements xgb.Error from "+
+ "a byte slice.", e.ErrType(), e.ErrType())
+ c.Putln("func %sNew(buf []byte) xgb.Error {", e.ErrType())
+ c.Putln("v := %s(%sNew(buf).(%s))",
+ e.ErrType(), e.Old.(*Error).ErrType(), e.Old.(*Error).ErrType())
+ c.Putln("v.NiceName = \"%s\"", e.SrcName())
+ c.Putln("return v")
+ c.Putln("}")
+ c.Putln("")
+}
+
+// ImplementsError writes functions to implement the XGB Error interface.
+func (e *ErrorCopy) ImplementsError(c *Context) {
+ c.Putln("// SequenceId returns the sequence id attached to the %s error.",
+ e.ErrConst())
+ c.Putln("// This is mostly used internally.")
+ c.Putln("func (err %s) SequenceId() uint16 {", e.ErrType())
+ c.Putln("return err.Sequence")
+ c.Putln("}")
+ c.Putln("")
+ c.Putln("// BadId returns the 'BadValue' number if one exists for the "+
+ "%s error. If no bad value exists, 0 is returned.", e.ErrConst())
+ c.Putln("func (err %s) BadId() uint32 {", e.ErrType())
+ if !c.protocol.isExt() {
+ c.Putln("return err.BadValue")
+ } else {
+ c.Putln("return 0")
+ }
+ c.Putln("}")
+ c.Putln("")
+ c.Putln("// Error returns a rudimentary string representation of the %s "+
+ "error.", e.ErrConst())
+ c.Putln("func (err %s) Error() string {", e.ErrType())
+ ErrorFieldString(c, e.Old.(*Error).Fields, e.ErrConst())
+ c.Putln("}")
+ c.Putln("")
+}
+
+// ErrorFieldString works for both Error and ErrorCopy. It assembles all of the
+// fields in an error and formats them into a single string.
+func ErrorFieldString(c *Context, fields []Field, errName string) {
+ c.Putln("fieldVals := make([]string, 0, %d)", len(fields))
+ c.Putln("fieldVals = append(fieldVals, \"NiceName: \" + err.NiceName)")
+ c.Putln("fieldVals = append(fieldVals, "+
+ "xgb.Sprintf(\"Sequence: %s\", err.Sequence))", "%d")
+ for _, field := range fields {
+ switch field.(type) {
+ case *PadField:
+ continue
+ default:
+ if field.SrcType() == "string" {
+ c.Putln("fieldVals = append(fieldVals, \"%s: \" + err.%s)",
+ field.SrcName(), field.SrcName())
+ } else {
+ format := fmt.Sprintf("xgb.Sprintf(\"%s: %s\", err.%s)",
+ field.SrcName(), "%d", field.SrcName())
+ c.Putln("fieldVals = append(fieldVals, %s)", format)
+ }
+ }
+ }
+ c.Putln("return \"%s {\" + xgb.StringsJoin(fieldVals, \", \") + \"}\"",
+ errName)
+}
diff --git a/nexgb/xgbgen/go_event.go b/nexgb/xgbgen/go_event.go
new file mode 100644
index 0000000..9b5e748
--- /dev/null
+++ b/nexgb/xgbgen/go_event.go
@@ -0,0 +1,209 @@
+package main
+
+import (
+ "fmt"
+)
+
+// Event types
+func (e *Event) Define(c *Context) {
+ c.Putln("// %s is the event number for a %s.", e.SrcName(), e.EvType())
+ c.Putln("const %s = %d", e.SrcName(), e.Number)
+ c.Putln("")
+ c.Putln("type %s struct {", e.EvType())
+ if !e.NoSequence {
+ c.Putln("Sequence uint16")
+ }
+ for _, field := range e.Fields {
+ field.Define(c)
+ }
+ c.Putln("}")
+ c.Putln("")
+
+ // Read defines a function that transforms a byte slice into this
+ // event struct.
+ e.Read(c)
+
+ // Write defines a function that transforms this event struct into
+ // a byte slice.
+ e.Write(c)
+
+ // Makes sure that this event type is an Event interface.
+ c.Putln("// SequenceId returns the sequence id attached to the %s event.",
+ e.SrcName())
+ c.Putln("// Events without a sequence number (KeymapNotify) return 0.")
+ c.Putln("// This is mostly used internally.")
+ c.Putln("func (v %s) SequenceId() uint16 {", e.EvType())
+ if e.NoSequence {
+ c.Putln("return uint16(0)")
+ } else {
+ c.Putln("return v.Sequence")
+ }
+ c.Putln("}")
+ c.Putln("")
+ c.Putln("// String is a rudimentary string representation of %s.",
+ e.EvType())
+ c.Putln("func (v %s) String() string {", e.EvType())
+ EventFieldString(c, e.Fields, e.SrcName())
+ c.Putln("}")
+ c.Putln("")
+
+ // Let's the XGB event loop read this event.
+ c.Putln("func init() {")
+ if c.protocol.isExt() {
+ c.Putln("xgb.NewExtEventFuncs[\"%s\"][%d] = %sNew",
+ c.protocol.ExtXName, e.Number, e.EvType())
+ } else {
+ c.Putln("xgb.NewEventFuncs[%d] = %sNew", e.Number, e.EvType())
+ }
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (e *Event) Read(c *Context) {
+ c.Putln("// %sNew constructs a %s value that implements xgb.Event from "+
+ "a byte slice.", e.EvType(), e.EvType())
+ c.Putln("func %sNew(buf []byte) xgb.Event {", e.EvType())
+ c.Putln("v := %s{}", e.EvType())
+ c.Putln("b := 1 // don't read event number")
+ c.Putln("")
+ for i, field := range e.Fields {
+ if i == 1 && !e.NoSequence {
+ c.Putln("v.Sequence = xgb.Get16(buf[b:])")
+ c.Putln("b += 2")
+ c.Putln("")
+ }
+ field.Read(c, "v.")
+ c.Putln("")
+ }
+ c.Putln("return v")
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (e *Event) Write(c *Context) {
+ c.Putln("// Bytes writes a %s value to a byte slice.", e.EvType())
+ c.Putln("func (v %s) Bytes() []byte {", e.EvType())
+ c.Putln("buf := make([]byte, %s)", e.Size())
+ c.Putln("b := 0")
+ c.Putln("")
+ c.Putln("// write event number")
+ c.Putln("buf[b] = %d", e.Number)
+ c.Putln("b += 1")
+ c.Putln("")
+ for i, field := range e.Fields {
+ if i == 1 && !e.NoSequence {
+ c.Putln("b += 2 // skip sequence number")
+ c.Putln("")
+ }
+ field.Write(c, "v.")
+ c.Putln("")
+ }
+ c.Putln("return buf")
+ c.Putln("}")
+ c.Putln("")
+}
+
+// EventCopy types
+func (e *EventCopy) Define(c *Context) {
+ c.Putln("// %s is the event number for a %s.", e.SrcName(), e.EvType())
+ c.Putln("const %s = %d", e.SrcName(), e.Number)
+ c.Putln("")
+ c.Putln("type %s %s", e.EvType(), e.Old.(*Event).EvType())
+ c.Putln("")
+
+ // Read defines a function that transforms a byte slice into this
+ // event struct.
+ e.Read(c)
+
+ // Write defines a function that transoforms this event struct into
+ // a byte slice.
+ e.Write(c)
+
+ // Makes sure that this event type is an Event interface.
+ c.Putln("// SequenceId returns the sequence id attached to the %s event.",
+ e.SrcName())
+ c.Putln("// Events without a sequence number (KeymapNotify) return 0.")
+ c.Putln("// This is mostly used internally.")
+ c.Putln("func (v %s) SequenceId() uint16 {", e.EvType())
+ if e.Old.(*Event).NoSequence {
+ c.Putln("return uint16(0)")
+ } else {
+ c.Putln("return v.Sequence")
+ }
+ c.Putln("}")
+ c.Putln("")
+ c.Putln("func (v %s) String() string {", e.EvType())
+ EventFieldString(c, e.Old.(*Event).Fields, e.SrcName())
+ c.Putln("}")
+ c.Putln("")
+
+ // Let's the XGB event loop read this event.
+ c.Putln("func init() {")
+ if c.protocol.isExt() {
+ c.Putln("xgb.NewExtEventFuncs[\"%s\"][%d] = %sNew",
+ c.protocol.ExtXName, e.Number, e.EvType())
+ } else {
+ c.Putln("xgb.NewEventFuncs[%d] = %sNew", e.Number, e.EvType())
+ }
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (e *EventCopy) Read(c *Context) {
+ c.Putln("// %sNew constructs a %s value that implements xgb.Event from "+
+ "a byte slice.", e.EvType(), e.EvType())
+ c.Putln("func %sNew(buf []byte) xgb.Event {", e.EvType())
+ c.Putln("return %s(%sNew(buf).(%s))",
+ e.EvType(), e.Old.(*Event).EvType(), e.Old.(*Event).EvType())
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (e *EventCopy) Write(c *Context) {
+ c.Putln("// Bytes writes a %s value to a byte slice.", e.EvType())
+ c.Putln("func (v %s) Bytes() []byte {", e.EvType())
+ c.Putln("return %s(v).Bytes()", e.Old.(*Event).EvType())
+ c.Putln("}")
+ c.Putln("")
+}
+
+// EventFieldString works for both Event and EventCopy. It assembles all of the
+// fields in an event and formats them into a single string.
+func EventFieldString(c *Context, fields []Field, evName string) {
+ c.Putln("fieldVals := make([]string, 0, %d)", len(fields))
+ if evName != "KeymapNotify" {
+ c.Putln("fieldVals = append(fieldVals, "+
+ "xgb.Sprintf(\"Sequence: %s\", v.Sequence))", "%d")
+ }
+ for _, field := range fields {
+ switch f := field.(type) {
+ case *PadField:
+ continue
+ case *SingleField:
+ switch f.Type.(type) {
+ case *Base:
+ case *Resource:
+ case *TypeDef:
+ default:
+ continue
+ }
+
+ switch field.SrcType() {
+ case "string":
+ format := fmt.Sprintf("xgb.Sprintf(\"%s: %s\", v.%s)",
+ field.SrcName(), "%s", field.SrcName())
+ c.Putln("fieldVals = append(fieldVals, %s)", format)
+ case "bool":
+ format := fmt.Sprintf("xgb.Sprintf(\"%s: %s\", v.%s)",
+ field.SrcName(), "%t", field.SrcName())
+ c.Putln("fieldVals = append(fieldVals, %s)", format)
+ default:
+ format := fmt.Sprintf("xgb.Sprintf(\"%s: %s\", v.%s)",
+ field.SrcName(), "%d", field.SrcName())
+ c.Putln("fieldVals = append(fieldVals, %s)", format)
+ }
+ }
+ }
+ c.Putln("return \"%s {\" + xgb.StringsJoin(fieldVals, \", \") + \"}\"",
+ evName)
+}
diff --git a/nexgb/xgbgen/go_list.go b/nexgb/xgbgen/go_list.go
new file mode 100644
index 0000000..1e85d9f
--- /dev/null
+++ b/nexgb/xgbgen/go_list.go
@@ -0,0 +1,107 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "strings"
+)
+
+// List fields
+func (f *ListField) Define(c *Context) {
+ c.Putln("%s %s // size: %s",
+ f.SrcName(), f.SrcType(), f.Size())
+}
+
+func (f *ListField) Read(c *Context, prefix string) {
+ switch t := f.Type.(type) {
+ case *Resource:
+ length := f.LengthExpr.Reduce(prefix)
+ c.Putln("%s%s = make([]%s, %s)",
+ prefix, f.SrcName(), t.SrcName(), length)
+ c.Putln("for i := 0; i < int(%s); i++ {", length)
+ ReadSimpleSingleField(c, fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
+ c.Putln("}")
+ case *Base:
+ length := f.LengthExpr.Reduce(prefix)
+ if strings.ToLower(t.XmlName()) == "char" {
+ c.Putln("{")
+ c.Putln("byteString := make([]%s, %s)", t.SrcName(), length)
+ c.Putln("copy(byteString[:%s], buf[b:])", length)
+ c.Putln("%s%s = string(byteString)", prefix, f.SrcName())
+ // This is apparently a special case. The "Str" type itself
+ // doesn't specify any padding. I suppose it's up to the
+ // request/reply spec that uses it to get the padding right?
+ c.Putln("b += int(%s)", length)
+ c.Putln("}")
+ } else if t.SrcName() == "byte" {
+ c.Putln("%s%s = make([]%s, %s)",
+ prefix, f.SrcName(), t.SrcName(), length)
+ c.Putln("copy(%s%s[:%s], buf[b:])", prefix, f.SrcName(), length)
+ c.Putln("b += int(%s)", length)
+ } else {
+ c.Putln("%s%s = make([]%s, %s)",
+ prefix, f.SrcName(), t.SrcName(), length)
+ c.Putln("for i := 0; i < int(%s); i++ {", length)
+ ReadSimpleSingleField(c,
+ fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
+ c.Putln("}")
+ }
+ case *TypeDef:
+ length := f.LengthExpr.Reduce(prefix)
+ c.Putln("%s%s = make([]%s, %s)",
+ prefix, f.SrcName(), t.SrcName(), length)
+ c.Putln("for i := 0; i < int(%s); i++ {", length)
+ ReadSimpleSingleField(c, fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
+ c.Putln("}")
+ case *Union:
+ c.Putln("%s%s = make([]%s, %s)",
+ prefix, f.SrcName(), t.SrcName(), f.LengthExpr.Reduce(prefix))
+ c.Putln("b += %sReadList(buf[b:], %s%s)",
+ t.SrcName(), prefix, f.SrcName())
+ case *Struct:
+ c.Putln("%s%s = make([]%s, %s)",
+ prefix, f.SrcName(), t.SrcName(), f.LengthExpr.Reduce(prefix))
+ c.Putln("b += %sReadList(buf[b:], %s%s)",
+ t.SrcName(), prefix, f.SrcName())
+ default:
+ log.Panicf("Cannot read list field '%s' with %T type.",
+ f.XmlName(), f.Type)
+ }
+}
+
+func (f *ListField) Write(c *Context, prefix string) {
+ switch t := f.Type.(type) {
+ case *Resource:
+ length := f.Length().Reduce(prefix)
+ c.Putln("for i := 0; i < int(%s); i++ {", length)
+ WriteSimpleSingleField(c,
+ fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
+ c.Putln("}")
+ case *Base:
+ length := f.Length().Reduce(prefix)
+ if t.SrcName() == "byte" {
+ c.Putln("copy(buf[b:], %s%s[:%s])", prefix, f.SrcName(), length)
+ c.Putln("b += int(%s)", length)
+ } else {
+ c.Putln("for i := 0; i < int(%s); i++ {", length)
+ WriteSimpleSingleField(c,
+ fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
+ c.Putln("}")
+ }
+ case *TypeDef:
+ length := f.Length().Reduce(prefix)
+ c.Putln("for i := 0; i < int(%s); i++ {", length)
+ WriteSimpleSingleField(c,
+ fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
+ c.Putln("}")
+ case *Union:
+ c.Putln("b += %sListBytes(buf[b:], %s%s)",
+ t.SrcName(), prefix, f.SrcName())
+ case *Struct:
+ c.Putln("b += %sListBytes(buf[b:], %s%s)",
+ t.SrcName(), prefix, f.SrcName())
+ default:
+ log.Panicf("Cannot write list field '%s' with %T type.",
+ f.XmlName(), f.Type)
+ }
+}
diff --git a/nexgb/xgbgen/go_request_reply.go b/nexgb/xgbgen/go_request_reply.go
new file mode 100644
index 0000000..9cadc33
--- /dev/null
+++ b/nexgb/xgbgen/go_request_reply.go
@@ -0,0 +1,242 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+func (r *Request) Define(c *Context) {
+ c.Putln("// %s is a cookie used only for %s requests.",
+ r.CookieName(), r.SrcName())
+ c.Putln("type %s struct {", r.CookieName())
+ c.Putln("*xgb.Cookie")
+ c.Putln("}")
+ c.Putln("")
+ if r.Reply != nil {
+ c.Putln("// %s sends a checked request.", r.SrcName())
+ c.Putln("// If an error occurs, it will be returned with the reply "+
+ "by calling %s.Reply()", r.CookieName())
+ c.Putln("func %s(c *xgb.Conn, %s) %s {",
+ r.SrcName(), r.ParamNameTypes(), r.CookieName())
+ r.CheckExt(c)
+ c.Putln("cookie := c.NewCookie(true, true)")
+ c.Putln("c.NewRequest(%s(c, %s), cookie)", r.ReqName(), r.ParamNames())
+ c.Putln("return %s{cookie}", r.CookieName())
+ c.Putln("}")
+ c.Putln("")
+
+ c.Putln("// %sUnchecked sends an unchecked request.", r.SrcName())
+ c.Putln("// If an error occurs, it can only be retrieved using " +
+ "xgb.WaitForEvent or xgb.PollForEvent.")
+ c.Putln("func %sUnchecked(c *xgb.Conn, %s) %s {",
+ r.SrcName(), r.ParamNameTypes(), r.CookieName())
+ r.CheckExt(c)
+ c.Putln("cookie := c.NewCookie(false, true)")
+ c.Putln("c.NewRequest(%s(c, %s), cookie)", r.ReqName(), r.ParamNames())
+ c.Putln("return %s{cookie}", r.CookieName())
+ c.Putln("}")
+ c.Putln("")
+
+ r.ReadReply(c)
+ } else {
+ c.Putln("// %s sends an unchecked request.", r.SrcName())
+ c.Putln("// If an error occurs, it can only be retrieved using " +
+ "xgb.WaitForEvent or xgb.PollForEvent.")
+ c.Putln("func %s(c *xgb.Conn, %s) %s {",
+ r.SrcName(), r.ParamNameTypes(), r.CookieName())
+ r.CheckExt(c)
+ c.Putln("cookie := c.NewCookie(false, false)")
+ c.Putln("c.NewRequest(%s(c, %s), cookie)", r.ReqName(), r.ParamNames())
+ c.Putln("return %s{cookie}", r.CookieName())
+ c.Putln("}")
+ c.Putln("")
+
+ c.Putln("// %sChecked sends a checked request.", r.SrcName())
+ c.Putln("// If an error occurs, it can be retrieved using "+
+ "%s.Check()", r.CookieName())
+ c.Putln("func %sChecked(c *xgb.Conn, %s) %s {",
+ r.SrcName(), r.ParamNameTypes(), r.CookieName())
+ r.CheckExt(c)
+ c.Putln("cookie := c.NewCookie(true, false)")
+ c.Putln("c.NewRequest(%s(c, %s), cookie)", r.ReqName(), r.ParamNames())
+ c.Putln("return %s{cookie}", r.CookieName())
+ c.Putln("}")
+ c.Putln("")
+
+ c.Putln("// Check returns an error if one occurred for checked " +
+ "requests that are not expecting a reply.")
+ c.Putln("// This cannot be called for requests expecting a reply, " +
+ "nor for unchecked requests.")
+ c.Putln("func (cook %s) Check() error {", r.CookieName())
+ c.Putln("return cook.Cookie.Check()")
+ c.Putln("}")
+ c.Putln("")
+ }
+ r.WriteRequest(c)
+}
+
+func (r *Request) CheckExt(c *Context) {
+ if !c.protocol.isExt() {
+ return
+ }
+ c.Putln("c.ExtLock.RLock()")
+ c.Putln("defer c.ExtLock.RUnlock()")
+ c.Putln("if _, ok := c.Extensions[\"%s\"]; !ok {", c.protocol.ExtXName)
+ c.Putln("panic(\"Cannot issue request '%s' using the uninitialized "+
+ "extension '%s'. %s.Init(connObj) must be called first.\")",
+ r.SrcName(), c.protocol.ExtXName, c.protocol.PkgName())
+ c.Putln("}")
+}
+
+func (r *Request) ReadReply(c *Context) {
+ c.Putln("// %s represents the data returned from a %s request.",
+ r.ReplyTypeName(), r.SrcName())
+ c.Putln("type %s struct {", r.ReplyTypeName())
+ c.Putln("Sequence uint16 // sequence number of the request for this reply")
+ c.Putln("Length uint32 // number of bytes in this reply")
+ for _, field := range r.Reply.Fields {
+ field.Define(c)
+ }
+ c.Putln("}")
+ c.Putln("")
+
+ c.Putln("// Reply blocks and returns the reply data for a %s request.",
+ r.SrcName())
+ c.Putln("func (cook %s) Reply() (*%s, error) {",
+ r.CookieName(), r.ReplyTypeName())
+ c.Putln("buf, err := cook.Cookie.Reply()")
+ c.Putln("if err != nil {")
+ c.Putln("return nil, err")
+ c.Putln("}")
+ c.Putln("if buf == nil {")
+ c.Putln("return nil, nil")
+ c.Putln("}")
+ c.Putln("return %s(buf), nil", r.ReplyName())
+ c.Putln("}")
+ c.Putln("")
+
+ c.Putln("// %s reads a byte slice into a %s value.",
+ r.ReplyName(), r.ReplyTypeName())
+ c.Putln("func %s(buf []byte) *%s {",
+ r.ReplyName(), r.ReplyTypeName())
+ c.Putln("v := new(%s)", r.ReplyTypeName())
+ c.Putln("b := 1 // skip reply determinant")
+ c.Putln("")
+ for i, field := range r.Reply.Fields {
+ field.Read(c, "v.")
+ c.Putln("")
+ if i == 0 {
+ c.Putln("v.Sequence = xgb.Get16(buf[b:])")
+ c.Putln("b += 2")
+ c.Putln("")
+ c.Putln("v.Length = xgb.Get32(buf[b:]) // 4-byte units")
+ c.Putln("b += 4")
+ c.Putln("")
+ }
+ }
+ c.Putln("return v")
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (r *Request) WriteRequest(c *Context) {
+ sz := r.Size(c)
+ writeSize1 := func() {
+ if sz.exact {
+ c.Putln("xgb.Put16(buf[b:], uint16(size / 4)) " +
+ "// write request size in 4-byte units")
+ } else {
+ c.Putln("blen := b")
+ }
+ c.Putln("b += 2")
+ c.Putln("")
+ }
+ writeSize2 := func() {
+ if sz.exact {
+ c.Putln("return buf")
+ return
+ }
+ c.Putln("b = xgb.Pad(b)")
+ c.Putln("xgb.Put16(buf[blen:], uint16(b / 4)) " +
+ "// write request size in 4-byte units")
+ c.Putln("return buf[:b]")
+ }
+ c.Putln("// Write request to wire for %s", r.SrcName())
+ c.Putln("// %s writes a %s request to a byte slice.",
+ r.ReqName(), r.SrcName())
+ c.Putln("func %s(c *xgb.Conn, %s) []byte {",
+ r.ReqName(), r.ParamNameTypes())
+ c.Putln("size := %s", sz)
+ c.Putln("b := 0")
+ c.Putln("buf := make([]byte, size)")
+ c.Putln("")
+ if c.protocol.isExt() {
+ c.Putln("c.ExtLock.RLock()")
+ c.Putln("buf[b] = c.Extensions[\"%s\"]", c.protocol.ExtXName)
+ c.Putln("c.ExtLock.RUnlock()")
+ c.Putln("b += 1")
+ c.Putln("")
+ }
+ c.Putln("buf[b] = %d // request opcode", r.Opcode)
+ c.Putln("b += 1")
+ c.Putln("")
+ if len(r.Fields) == 0 {
+ if !c.protocol.isExt() {
+ c.Putln("b += 1 // padding")
+ }
+ writeSize1()
+ } else if c.protocol.isExt() {
+ writeSize1()
+ }
+ for i, field := range r.Fields {
+ field.Write(c, "")
+ c.Putln("")
+ if i == 0 && !c.protocol.isExt() {
+ writeSize1()
+ }
+ }
+ writeSize2()
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (r *Request) ParamNames() string {
+ names := make([]string, 0, len(r.Fields))
+ for _, field := range r.Fields {
+ switch f := field.(type) {
+ case *ValueField:
+ names = append(names, f.MaskName)
+ names = append(names, f.ListName)
+ case *PadField:
+ continue
+ case *ExprField:
+ continue
+ default:
+ names = append(names, fmt.Sprintf("%s", field.SrcName()))
+ }
+ }
+ return strings.Join(names, ", ")
+}
+
+func (r *Request) ParamNameTypes() string {
+ nameTypes := make([]string, 0, len(r.Fields))
+ for _, field := range r.Fields {
+ switch f := field.(type) {
+ case *ValueField:
+ nameTypes = append(nameTypes,
+ fmt.Sprintf("%s %s", f.MaskName, f.MaskType.SrcName()))
+ nameTypes = append(nameTypes,
+ fmt.Sprintf("%s []uint32", f.ListName))
+ case *PadField:
+ continue
+ case *ExprField:
+ continue
+ case *RequiredStartAlign:
+ continue
+ default:
+ nameTypes = append(nameTypes,
+ fmt.Sprintf("%s %s", field.SrcName(), field.SrcType()))
+ }
+ }
+ return strings.Join(nameTypes, ", ")
+}
diff --git a/nexgb/xgbgen/go_single_field.go b/nexgb/xgbgen/go_single_field.go
new file mode 100644
index 0000000..6c7218e
--- /dev/null
+++ b/nexgb/xgbgen/go_single_field.go
@@ -0,0 +1,166 @@
+package main
+
+import (
+ "fmt"
+ "log"
+)
+
+func (f *SingleField) Define(c *Context) {
+ c.Putln("%s %s", f.SrcName(), f.Type.SrcName())
+}
+
+func ReadSimpleSingleField(c *Context, name string, typ Type) {
+ switch t := typ.(type) {
+ case *Resource:
+ c.Putln("%s = %s(xgb.Get32(buf[b:]))", name, t.SrcName())
+ case *TypeDef:
+ switch t.Size().Eval() {
+ case 1:
+ c.Putln("%s = %s(buf[b])", name, t.SrcName())
+ case 2:
+ c.Putln("%s = %s(xgb.Get16(buf[b:]))", name, t.SrcName())
+ case 4:
+ c.Putln("%s = %s(xgb.Get32(buf[b:]))", name, t.SrcName())
+ case 8:
+ c.Putln("%s = %s(xgb.Get64(buf[b:]))", name, t.SrcName())
+ }
+ case *Base:
+ // If this is a bool, stop short and do something special.
+ if t.SrcName() == "bool" {
+ c.Putln("if buf[b] == 1 {")
+ c.Putln("%s = true", name)
+ c.Putln("} else {")
+ c.Putln("%s = false", name)
+ c.Putln("}")
+ break
+ }
+
+ var val string
+ switch t.Size().Eval() {
+ case 1:
+ val = fmt.Sprintf("buf[b]")
+ case 2:
+ val = fmt.Sprintf("xgb.Get16(buf[b:])")
+ case 4:
+ val = fmt.Sprintf("xgb.Get32(buf[b:])")
+ case 8:
+ val = fmt.Sprintf("xgb.Get64(buf[b:])")
+ }
+
+ // We need to convert base types if they aren't uintXX or byte
+ ty := t.SrcName()
+ if ty != "byte" && ty != "uint16" && ty != "uint32" && ty != "uint64" {
+ val = fmt.Sprintf("%s(%s)", ty, val)
+ }
+ c.Putln("%s = %s", name, val)
+ default:
+ log.Panicf("Cannot read field '%s' as a simple field with %T type.",
+ name, typ)
+ }
+
+ c.Putln("b += %s", typ.Size())
+}
+
+func (f *SingleField) Read(c *Context, prefix string) {
+ switch t := f.Type.(type) {
+ case *Resource:
+ ReadSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
+ case *TypeDef:
+ ReadSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
+ case *Base:
+ ReadSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
+ case *Struct:
+ c.Putln("%s%s = %s{}", prefix, f.SrcName(), t.SrcName())
+ c.Putln("b += %sRead(buf[b:], &%s%s)", t.SrcName(), prefix, f.SrcName())
+ case *Union:
+ c.Putln("%s%s = %s{}", prefix, f.SrcName(), t.SrcName())
+ c.Putln("b += %sRead(buf[b:], &%s%s)", t.SrcName(), prefix, f.SrcName())
+ default:
+ log.Panicf("Cannot read field '%s' with %T type.", f.XmlName(), f.Type)
+ }
+}
+
+func WriteSimpleSingleField(c *Context, name string, typ Type) {
+ switch t := typ.(type) {
+ case *Resource:
+ c.Putln("xgb.Put32(buf[b:], uint32(%s))", name)
+ case *TypeDef:
+ switch t.Size().Eval() {
+ case 1:
+ c.Putln("buf[b] = byte(%s)", name)
+ case 2:
+ c.Putln("xgb.Put16(buf[b:], uint16(%s))", name)
+ case 4:
+ c.Putln("xgb.Put32(buf[b:], uint32(%s))", name)
+ case 8:
+ c.Putln("xgb.Put64(buf[b:], uint64(%s))", name)
+ }
+ case *Base:
+ // If this is a bool, stop short and do something special.
+ if t.SrcName() == "bool" {
+ c.Putln("if %s {", name)
+ c.Putln("buf[b] = 1")
+ c.Putln("} else {")
+ c.Putln("buf[b] = 0")
+ c.Putln("}")
+ break
+ }
+
+ switch t.Size().Eval() {
+ case 1:
+ if t.SrcName() != "byte" {
+ c.Putln("buf[b] = byte(%s)", name)
+ } else {
+ c.Putln("buf[b] = %s", name)
+ }
+ case 2:
+ if t.SrcName() != "uint16" {
+ c.Putln("xgb.Put16(buf[b:], uint16(%s))", name)
+ } else {
+ c.Putln("xgb.Put16(buf[b:], %s)", name)
+ }
+ case 4:
+ if t.SrcName() != "uint32" {
+ c.Putln("xgb.Put32(buf[b:], uint32(%s))", name)
+ } else {
+ c.Putln("xgb.Put32(buf[b:], %s)", name)
+ }
+ case 8:
+ if t.SrcName() != "uint64" {
+ c.Putln("xgb.Put64(buf[b:], uint64(%s))", name)
+ } else {
+ c.Putln("xgb.Put64(buf[b:], %s)", name)
+ }
+ }
+ default:
+ log.Fatalf("Cannot read field '%s' as a simple field with %T type.",
+ name, typ)
+ }
+
+ c.Putln("b += %s", typ.Size())
+}
+
+func (f *SingleField) Write(c *Context, prefix string) {
+ switch t := f.Type.(type) {
+ case *Resource:
+ WriteSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
+ case *TypeDef:
+ WriteSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
+ case *Base:
+ WriteSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
+ case *Union:
+ c.Putln("{")
+ c.Putln("unionBytes := %s%s.Bytes()", prefix, f.SrcName())
+ c.Putln("copy(buf[b:], unionBytes)")
+ c.Putln("b += len(unionBytes)")
+ c.Putln("}")
+ case *Struct:
+ c.Putln("{")
+ c.Putln("structBytes := %s%s.Bytes()", prefix, f.SrcName())
+ c.Putln("copy(buf[b:], structBytes)")
+ c.Putln("b += len(structBytes)")
+ c.Putln("}")
+ default:
+ log.Fatalf("Cannot read field '%s' with %T type.", f.XmlName(), f.Type)
+ }
+}
diff --git a/nexgb/xgbgen/go_struct.go b/nexgb/xgbgen/go_struct.go
new file mode 100644
index 0000000..ee74d90
--- /dev/null
+++ b/nexgb/xgbgen/go_struct.go
@@ -0,0 +1,118 @@
+package main
+
+func (s *Struct) Define(c *Context) {
+ c.Putln("type %s struct {", s.SrcName())
+ for _, field := range s.Fields {
+ field.Define(c)
+ }
+ c.Putln("}")
+ c.Putln("")
+
+ // Write function that reads bytes and produces this struct.
+ s.Read(c)
+
+ // Write function that reads bytes and produces a list of this struct.
+ s.ReadList(c)
+
+ // Write function that writes bytes given this struct.
+ s.Write(c)
+
+ // Write function that writes a list of this struct.
+ s.WriteList(c)
+
+ // Write function that computes the size of a list of these structs,
+ // IF there is a list field in this struct.
+ if s.HasList() {
+ s.WriteListSize(c)
+ }
+}
+
+// Read for a struct creates a function 'ReadStructName' that takes a source
+// byte slice (i.e., the buffer) and a destination struct, and returns
+// the number of bytes read off the buffer.
+// 'ReadStructName' should only be used to read raw reply data from the wire.
+func (s *Struct) Read(c *Context) {
+ c.Putln("// %sRead reads a byte slice into a %s value.",
+ s.SrcName(), s.SrcName())
+ c.Putln("func %sRead(buf []byte, v *%s) int {", s.SrcName(), s.SrcName())
+
+ c.Putln("b := 0")
+ c.Putln("")
+ for _, field := range s.Fields {
+ field.Read(c, "v.")
+ c.Putln("")
+ }
+ c.Putln("return b")
+
+ c.Putln("}")
+ c.Putln("")
+}
+
+// ReadList for a struct creates a function 'ReadStructNameList' that takes
+// a source (i.e., the buffer) byte slice, and a destination slice and returns
+// the number of bytes read from the byte slice.
+func (s *Struct) ReadList(c *Context) {
+ c.Putln("// %sReadList reads a byte slice into a list of %s values.",
+ s.SrcName(), s.SrcName())
+ c.Putln("func %sReadList(buf []byte, dest []%s) int {",
+ s.SrcName(), s.SrcName())
+ c.Putln("b := 0")
+ c.Putln("for i := 0; i < len(dest); i++ {")
+ c.Putln("dest[i] = %s{}", s.SrcName())
+ c.Putln("b += %sRead(buf[b:], &dest[i])", s.SrcName())
+ c.Putln("}")
+
+ c.Putln("return xgb.Pad(b)")
+
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (s *Struct) Write(c *Context) {
+ c.Putln("// Bytes writes a %s value to a byte slice.", s.SrcName())
+ c.Putln("func (v %s) Bytes() []byte {", s.SrcName())
+ c.Putln("buf := make([]byte, %s)", s.Size().Reduce("v."))
+ c.Putln("b := 0")
+ c.Putln("")
+ for _, field := range s.Fields {
+ field.Write(c, "v.")
+ c.Putln("")
+ }
+ c.Putln("return buf[:b]")
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (s *Struct) WriteList(c *Context) {
+ c.Putln("// %sListBytes writes a list of %s values to a byte slice.",
+ s.SrcName(), s.SrcName())
+ c.Putln("func %sListBytes(buf []byte, list []%s) int {",
+ s.SrcName(), s.SrcName())
+ c.Putln("b := 0")
+ c.Putln("var structBytes []byte")
+ c.Putln("for _, item := range list {")
+ c.Putln("structBytes = item.Bytes()")
+ c.Putln("copy(buf[b:], structBytes)")
+ c.Putln("b += len(structBytes)")
+ c.Putln("}")
+ c.Putln("return xgb.Pad(b)")
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (s *Struct) WriteListSize(c *Context) {
+ c.Putln("// %sListSize computes the size (bytes) of a list of %s values.",
+ s.SrcName(), s.SrcName())
+ c.Putln("func %sListSize(list []%s) int {", s.SrcName(), s.SrcName())
+ c.Putln("size := 0")
+ if s.Size().Expression.Concrete() {
+ c.Putln("for _ = range list {")
+ } else {
+ c.Putln("for _, item := range list {")
+ }
+ c.Putln("size += %s", s.Size().Reduce("item."))
+ c.Putln("}")
+ c.Putln("return size")
+ c.Putln("}")
+ c.Putln("")
+}
diff --git a/nexgb/xgbgen/go_union.go b/nexgb/xgbgen/go_union.go
new file mode 100644
index 0000000..74816d3
--- /dev/null
+++ b/nexgb/xgbgen/go_union.go
@@ -0,0 +1,147 @@
+package main
+
+// Union types
+func (u *Union) Define(c *Context) {
+ c.Putln("// %s is a represention of the %s union type.",
+ u.SrcName(), u.SrcName())
+ c.Putln("// Note that to *create* a Union, you should *never* create")
+ c.Putln("// this struct directly (unless you know what you're doing).")
+ c.Putln("// Instead use one of the following constructors for '%s':",
+ u.SrcName())
+ for _, field := range u.Fields {
+ c.Putln("// %s%sNew(%s %s) %s", u.SrcName(), field.SrcName(),
+ field.SrcName(), field.SrcType(), u.SrcName())
+ }
+
+ c.Putln("type %s struct {", u.SrcName())
+ for _, field := range u.Fields {
+ field.Define(c)
+ }
+ c.Putln("}")
+ c.Putln("")
+
+ // Write functions for each field that create instances of this
+ // union using the corresponding field.
+ u.New(c)
+
+ // Write function that reads bytes and produces this union.
+ u.Read(c)
+
+ // Write function that reads bytes and produces a list of this union.
+ u.ReadList(c)
+
+ // Write function that writes bytes given this union.
+ u.Write(c)
+
+ // Write function that writes a list of this union.
+ u.WriteList(c)
+}
+
+func (u *Union) New(c *Context) {
+ for _, field := range u.Fields {
+ c.Putln("// %s%sNew constructs a new %s union type with the %s field.",
+ u.SrcName(), field.SrcName(), u.SrcName(), field.SrcName())
+ c.Putln("func %s%sNew(%s %s) %s {",
+ u.SrcName(), field.SrcName(), field.SrcName(),
+ field.SrcType(), u.SrcName())
+ c.Putln("var b int")
+ c.Putln("buf := make([]byte, %s)", u.Size())
+ c.Putln("")
+ field.Write(c, "")
+ c.Putln("")
+ c.Putln("// Create the Union type")
+ c.Putln("v := %s{}", u.SrcName())
+ c.Putln("")
+ c.Putln("// Now copy buf into all fields")
+ c.Putln("")
+ for _, field2 := range u.Fields {
+ c.Putln("b = 0 // always read the same bytes")
+ field2.Read(c, "v.")
+ c.Putln("")
+ }
+ c.Putln("return v")
+ c.Putln("}")
+ c.Putln("")
+ }
+}
+
+func (u *Union) Read(c *Context) {
+ c.Putln("// %sRead reads a byte slice into a %s value.",
+ u.SrcName(), u.SrcName())
+ c.Putln("func %sRead(buf []byte, v *%s) int {", u.SrcName(), u.SrcName())
+ c.Putln("var b int")
+ c.Putln("")
+ for _, field := range u.Fields {
+ c.Putln("b = 0 // re-read the same bytes")
+ field.Read(c, "v.")
+ c.Putln("")
+ }
+ c.Putln("return %s", u.Size())
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (u *Union) ReadList(c *Context) {
+ c.Putln("// %sReadList reads a byte slice into a list of %s values.",
+ u.SrcName(), u.SrcName())
+ c.Putln("func %sReadList(buf []byte, dest []%s) int {",
+ u.SrcName(), u.SrcName())
+ c.Putln("b := 0")
+ c.Putln("for i := 0; i < len(dest); i++ {")
+ c.Putln("dest[i] = %s{}", u.SrcName())
+ c.Putln("b += %sRead(buf[b:], &dest[i])", u.SrcName())
+ c.Putln("}")
+ c.Putln("return xgb.Pad(b)")
+ c.Putln("}")
+ c.Putln("")
+}
+
+// This is a bit tricky since writing from a Union implies that only
+// the data inside ONE of the elements is actually written.
+// However, we only currently support unions where every field has the
+// *same* *fixed* size. Thus, we make sure to always read bytes into
+// every field which allows us to simply pick the first field and write it.
+func (u *Union) Write(c *Context) {
+ c.Putln("// Bytes writes a %s value to a byte slice.", u.SrcName())
+ c.Putln("// Each field in a union must contain the same data.")
+ c.Putln("// So simply pick the first field and write that to the wire.")
+ c.Putln("func (v %s) Bytes() []byte {", u.SrcName())
+ c.Putln("buf := make([]byte, %s)", u.Size().Reduce("v."))
+ c.Putln("b := 0")
+ c.Putln("")
+ u.Fields[0].Write(c, "v.")
+ c.Putln("return buf")
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (u *Union) WriteList(c *Context) {
+ c.Putln("// %sListBytes writes a list of %s values to a byte slice.",
+ u.SrcName(), u.SrcName())
+ c.Putln("func %sListBytes(buf []byte, list []%s) int {",
+ u.SrcName(), u.SrcName())
+ c.Putln("b := 0")
+ c.Putln("var unionBytes []byte")
+ c.Putln("for _, item := range list {")
+ c.Putln("unionBytes = item.Bytes()")
+ c.Putln("copy(buf[b:], unionBytes)")
+ c.Putln("b += xgb.Pad(len(unionBytes))")
+ c.Putln("}")
+ c.Putln("return b")
+ c.Putln("}")
+ c.Putln("")
+}
+
+func (u *Union) WriteListSize(c *Context) {
+ c.Putln("// Union list size %s", u.SrcName())
+ c.Putln("// %sListSize computes the size (bytes) of a list of %s values.",
+ u.SrcName())
+ c.Putln("func %sListSize(list []%s) int {", u.SrcName(), u.SrcName())
+ c.Putln("size := 0")
+ c.Putln("for _, item := range list {")
+ c.Putln("size += %s", u.Size().Reduce("item."))
+ c.Putln("}")
+ c.Putln("return size")
+ c.Putln("}")
+ c.Putln("")
+}
diff --git a/nexgb/xgbgen/main.go b/nexgb/xgbgen/main.go
new file mode 100644
index 0000000..fd5eac7
--- /dev/null
+++ b/nexgb/xgbgen/main.go
@@ -0,0 +1,64 @@
+package main
+
+import (
+ "flag"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+var (
+ protoPath = flag.String("proto-path",
+ "/usr/share/xcb", "path to directory of X protocol XML files")
+ gofmt = flag.Bool("gofmt", true,
+ "When disabled, gofmt will not be run before outputting Go code")
+)
+
+func usage() {
+ basename := os.Args[0]
+ if lastSlash := strings.LastIndex(basename, "/"); lastSlash > -1 {
+ basename = basename[lastSlash+1:]
+ }
+ log.Printf("Usage: %s [flags] xml-file", basename)
+ flag.PrintDefaults()
+ os.Exit(1)
+}
+
+func init() {
+ log.SetFlags(0)
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+
+ if flag.NArg() != 1 {
+ log.Printf("A single XML protocol file can be processed at once.")
+ flag.Usage()
+ }
+
+ // Read the single XML file into []byte
+ xmlBytes, err := ioutil.ReadFile(flag.Arg(0))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Initialize the buffer, parse it, and filter it through gofmt.
+ c := newContext()
+ c.Morph(xmlBytes)
+
+ if !*gofmt {
+ c.out.WriteTo(os.Stdout)
+ } else {
+ cmdGofmt := exec.Command("gofmt")
+ cmdGofmt.Stdin = c.out
+ cmdGofmt.Stdout = os.Stdout
+ cmdGofmt.Stderr = os.Stderr
+ err = cmdGofmt.Run()
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
diff --git a/nexgb/xgbgen/misc.go b/nexgb/xgbgen/misc.go
new file mode 100644
index 0000000..85d788f
--- /dev/null
+++ b/nexgb/xgbgen/misc.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+ "regexp"
+ "strings"
+)
+
+// AllCaps is a regex to test if a string identifier is made of
+// all upper case letters.
+var allCaps = regexp.MustCompile("^[A-Z0-9]+$")
+
+// popCount counts number of bits 'set' in mask.
+func popCount(mask uint) uint {
+ m := uint32(mask)
+ n := uint(0)
+ for i := uint32(0); i < 32; i++ {
+ if m&(1<<i) != 0 {
+ n++
+ }
+ }
+ return n
+}
+
+// pad makes sure 'n' aligns on 4 bytes.
+func pad(n int) int {
+ return (n + 3) & ^3
+}
+
+// splitAndTitle takes a string, splits it by underscores, capitalizes the
+// first letter of each chunk, and smushes'em back together.
+func splitAndTitle(s string) string {
+ // If the string is all caps, lower it and capitalize first letter.
+ if allCaps.MatchString(s) {
+ return strings.Title(strings.ToLower(s))
+ }
+
+ // If the string has no underscores, capitalize it and leave it be.
+ if i := strings.Index(s, "_"); i == -1 {
+ return strings.Title(s)
+ }
+
+ // Now split the name at underscores, capitalize the first
+ // letter of each chunk, and smush'em back together.
+ chunks := strings.Split(s, "_")
+ for i, chunk := range chunks {
+ chunks[i] = strings.Title(strings.ToLower(chunk))
+ }
+ return strings.Join(chunks, "")
+}
diff --git a/nexgb/xgbgen/protocol.go b/nexgb/xgbgen/protocol.go
new file mode 100644
index 0000000..433f4e2
--- /dev/null
+++ b/nexgb/xgbgen/protocol.go
@@ -0,0 +1,70 @@
+package main
+
+import (
+ "log"
+ "strings"
+)
+
+// Protocol is a type that encapsulates all information about one
+// particular XML file. It also contains links to other protocol types
+// if this protocol imports other other extensions. The import relationship
+// is recursive.
+type Protocol struct {
+ Parent *Protocol
+ Name string
+ ExtXName string
+ ExtName string
+ MajorVersion string
+ MinorVersion string
+
+ Imports []*Protocol
+ Types []Type
+ Requests []*Request
+}
+
+type Protocols []*Protocol
+
+func (ps Protocols) Len() int { return len(ps) }
+func (ps Protocols) Swap(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
+func (ps Protocols) Less(i, j int) bool { return ps[i].ExtName < ps[j].ExtName }
+
+// Initialize traverses all structures, looks for 'Translation' type,
+// and looks up the real type in the namespace. It also sets the source
+// name for all relevant fields/structures.
+// This is necessary because we don't traverse the XML in order initially.
+func (p *Protocol) Initialize() {
+ for _, typ := range p.Types {
+ typ.Initialize(p)
+ }
+ for _, req := range p.Requests {
+ req.Initialize(p)
+ }
+}
+
+// PkgName returns the name of this package.
+// i.e., 'xproto' for the core X protocol, 'randr' for the RandR extension, etc.
+func (p *Protocol) PkgName() string {
+ return strings.Replace(p.Name, "_", "", -1)
+}
+
+// ProtocolGet searches the current context for the protocol with the given
+// name. (i.e., the current protocol and its imports.)
+// It is an error if one is not found.
+func (p *Protocol) ProtocolFind(name string) *Protocol {
+ if p.Name == name {
+ return p // that was easy
+ }
+ for _, imp := range p.Imports {
+ if imp.Name == name {
+ return imp
+ }
+ }
+ log.Panicf("Could not find protocol with name '%s'.", name)
+ panic("unreachable")
+}
+
+// isExt returns true if this protocol is an extension.
+// i.e., it's name isn't "xproto".
+func (p *Protocol) isExt() bool {
+ return strings.ToLower(p.Name) != "xproto"
+}
diff --git a/nexgb/xgbgen/request_reply.go b/nexgb/xgbgen/request_reply.go
new file mode 100644
index 0000000..5032e31
--- /dev/null
+++ b/nexgb/xgbgen/request_reply.go
@@ -0,0 +1,152 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "unicode"
+)
+
+// Request represents all XML 'request' nodes.
+// If the request doesn't have a reply, Reply is nil.
+type Request struct {
+ srcName string // The Go name of this request.
+ xmlName string // The XML name of this request.
+ Opcode int
+ Combine bool // Not currently used.
+ Fields []Field // All fields in the request.
+ Reply *Reply // A reply, if one exists for this request.
+}
+
+type Requests []*Request
+
+func (rs Requests) Len() int { return len(rs) }
+func (rs Requests) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] }
+func (rs Requests) Less(i, j int) bool { return rs[i].xmlName < rs[j].xmlName }
+
+// Initialize creates the proper Go source name for this request.
+// It also initializes the reply if one exists, and all fields in this request.
+func (r *Request) Initialize(p *Protocol) {
+ r.srcName = SrcName(p, r.xmlName)
+ if p.isExt() {
+ r.srcName = r.srcName
+ }
+
+ if r.Reply != nil {
+ r.Reply.Initialize(p)
+ }
+ for _, field := range r.Fields {
+ field.Initialize(p)
+ }
+}
+
+func (r *Request) SrcName() string {
+ return r.srcName
+}
+
+func (r *Request) XmlName() string {
+ return r.xmlName
+}
+
+// ReplyName gets the Go source name of the function that generates a
+// reply type from a slice of bytes.
+// The generated function is not currently exported.
+func (r *Request) ReplyName() string {
+ if r.Reply == nil {
+ log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
+ r.SrcName())
+ }
+ name := r.SrcName()
+ lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
+ return fmt.Sprintf("%sReply", lower)
+}
+
+// ReplyTypeName gets the Go source name of the type holding all reply data
+// for this request.
+func (r *Request) ReplyTypeName() string {
+ if r.Reply == nil {
+ log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
+ r.SrcName())
+ }
+ return fmt.Sprintf("%sReply", r.SrcName())
+}
+
+// ReqName gets the Go source name of the function that generates a byte
+// slice from a list of parameters.
+// The generated function is not currently exported.
+func (r *Request) ReqName() string {
+ name := r.SrcName()
+ lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
+ return fmt.Sprintf("%sRequest", lower)
+}
+
+// CookieName gets the Go source name of the type that holds cookies for
+// this request.
+func (r *Request) CookieName() string {
+ return fmt.Sprintf("%sCookie", r.SrcName())
+}
+
+// Size for Request needs a context.
+// Namely, if this is an extension, we need to account for *four* bytes
+// of a header (extension opcode, request opcode, and the sequence number).
+// If it's a core protocol request, then we only account for *three*
+// bytes of the header (remove the extension opcode).
+func (r *Request) Size(c *Context) Size {
+ size := newFixedSize(0, true)
+
+ // If this is a core protocol request, we squeeze in an extra byte of
+ // data (from the fields below) between the opcode and the size of the
+ // request. In an extension request, this byte is always occupied
+ // by the opcode of the request (while the first byte is always occupied
+ // by the opcode of the extension).
+ if !c.protocol.isExt() {
+ size = size.Add(newFixedSize(3, true))
+ } else {
+ size = size.Add(newFixedSize(4, true))
+ }
+
+ for _, field := range r.Fields {
+ switch field := field.(type) {
+ case *LocalField: // local fields don't go over the wire
+ continue
+ case *SingleField:
+ fsz := field.Size()
+ if _, isstruct := field.Type.(*Struct); isstruct {
+ fsz.Expression = fsz.Expression.Specialize(field.SrcName())
+ }
+ size = size.Add(fsz)
+ default:
+ size = size.Add(field.Size())
+ }
+ }
+ return newExpressionSize(&Padding{
+ Expr: size.Expression,
+ }, size.exact)
+}
+
+// Reply encapsulates the fields associated with a 'reply' element.
+type Reply struct {
+ Fields []Field
+}
+
+// Size gets the number of bytes in this request's reply.
+// A reply always has at least 7 bytes:
+// 1 byte: A reply discriminant (first byte set to 1)
+// 2 bytes: A sequence number
+// 4 bytes: Number of additional bytes in 4-byte units past initial 32 bytes.
+func (r *Reply) Size() Size {
+ size := newFixedSize(0, true)
+
+ // Account for reply discriminant, sequence number and reply length
+ size = size.Add(newFixedSize(7, true))
+
+ for _, field := range r.Fields {
+ size = size.Add(field.Size())
+ }
+ return size
+}
+
+func (r *Reply) Initialize(p *Protocol) {
+ for _, field := range r.Fields {
+ field.Initialize(p)
+ }
+}
diff --git a/nexgb/xgbgen/size.go b/nexgb/xgbgen/size.go
new file mode 100644
index 0000000..6e49371
--- /dev/null
+++ b/nexgb/xgbgen/size.go
@@ -0,0 +1,31 @@
+package main
+
+// Size corresponds to an expression that represents the number of bytes
+// in some *thing*. Generally, sizes are used to allocate buffers and to
+// inform X how big requests are.
+// Size is basically a thin layer over an Expression that yields easy methods
+// for adding and multiplying sizes.
+type Size struct {
+ Expression
+ exact bool
+}
+
+// newFixedSize creates a new Size with some fixed and known value.
+func newFixedSize(fixed uint, exact bool) Size {
+ return Size{&Value{v: int(fixed)}, exact}
+}
+
+// newExpressionSize creates a new Size with some expression.
+func newExpressionSize(variable Expression, exact bool) Size {
+ return Size{variable, exact}
+}
+
+// Add adds s1 and s2 and returns a new Size.
+func (s1 Size) Add(s2 Size) Size {
+ return Size{newBinaryOp("+", s1, s2), s1.exact && s2.exact}
+}
+
+// Multiply mupltiplies s1 and s2 and returns a new Size.
+func (s1 Size) Multiply(s2 Size) Size {
+ return Size{newBinaryOp("*", s1, s2), s1.exact && s2.exact}
+}
diff --git a/nexgb/xgbgen/translation.go b/nexgb/xgbgen/translation.go
new file mode 100644
index 0000000..d35fa88
--- /dev/null
+++ b/nexgb/xgbgen/translation.go
@@ -0,0 +1,427 @@
+package main
+
+/*
+ translation.go provides a 'Translate' method on every XML type that converts
+ the XML type into our "better" representation.
+
+ i.e., the representation of Fields and Expressions is just too general.
+ We end up losing a lot of the advantages of static typing if we keep
+ the types that encoding/xml forces us into.
+
+ Please see 'representation.go' for the type definitions that we're
+ translating to.
+*/
+
+import (
+ "log"
+ "strconv"
+ "strings"
+)
+
+func (xml *XML) Translate(parent *Protocol) *Protocol {
+ protocol := &Protocol{
+ Parent: parent,
+ Name: xml.Header,
+ ExtXName: xml.ExtensionXName,
+ ExtName: xml.ExtensionName,
+ MajorVersion: xml.MajorVersion,
+ MinorVersion: xml.MinorVersion,
+
+ Imports: make([]*Protocol, 0),
+ Types: make([]Type, 0),
+ Requests: make([]*Request, len(xml.Requests)),
+ }
+
+ for _, imp := range xml.Imports {
+ if imp.xml != nil {
+ protocol.Imports = append(protocol.Imports,
+ imp.xml.Translate(protocol))
+ }
+ }
+
+ for xmlName, srcName := range BaseTypeMap {
+ newBaseType := &Base{
+ srcName: srcName,
+ xmlName: xmlName,
+ size: newFixedSize(BaseTypeSizes[xmlName], true),
+ }
+ protocol.Types = append(protocol.Types, newBaseType)
+ }
+ for _, enum := range xml.Enums {
+ protocol.Types = append(protocol.Types, enum.Translate())
+ }
+ for _, xid := range xml.Xids {
+ protocol.Types = append(protocol.Types, xid.Translate())
+ }
+ for _, xidunion := range xml.XidUnions {
+ protocol.Types = append(protocol.Types, xidunion.Translate())
+ }
+ for _, typedef := range xml.TypeDefs {
+ protocol.Types = append(protocol.Types, typedef.Translate())
+ }
+ for _, s := range xml.Structs {
+ protocol.Types = append(protocol.Types, s.Translate())
+ }
+ for _, union := range xml.Unions {
+ protocol.Types = append(protocol.Types, union.Translate())
+ }
+ for _, ev := range xml.Events {
+ protocol.Types = append(protocol.Types, ev.Translate())
+ }
+ for _, evcopy := range xml.EventCopies {
+ protocol.Types = append(protocol.Types, evcopy.Translate())
+ }
+ for _, err := range xml.Errors {
+ protocol.Types = append(protocol.Types, err.Translate())
+ }
+ for _, errcopy := range xml.ErrorCopies {
+ protocol.Types = append(protocol.Types, errcopy.Translate())
+ }
+
+ for i, request := range xml.Requests {
+ protocol.Requests[i] = request.Translate()
+ }
+
+ // Now load all of the type and source name information.
+ protocol.Initialize()
+
+ // Make sure all enums have concrete values.
+ for _, typ := range protocol.Types {
+ enum, ok := typ.(*Enum)
+ if !ok {
+ continue
+ }
+ nextValue := 0
+ for _, item := range enum.Items {
+ if item.Expr == nil {
+ item.Expr = &Value{v: nextValue}
+ nextValue++
+ } else {
+ nextValue = item.Expr.Eval() + 1
+ }
+ }
+ }
+
+ return protocol
+}
+
+func (x *XMLEnum) Translate() *Enum {
+ enum := &Enum{
+ xmlName: x.Name,
+ Items: make([]*EnumItem, len(x.Items)),
+ }
+ for i, item := range x.Items {
+ enum.Items[i] = &EnumItem{
+ xmlName: item.Name,
+ Expr: item.Expr.Translate(),
+ }
+ }
+ return enum
+}
+
+func (x *XMLXid) Translate() *Resource {
+ return &Resource{
+ xmlName: x.Name,
+ }
+}
+
+func (x *XMLTypeDef) Translate() *TypeDef {
+ return &TypeDef{
+ xmlName: x.New,
+ Old: newTranslation(x.Old),
+ }
+}
+
+func (x *XMLEvent) Translate() *Event {
+ ev := &Event{
+ xmlName: x.Name,
+ Number: x.Number,
+ NoSequence: x.NoSequence,
+ Fields: make([]Field, 0, len(x.Fields)),
+ }
+ for _, field := range x.Fields {
+ if field.XMLName.Local == "doc" {
+ continue
+ }
+ ev.Fields = append(ev.Fields, field.Translate(ev))
+ }
+ return ev
+}
+
+func (x *XMLEventCopy) Translate() *EventCopy {
+ return &EventCopy{
+ xmlName: x.Name,
+ Number: x.Number,
+ Old: newTranslation(x.Ref),
+ }
+}
+
+func (x *XMLError) Translate() *Error {
+ err := &Error{
+ xmlName: x.Name,
+ Number: x.Number,
+ Fields: make([]Field, len(x.Fields)),
+ }
+ for i, field := range x.Fields {
+ err.Fields[i] = field.Translate(err)
+ }
+ return err
+}
+
+func (x *XMLErrorCopy) Translate() *ErrorCopy {
+ return &ErrorCopy{
+ xmlName: x.Name,
+ Number: x.Number,
+ Old: newTranslation(x.Ref),
+ }
+}
+
+func (x *XMLStruct) Translate() *Struct {
+ s := &Struct{
+ xmlName: x.Name,
+ Fields: make([]Field, len(x.Fields)),
+ }
+ for i, field := range x.Fields {
+ s.Fields[i] = field.Translate(s)
+ }
+ return s
+}
+
+func (x *XMLUnion) Translate() *Union {
+ u := &Union{
+ xmlName: x.Name,
+ Fields: make([]Field, len(x.Fields)),
+ }
+ for i, field := range x.Fields {
+ u.Fields[i] = field.Translate(u)
+ }
+ return u
+}
+
+func (x *XMLRequest) Translate() *Request {
+ r := &Request{
+ xmlName: x.Name,
+ Opcode: x.Opcode,
+ Combine: x.Combine,
+ Fields: make([]Field, 0, len(x.Fields)),
+ Reply: x.Reply.Translate(),
+ }
+ for _, field := range x.Fields {
+ if field.XMLName.Local == "doc" || field.XMLName.Local == "fd" {
+ continue
+ }
+ r.Fields = append(r.Fields, field.Translate(r))
+ }
+
+ // Address bug (or legacy code) in QueryTextExtents.
+ // The XML protocol description references 'string_len' in the
+ // computation of the 'odd_length' field. However, 'string_len' is not
+ // defined. Therefore, let's forcefully add it as a 'local field'.
+ // (i.e., a parameter in the caller but does not get sent over the wire.)
+ if x.Name == "QueryTextExtents" {
+ stringLenLocal := &LocalField{&SingleField{
+ xmlName: "string_len",
+ Type: newTranslation("CARD16"),
+ }}
+ r.Fields = append(r.Fields, stringLenLocal)
+ }
+
+ return r
+}
+
+func (x *XMLReply) Translate() *Reply {
+ if x == nil {
+ return nil
+ }
+
+ r := &Reply{
+ Fields: make([]Field, 0, len(x.Fields)),
+ }
+ for _, field := range x.Fields {
+ if field.XMLName.Local == "doc" || field.XMLName.Local == "fd" {
+ continue
+ }
+ r.Fields = append(r.Fields, field.Translate(r))
+ }
+ return r
+}
+
+func (x *XMLExpression) Translate() Expression {
+ if x == nil {
+ return nil
+ }
+
+ switch x.XMLName.Local {
+ case "op":
+ if len(x.Exprs) != 2 {
+ log.Panicf("'op' found %d expressions; expected 2.", len(x.Exprs))
+ }
+ return &BinaryOp{
+ Op: x.Op,
+ Expr1: x.Exprs[0].Translate(),
+ Expr2: x.Exprs[1].Translate(),
+ }
+ case "unop":
+ if len(x.Exprs) != 1 {
+ log.Panicf("'unop' found %d expressions; expected 1.", len(x.Exprs))
+ }
+ return &UnaryOp{
+ Op: x.Op,
+ Expr: x.Exprs[0].Translate(),
+ }
+ case "popcount":
+ if len(x.Exprs) != 1 {
+ log.Panicf("'popcount' found %d expressions; expected 1.",
+ len(x.Exprs))
+ }
+ return &PopCount{
+ Expr: x.Exprs[0].Translate(),
+ }
+ case "value":
+ val, err := strconv.Atoi(strings.TrimSpace(x.Data))
+ if err != nil {
+ log.Panicf("Could not convert '%s' in 'value' expression to int.",
+ x.Data)
+ }
+ return &Value{
+ v: val,
+ }
+ case "bit":
+ bit, err := strconv.Atoi(strings.TrimSpace(x.Data))
+ if err != nil {
+ log.Panicf("Could not convert '%s' in 'bit' expression to int.",
+ x.Data)
+ }
+ if bit < 0 || bit > 31 {
+ log.Panicf("A 'bit' literal must be in the range [0, 31], but "+
+ " is %d", bit)
+ }
+ return &Bit{
+ b: bit,
+ }
+ case "fieldref":
+ return &FieldRef{
+ Name: x.Data,
+ }
+ case "enumref":
+ return &EnumRef{
+ EnumKind: newTranslation(x.Ref),
+ EnumItem: x.Data,
+ }
+ case "sumof":
+ return &SumOf{
+ Name: x.Ref,
+ }
+ }
+
+ log.Panicf("Unrecognized tag '%s' in expression context. Expected one of "+
+ "op, fieldref, value, bit, enumref, unop, sumof or popcount.",
+ x.XMLName.Local)
+ panic("unreachable")
+}
+
+func (x *XMLField) Translate(parent interface{}) Field {
+ switch x.XMLName.Local {
+ case "pad":
+ return &PadField{
+ Bytes: x.Bytes,
+ Align: x.Align,
+ }
+ case "field":
+ s := &SingleField{
+ xmlName: x.Name,
+ Type: newTranslation(x.Type),
+ }
+ return s
+ case "list":
+ return &ListField{
+ xmlName: x.Name,
+ Type: newTranslation(x.Type),
+ LengthExpr: x.Expr.Translate(),
+ }
+ case "localfield":
+ return &LocalField{&SingleField{
+ xmlName: x.Name,
+ Type: newTranslation(x.Type),
+ }}
+ case "exprfield":
+ return &ExprField{
+ xmlName: x.Name,
+ Type: newTranslation(x.Type),
+ Expr: x.Expr.Translate(),
+ }
+ case "valueparam":
+ return &ValueField{
+ Parent: parent,
+ MaskType: newTranslation(x.ValueMaskType),
+ MaskName: x.ValueMaskName,
+ ListName: x.ValueListName,
+ }
+ case "switch":
+ swtch := &SwitchField{
+ Name: x.Name,
+ Expr: x.Expr.Translate(),
+ Bitcases: make([]*Bitcase, len(x.Bitcases)),
+ }
+ for i, bitcase := range x.Bitcases {
+ swtch.Bitcases[i] = bitcase.Translate()
+ }
+ return swtch
+ case "required_start_align":
+ return &RequiredStartAlign{}
+ }
+
+ log.Panicf("Unrecognized field element: %s", x.XMLName.Local)
+ panic("unreachable")
+}
+
+func (x *XMLBitcase) Translate() *Bitcase {
+ b := &Bitcase{
+ Expr: x.Expr().Translate(),
+ Fields: make([]Field, len(x.Fields)),
+ }
+ for i, field := range x.Fields {
+ b.Fields[i] = field.Translate(b)
+ }
+ return b
+}
+
+// SrcName is used to translate any identifier into a Go name.
+// Mostly used for fields, but used in a couple other places too (enum items).
+func SrcName(p *Protocol, name string) string {
+ // If it's in the name map, use that translation.
+ if newn, ok := NameMap[name]; ok {
+ return newn
+ }
+ return splitAndTitle(name)
+}
+
+func TypeSrcName(p *Protocol, typ Type) string {
+ t := typ.XmlName()
+
+ // If this is a base type, then write the raw Go type.
+ if baseType, ok := typ.(*Base); ok {
+ return baseType.SrcName()
+ }
+
+ // If it's in the type map, use that translation.
+ if newt, ok := TypeMap[t]; ok {
+ return newt
+ }
+
+ // If there's a namespace to this type, just use it and be done.
+ if colon := strings.Index(t, ":"); colon > -1 {
+ namespace := t[:colon]
+ rest := t[colon+1:]
+ return p.ProtocolFind(namespace).PkgName() + "." + splitAndTitle(rest)
+ }
+
+ // Since there's no namespace, we're left with the raw type name.
+ // If the type is part of the source we're generating (i.e., there is
+ // no parent protocol), then just return that type name.
+ // Otherwise, we must qualify it with a package name.
+ if p.Parent == nil {
+ return splitAndTitle(t)
+ }
+ return p.PkgName() + "." + splitAndTitle(t)
+}
diff --git a/nexgb/xgbgen/type.go b/nexgb/xgbgen/type.go
new file mode 100644
index 0000000..59f1a2d
--- /dev/null
+++ b/nexgb/xgbgen/type.go
@@ -0,0 +1,390 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+type Type interface {
+ Initialize(p *Protocol)
+ SrcName() string
+ XmlName() string
+ Size() Size
+
+ Define(c *Context)
+}
+
+type Types []Type
+
+func (ts Types) Len() int { return len(ts) }
+func (ts Types) Swap(i, j int) { ts[i], ts[j] = ts[j], ts[i] }
+func (ts Types) Less(i, j int) bool {
+ x1, x2 := ts[i].XmlName(), ts[j].XmlName()
+ s1, s2 := ts[i].SrcName(), ts[j].SrcName()
+ return (s1 == s2 && x1 < x2) || s1 < s2
+}
+
+// Translation is used *only* when transitioning from XML types to
+// our better representation. They are placeholders for the real types (below)
+// that will replace them.
+type Translation struct {
+ xmlName string
+}
+
+func newTranslation(name string) *Translation {
+ return &Translation{xmlName: name}
+}
+
+// RealType takes 'XmlName' and finds its real concrete type in our Protocol.
+// It is an error if we can't find such a type.
+func (t *Translation) RealType(p *Protocol) Type {
+ // Check to see if there is a namespace. If so, strip it and use it to
+ // make sure we only look for a type in that protocol.
+ namespace, typeName := "", t.XmlName()
+ if ni := strings.Index(t.XmlName(), ":"); ni > -1 {
+ namespace, typeName = strings.ToLower(typeName[:ni]), typeName[ni+1:]
+ }
+
+ if len(namespace) == 0 || namespace == strings.ToLower(p.Name) {
+ for _, typ := range p.Types {
+ if typeName == typ.XmlName() {
+ return typ
+ }
+ }
+ }
+ for _, imp := range p.Imports {
+ if len(namespace) == 0 || namespace == strings.ToLower(imp.Name) {
+ for _, typ := range imp.Types {
+ if typeName == typ.XmlName() {
+ return typ
+ }
+ }
+ }
+ }
+ panic("Could not find real type for translation type: " + t.XmlName())
+}
+
+func (t *Translation) SrcName() string {
+ panic("it is illegal to call SrcName on a translation type")
+}
+
+func (t *Translation) XmlName() string {
+ return t.xmlName
+}
+
+func (t *Translation) Size() Size {
+ panic("it is illegal to call Size on a translation type")
+}
+
+func (t *Translation) Define(c *Context) {
+ panic("it is illegal to call Define on a translation type")
+}
+
+func (t *Translation) Initialize(p *Protocol) {
+ panic("it is illegal to call Initialize on a translation type")
+}
+
+type Base struct {
+ srcName string
+ xmlName string
+ size Size
+}
+
+func (b *Base) SrcName() string {
+ return b.srcName
+}
+
+func (b *Base) XmlName() string {
+ return b.xmlName
+}
+
+func (b *Base) Size() Size {
+ return b.size
+}
+
+func (b *Base) Initialize(p *Protocol) {
+ b.srcName = TypeSrcName(p, b)
+}
+
+type Enum struct {
+ srcName string
+ xmlName string
+ Items []*EnumItem
+}
+
+type EnumItem struct {
+ srcName string
+ xmlName string
+ Expr Expression
+}
+
+func (enum *Enum) SrcName() string {
+ return enum.srcName
+}
+
+func (enum *Enum) XmlName() string {
+ return enum.xmlName
+}
+
+func (enum *Enum) Size() Size {
+ panic("Cannot take size of enum")
+}
+
+func (enum *Enum) Initialize(p *Protocol) {
+ enum.srcName = TypeSrcName(p, enum)
+ for _, item := range enum.Items {
+ item.srcName = SrcName(p, item.xmlName)
+ if item.Expr != nil {
+ item.Expr.Initialize(p)
+ }
+ }
+}
+
+type Resource struct {
+ srcName string
+ xmlName string
+}
+
+func (r *Resource) SrcName() string {
+ return r.srcName
+}
+
+func (r *Resource) XmlName() string {
+ return r.xmlName
+}
+
+func (r *Resource) Size() Size {
+ return newFixedSize(BaseTypeSizes["Id"], true)
+}
+
+func (r *Resource) Initialize(p *Protocol) {
+ r.srcName = TypeSrcName(p, r)
+}
+
+type TypeDef struct {
+ srcName string
+ xmlName string
+ Old Type
+}
+
+func (t *TypeDef) SrcName() string {
+ return t.srcName
+}
+
+func (t *TypeDef) XmlName() string {
+ return t.xmlName
+}
+
+func (t *TypeDef) Size() Size {
+ return t.Old.Size()
+}
+
+func (t *TypeDef) Initialize(p *Protocol) {
+ t.Old = t.Old.(*Translation).RealType(p)
+ t.srcName = TypeSrcName(p, t)
+}
+
+type Event struct {
+ srcName string
+ xmlName string
+ Number int
+ NoSequence bool
+ Fields []Field
+}
+
+func (e *Event) SrcName() string {
+ return e.srcName
+}
+
+func (e *Event) XmlName() string {
+ return e.xmlName
+}
+
+func (e *Event) Size() Size {
+ return newExpressionSize(&Value{v: 32}, true)
+}
+
+func (e *Event) Initialize(p *Protocol) {
+ e.srcName = TypeSrcName(p, e)
+ for _, field := range e.Fields {
+ field.Initialize(p)
+ }
+}
+
+func (e *Event) EvType() string {
+ return fmt.Sprintf("%sEvent", e.srcName)
+}
+
+type EventCopy struct {
+ srcName string
+ xmlName string
+ Old Type
+ Number int
+}
+
+func (e *EventCopy) SrcName() string {
+ return e.srcName
+}
+
+func (e *EventCopy) XmlName() string {
+ return e.xmlName
+}
+
+func (e *EventCopy) Size() Size {
+ return newExpressionSize(&Value{v: 32}, true)
+}
+
+func (e *EventCopy) Initialize(p *Protocol) {
+ e.srcName = TypeSrcName(p, e)
+ e.Old = e.Old.(*Translation).RealType(p)
+ if _, ok := e.Old.(*Event); !ok {
+ panic("an EventCopy's old type *must* be *Event")
+ }
+}
+
+func (e *EventCopy) EvType() string {
+ return fmt.Sprintf("%sEvent", e.srcName)
+}
+
+type Error struct {
+ srcName string
+ xmlName string
+ Number int
+ Fields []Field
+}
+
+func (e *Error) SrcName() string {
+ return e.srcName
+}
+
+func (e *Error) XmlName() string {
+ return e.xmlName
+}
+
+func (e *Error) Size() Size {
+ return newExpressionSize(&Value{v: 32}, true)
+}
+
+func (e *Error) Initialize(p *Protocol) {
+ e.srcName = TypeSrcName(p, e)
+ for _, field := range e.Fields {
+ field.Initialize(p)
+ }
+}
+
+func (e *Error) ErrConst() string {
+ return fmt.Sprintf("Bad%s", e.srcName)
+}
+
+func (e *Error) ErrType() string {
+ return fmt.Sprintf("%sError", e.srcName)
+}
+
+type ErrorCopy struct {
+ srcName string
+ xmlName string
+ Old Type
+ Number int
+}
+
+func (e *ErrorCopy) SrcName() string {
+ return e.srcName
+}
+
+func (e *ErrorCopy) XmlName() string {
+ return e.xmlName
+}
+
+func (e *ErrorCopy) Size() Size {
+ return newExpressionSize(&Value{v: 32}, true)
+}
+
+func (e *ErrorCopy) Initialize(p *Protocol) {
+ e.srcName = TypeSrcName(p, e)
+ e.Old = e.Old.(*Translation).RealType(p)
+ if _, ok := e.Old.(*Error); !ok {
+ panic("an ErrorCopy's old type *must* be *Event")
+ }
+}
+
+func (e *ErrorCopy) ErrConst() string {
+ return fmt.Sprintf("Bad%s", e.srcName)
+}
+
+func (e *ErrorCopy) ErrType() string {
+ return fmt.Sprintf("%sError", e.srcName)
+}
+
+type Struct struct {
+ srcName string
+ xmlName string
+ Fields []Field
+}
+
+func (s *Struct) SrcName() string {
+ return s.srcName
+}
+
+func (s *Struct) XmlName() string {
+ return s.xmlName
+}
+
+func (s *Struct) Size() Size {
+ size := newFixedSize(0, true)
+ for _, field := range s.Fields {
+ size = size.Add(field.Size())
+ }
+ return size
+}
+
+func (s *Struct) Initialize(p *Protocol) {
+ s.srcName = TypeSrcName(p, s)
+ for _, field := range s.Fields {
+ field.Initialize(p)
+ }
+}
+
+// HasList returns whether there is a field in this struct that is a list.
+// When true, a more involved calculation is necessary to compute this struct's
+// size.
+func (s *Struct) HasList() bool {
+ for _, field := range s.Fields {
+ if _, ok := field.(*ListField); ok {
+ return true
+ }
+ }
+ return false
+}
+
+type Union struct {
+ srcName string
+ xmlName string
+ Fields []Field
+}
+
+func (u *Union) SrcName() string {
+ return u.srcName
+}
+
+func (u *Union) XmlName() string {
+ return u.xmlName
+}
+
+// Size for Union is broken. At least, it's broken for XKB.
+// It *looks* like the protocol inherently relies on some amount of
+// memory unsafety, since some members of unions in XKB are *variable* in
+// length! The only thing I can come up with, maybe, is when a union has
+// variable size, simply return the raw bytes. Then it's up to the user to
+// pass those raw bytes into the appropriate New* constructor. GROSS!
+// As of now, just pluck out the first field and return that size. This
+// should work for union elements in randr.xml and xproto.xml.
+func (u *Union) Size() Size {
+ return u.Fields[0].Size()
+}
+
+func (u *Union) Initialize(p *Protocol) {
+ u.srcName = fmt.Sprintf("%sUnion", TypeSrcName(p, u))
+ for _, field := range u.Fields {
+ field.Initialize(p)
+ }
+}
diff --git a/nexgb/xgbgen/xml.go b/nexgb/xgbgen/xml.go
new file mode 100644
index 0000000..440d0a8
--- /dev/null
+++ b/nexgb/xgbgen/xml.go
@@ -0,0 +1,138 @@
+package main
+
+import (
+ "encoding/xml"
+ "io/ioutil"
+ "log"
+)
+
+type XML struct {
+ // Root 'xcb' element properties.
+ XMLName xml.Name `xml:"xcb"`
+ Header string `xml:"header,attr"`
+ ExtensionXName string `xml:"extension-xname,attr"`
+ ExtensionName string `xml:"extension-name,attr"`
+ MajorVersion string `xml:"major-version,attr"`
+ MinorVersion string `xml:"minor-version,attr"`
+
+ // Types for all top-level elements.
+ // First are the simple ones.
+ Imports XMLImports `xml:"import"`
+ Enums []*XMLEnum `xml:"enum"`
+ Xids []*XMLXid `xml:"xidtype"`
+ XidUnions []*XMLXid `xml:"xidunion"`
+ TypeDefs []*XMLTypeDef `xml:"typedef"`
+ EventCopies []*XMLEventCopy `xml:"eventcopy"`
+ ErrorCopies []*XMLErrorCopy `xml:"errorcopy"`
+
+ // Here are the complex ones, i.e., anything with "structure contents"
+ Structs []*XMLStruct `xml:"struct"`
+ Unions []*XMLUnion `xml:"union"`
+ Requests []*XMLRequest `xml:"request"`
+ Events []*XMLEvent `xml:"event"`
+ Errors []*XMLError `xml:"error"`
+}
+
+type XMLImports []*XMLImport
+
+func (imports XMLImports) Eval() {
+ for _, imp := range imports {
+ xmlBytes, err := ioutil.ReadFile(*protoPath + "/" + imp.Name + ".xml")
+ if err != nil {
+ log.Fatalf("Could not read X protocol description for import "+
+ "'%s' because: %s", imp.Name, err)
+ }
+
+ imp.xml = &XML{}
+ err = xml.Unmarshal(xmlBytes, imp.xml)
+ if err != nil {
+ log.Fatal("Could not parse X protocol description for import "+
+ "'%s' because: %s", imp.Name, err)
+ }
+
+ // recursive imports...
+ imp.xml.Imports.Eval()
+ }
+}
+
+type XMLImport struct {
+ Name string `xml:",chardata"`
+ xml *XML `xml:"-"`
+}
+
+type XMLEnum struct {
+ Name string `xml:"name,attr"`
+ Items []*XMLEnumItem `xml:"item"`
+}
+
+type XMLEnumItem struct {
+ Name string `xml:"name,attr"`
+ Expr *XMLExpression `xml:",any"`
+}
+
+type XMLXid struct {
+ XMLName xml.Name
+ Name string `xml:"name,attr"`
+}
+
+type XMLTypeDef struct {
+ Old string `xml:"oldname,attr"`
+ New string `xml:"newname,attr"`
+}
+
+type XMLEventCopy struct {
+ Name string `xml:"name,attr"`
+ Number int `xml:"number,attr"`
+ Ref string `xml:"ref,attr"`
+}
+
+type XMLErrorCopy struct {
+ Name string `xml:"name,attr"`
+ Number int `xml:"number,attr"`
+ Ref string `xml:"ref,attr"`
+}
+
+type XMLStruct struct {
+ Name string `xml:"name,attr"`
+ Fields []*XMLField `xml:",any"`
+}
+
+type XMLUnion struct {
+ Name string `xml:"name,attr"`
+ Fields []*XMLField `xml:",any"`
+}
+
+type XMLRequest struct {
+ Name string `xml:"name,attr"`
+ Opcode int `xml:"opcode,attr"`
+ Combine bool `xml:"combine-adjacent,attr"`
+ Fields []*XMLField `xml:",any"`
+ Reply *XMLReply `xml:"reply"`
+}
+
+type XMLReply struct {
+ Fields []*XMLField `xml:",any"`
+}
+
+type XMLEvent struct {
+ Name string `xml:"name,attr"`
+ Number int `xml:"number,attr"`
+ NoSequence bool `xml:"no-sequence-number,attr"`
+ Fields []*XMLField `xml:",any"`
+}
+
+type XMLError struct {
+ Name string `xml:"name,attr"`
+ Number int `xml:"number,attr"`
+ Fields []*XMLField `xml:",any"`
+}
+
+type XMLExpression struct {
+ XMLName xml.Name
+
+ Exprs []*XMLExpression `xml:",any"`
+
+ Data string `xml:",chardata"`
+ Op string `xml:"op,attr"`
+ Ref string `xml:"ref,attr"`
+}
diff --git a/nexgb/xgbgen/xml_fields.go b/nexgb/xgbgen/xml_fields.go
new file mode 100644
index 0000000..8b7b5c7
--- /dev/null
+++ b/nexgb/xgbgen/xml_fields.go
@@ -0,0 +1,86 @@
+package main
+
+import (
+ "encoding/xml"
+ "log"
+)
+
+type XMLField struct {
+ XMLName xml.Name
+
+ // For 'pad' element
+ Bytes uint `xml:"bytes,attr"`
+ Align uint16 `xml:"align,attr"`
+
+ // For 'field', 'list', 'localfield', 'exprfield' and 'switch' elements.
+ Name string `xml:"name,attr"`
+
+ // For 'field', 'list', 'localfield', and 'exprfield' elements.
+ Type string `xml:"type,attr"`
+
+ // For 'list', 'exprfield' and 'switch' elements.
+ Expr *XMLExpression `xml:",any"`
+
+ // For 'valueparm' element.
+ ValueMaskType string `xml:"value-mask-type,attr"`
+ ValueMaskName string `xml:"value-mask-name,attr"`
+ ValueListName string `xml:"value-list-name,attr"`
+
+ // For 'switch' element.
+ Bitcases []*XMLBitcase `xml:"bitcase"`
+
+ // I don't know which elements these are for. The documentation is vague.
+ // They also seem to be completely optional.
+ OptEnum string `xml:"enum,attr"`
+ OptMask string `xml:"mask,attr"`
+ OptAltEnum string `xml:"altenum,attr"`
+}
+
+// Bitcase represents a single expression followed by any number of fields.
+// Namely, if the switch's expression (all bitcases are inside a switch),
+// and'd with the bitcase's expression is equal to the bitcase expression,
+// then the fields should be included in its parent structure.
+// Note that since a bitcase is unique in that expressions and fields are
+// siblings, we must exhaustively search for one of them. Essentially,
+// it's the closest thing to a Union I can get to in Go without interfaces.
+// Would an '<expression>' tag have been too much to ask? :-(
+type XMLBitcase struct {
+ Fields []*XMLField `xml:",any"`
+
+ // All the different expressions.
+ // When it comes time to choose one, use the 'Expr' method.
+ ExprOp *XMLExpression `xml:"op"`
+ ExprUnOp *XMLExpression `xml:"unop"`
+ ExprField *XMLExpression `xml:"fieldref"`
+ ExprValue *XMLExpression `xml:"value"`
+ ExprBit *XMLExpression `xml:"bit"`
+ ExprEnum *XMLExpression `xml:"enumref"`
+ ExprSum *XMLExpression `xml:"sumof"`
+ ExprPop *XMLExpression `xml:"popcount"`
+}
+
+// Expr chooses the only non-nil Expr* field from Bitcase.
+// Panic if there is more than one non-nil expression.
+func (b *XMLBitcase) Expr() *XMLExpression {
+ choices := []*XMLExpression{
+ b.ExprOp, b.ExprUnOp, b.ExprField, b.ExprValue,
+ b.ExprBit, b.ExprEnum, b.ExprSum, b.ExprPop,
+ }
+
+ var choice *XMLExpression = nil
+ numNonNil := 0
+ for _, c := range choices {
+ if c != nil {
+ numNonNil++
+ choice = c
+ }
+ }
+
+ if choice == nil {
+ log.Panicf("No top level expression found in a bitcase.")
+ }
+ if numNonNil > 1 {
+ log.Panicf("More than one top-level expression was found in a bitcase.")
+ }
+ return choice
+}