aboutsummaryrefslogtreecommitdiff
path: root/nexgb/xgbgen
diff options
context:
space:
mode:
authorPřemysl Janouch <p@janouch.name>2018-09-08 16:54:17 +0200
committerPřemysl Janouch <p@janouch.name>2018-09-08 16:54:17 +0200
commit3173202cc1e08762c6e156a8fffd23269a5ddb2b (patch)
tree95c4a06f8384d41b15e9c22afac0a387de79dc51 /nexgb/xgbgen
parent632b3ae494d45755525644fe5d04475c95aae364 (diff)
parent3906399e7c2a40fbaf355de572cf50a314083f64 (diff)
downloadhaven-3173202cc1e08762c6e156a8fffd23269a5ddb2b.tar.gz
haven-3173202cc1e08762c6e156a8fffd23269a5ddb2b.tar.xz
haven-3173202cc1e08762c6e156a8fffd23269a5ddb2b.zip
Merge aarzilli/xgb, branch xcb1.12 as nexgb
History has been linearized and rewritten to stay under the new subdirectory. I want to make changes incompatible to BurntSushi/xgb. The history begs for being thrown away entirely because of its quality and because it doesn't cover the Google period but it is still useful for copyright tracking.
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
+}