github.com/BurntSushi/xgb@v0.0.0-20210121224620-deaf085860bc/xgbgen/request_reply.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"unicode"
     7  )
     8  
     9  // Request represents all XML 'request' nodes.
    10  // If the request doesn't have a reply, Reply is nil.
    11  type Request struct {
    12  	srcName string // The Go name of this request.
    13  	xmlName string // The XML name of this request.
    14  	Opcode  int
    15  	Combine bool    // Not currently used.
    16  	Fields  []Field // All fields in the request.
    17  	Reply   *Reply  // A reply, if one exists for this request.
    18  }
    19  
    20  type Requests []*Request
    21  
    22  func (rs Requests) Len() int           { return len(rs) }
    23  func (rs Requests) Swap(i, j int)      { rs[i], rs[j] = rs[j], rs[i] }
    24  func (rs Requests) Less(i, j int) bool { return rs[i].xmlName < rs[j].xmlName }
    25  
    26  // Initialize creates the proper Go source name for this request.
    27  // It also initializes the reply if one exists, and all fields in this request.
    28  func (r *Request) Initialize(p *Protocol) {
    29  	r.srcName = SrcName(p, r.xmlName)
    30  	if p.isExt() {
    31  		r.srcName = r.srcName
    32  	}
    33  
    34  	if r.Reply != nil {
    35  		r.Reply.Initialize(p)
    36  	}
    37  	for _, field := range r.Fields {
    38  		field.Initialize(p)
    39  	}
    40  }
    41  
    42  func (r *Request) SrcName() string {
    43  	return r.srcName
    44  }
    45  
    46  func (r *Request) XmlName() string {
    47  	return r.xmlName
    48  }
    49  
    50  // ReplyName gets the Go source name of the function that generates a
    51  // reply type from a slice of bytes.
    52  // The generated function is not currently exported.
    53  func (r *Request) ReplyName() string {
    54  	if r.Reply == nil {
    55  		log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
    56  			r.SrcName())
    57  	}
    58  	name := r.SrcName()
    59  	lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
    60  	return fmt.Sprintf("%sReply", lower)
    61  }
    62  
    63  // ReplyTypeName gets the Go source name of the type holding all reply data
    64  // for this request.
    65  func (r *Request) ReplyTypeName() string {
    66  	if r.Reply == nil {
    67  		log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
    68  			r.SrcName())
    69  	}
    70  	return fmt.Sprintf("%sReply", r.SrcName())
    71  }
    72  
    73  // ReqName gets the Go source name of the function that generates a byte
    74  // slice from a list of parameters.
    75  // The generated function is not currently exported.
    76  func (r *Request) ReqName() string {
    77  	name := r.SrcName()
    78  	lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
    79  	return fmt.Sprintf("%sRequest", lower)
    80  }
    81  
    82  // CookieName gets the Go source name of the type that holds cookies for
    83  // this request.
    84  func (r *Request) CookieName() string {
    85  	return fmt.Sprintf("%sCookie", r.SrcName())
    86  }
    87  
    88  // Size for Request needs a context.
    89  // Namely, if this is an extension, we need to account for *four* bytes
    90  // of a header (extension opcode, request opcode, and the sequence number).
    91  // If it's a core protocol request, then we only account for *three*
    92  // bytes of the header (remove the extension opcode).
    93  func (r *Request) Size(c *Context) Size {
    94  	size := newFixedSize(0, true)
    95  
    96  	// If this is a core protocol request, we squeeze in an extra byte of
    97  	// data (from the fields below) between the opcode and the size of the
    98  	// request. In an extension request, this byte is always occupied
    99  	// by the opcode of the request (while the first byte is always occupied
   100  	// by the opcode of the extension).
   101  	if !c.protocol.isExt() {
   102  		size = size.Add(newFixedSize(3, true))
   103  	} else {
   104  		size = size.Add(newFixedSize(4, true))
   105  	}
   106  
   107  	for _, field := range r.Fields {
   108  		switch field.(type) {
   109  		case *LocalField: // local fields don't go over the wire
   110  			continue
   111  		case *SingleField:
   112  			// mofos!!!
   113  			if r.SrcName() == "ConfigureWindow" &&
   114  				field.SrcName() == "ValueMask" {
   115  
   116  				continue
   117  			}
   118  			size = size.Add(field.Size())
   119  		default:
   120  			size = size.Add(field.Size())
   121  		}
   122  	}
   123  	return newExpressionSize(&Padding{
   124  		Expr: size.Expression,
   125  	}, size.exact)
   126  }
   127  
   128  // Reply encapsulates the fields associated with a 'reply' element.
   129  type Reply struct {
   130  	Fields []Field
   131  }
   132  
   133  // Size gets the number of bytes in this request's reply.
   134  // A reply always has at least 7 bytes:
   135  // 1 byte: A reply discriminant (first byte set to 1)
   136  // 2 bytes: A sequence number
   137  // 4 bytes: Number of additional bytes in 4-byte units past initial 32 bytes.
   138  func (r *Reply) Size() Size {
   139  	size := newFixedSize(0, true)
   140  
   141  	// Account for reply discriminant, sequence number and reply length
   142  	size = size.Add(newFixedSize(7, true))
   143  
   144  	for _, field := range r.Fields {
   145  		size = size.Add(field.Size())
   146  	}
   147  	return size
   148  }
   149  
   150  func (r *Reply) Initialize(p *Protocol) {
   151  	for _, field := range r.Fields {
   152  		field.Initialize(p)
   153  	}
   154  }