github.com/robotn/xgb@v0.0.0-20190912153532-2cb92d044934/xgbgen/context.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "encoding/xml" 6 "fmt" 7 "log" 8 "sort" 9 ) 10 11 // Context represents the protocol we're converting to Go, and a writer 12 // buffer to write the Go source to. 13 type Context struct { 14 protocol *Protocol 15 out *bytes.Buffer 16 } 17 18 func newContext() *Context { 19 return &Context{ 20 out: bytes.NewBuffer([]byte{}), 21 } 22 } 23 24 // Putln calls put and adds a new line to the end of 'format'. 25 func (c *Context) Putln(format string, v ...interface{}) { 26 c.Put(format+"\n", v...) 27 } 28 29 // Put is a short alias to write to 'out'. 30 func (c *Context) Put(format string, v ...interface{}) { 31 _, err := c.out.WriteString(fmt.Sprintf(format, v...)) 32 if err != nil { 33 log.Fatalf("There was an error writing to context buffer: %s", err) 34 } 35 } 36 37 // Morph is the big daddy of them all. It takes in an XML byte slice, 38 // parse it, transforms the XML types into more usable types, 39 // and writes Go code to the 'out' buffer. 40 func (c *Context) Morph(xmlBytes []byte) { 41 parsedXml := &XML{} 42 err := xml.Unmarshal(xmlBytes, parsedXml) 43 if err != nil { 44 log.Fatal(err) 45 } 46 47 // Parse all imports 48 parsedXml.Imports.Eval() 49 50 // Translate XML types to nice types 51 c.protocol = parsedXml.Translate(nil) 52 53 c.protocol.AddAlignGaps() 54 55 // Start with Go header. 56 c.Putln("// Package %s is the X client API for the %s extension.", 57 c.protocol.PkgName(), c.protocol.ExtXName) 58 c.Putln("package %s", c.protocol.PkgName()) 59 c.Putln("") 60 c.Putln("// This file is automatically generated from %s.xml. "+ 61 "Edit at your peril!", c.protocol.Name) 62 c.Putln("") 63 64 // Write imports. We always need to import at least xgb. 65 // We also need to import xproto if it's an extension. 66 c.Putln("import (") 67 c.Putln("\"github.com/robotn/xgb\"") 68 c.Putln("") 69 if c.protocol.isExt() { 70 c.Putln("\"github.com/robotn/xgb/xproto\"") 71 } 72 73 sort.Sort(Protocols(c.protocol.Imports)) 74 for _, imp := range c.protocol.Imports { 75 // We always import xproto, so skip it if it's explicitly imported 76 if imp.Name == "xproto" { 77 continue 78 } 79 c.Putln("\"github.com/robotn/xgb/%s\"", imp.Name) 80 } 81 c.Putln(")") 82 c.Putln("") 83 84 // If this is an extension, create a function to initialize the extension 85 // before it can be used. 86 if c.protocol.isExt() { 87 xname := c.protocol.ExtXName 88 89 c.Putln("// Init must be called before using the %s extension.", 90 xname) 91 c.Putln("func Init(c *xgb.Conn) error {") 92 c.Putln("reply, err := xproto.QueryExtension(c, %d, \"%s\").Reply()", 93 len(xname), xname) 94 c.Putln("switch {") 95 c.Putln("case err != nil:") 96 c.Putln("return err") 97 c.Putln("case !reply.Present:") 98 c.Putln("return xgb.Errorf(\"No extension named %s could be found on "+ 99 "on the server.\")", xname) 100 c.Putln("}") 101 c.Putln("") 102 c.Putln("c.ExtLock.Lock()") 103 c.Putln("c.Extensions[\"%s\"] = reply.MajorOpcode", xname) 104 c.Putln("c.ExtLock.Unlock()") 105 c.Putln("for evNum, fun := range xgb.NewExtEventFuncs[\"%s\"] {", 106 xname) 107 c.Putln("xgb.NewEventFuncs[int(reply.FirstEvent) + evNum] = fun") 108 c.Putln("}") 109 c.Putln("for errNum, fun := range xgb.NewExtErrorFuncs[\"%s\"] {", 110 xname) 111 c.Putln("xgb.NewErrorFuncs[int(reply.FirstError) + errNum] = fun") 112 c.Putln("}") 113 c.Putln("return nil") 114 c.Putln("}") 115 c.Putln("") 116 117 // Make sure newExtEventFuncs["EXT_NAME"] map is initialized. 118 // Same deal for newExtErrorFuncs["EXT_NAME"] 119 c.Putln("func init() {") 120 c.Putln("xgb.NewExtEventFuncs[\"%s\"] = make(map[int]xgb.NewEventFun)", 121 xname) 122 c.Putln("xgb.NewExtErrorFuncs[\"%s\"] = make(map[int]xgb.NewErrorFun)", 123 xname) 124 c.Putln("}") 125 c.Putln("") 126 } else { 127 // In the xproto package, we must provide a Setup function that uses 128 // SetupBytes in xgb.Conn to return a SetupInfo structure. 129 c.Putln("// Setup parses the setup bytes retrieved when") 130 c.Putln("// connecting into a SetupInfo struct.") 131 c.Putln("func Setup(c *xgb.Conn) *SetupInfo {") 132 c.Putln("setup := new(SetupInfo)") 133 c.Putln("SetupInfoRead(c.SetupBytes, setup)") 134 c.Putln("return setup") 135 c.Putln("}") 136 c.Putln("") 137 c.Putln("// DefaultScreen gets the default screen info from SetupInfo.") 138 c.Putln("func (s *SetupInfo) DefaultScreen(c *xgb.Conn) *ScreenInfo {") 139 c.Putln("return &s.Roots[c.DefaultScreen]") 140 c.Putln("}") 141 c.Putln("") 142 } 143 144 // Now write Go source code 145 sort.Sort(Types(c.protocol.Types)) 146 sort.Sort(Requests(c.protocol.Requests)) 147 for _, typ := range c.protocol.Types { 148 typ.Define(c) 149 } 150 for _, req := range c.protocol.Requests { 151 req.Define(c) 152 } 153 }