initial commit. not currently in a working state.
This commit is contained in:
		
							
								
								
									
										18
									
								
								nexgb/AUTHORS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								nexgb/AUTHORS
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
Andrew Gallant is the maintainer of this fork. What follows is the original
 | 
			
		||||
list of authors for the x-go-binding.
 | 
			
		||||
 | 
			
		||||
# This is the official list of XGB authors for copyright purposes.
 | 
			
		||||
# This file is distinct from the CONTRIBUTORS files.
 | 
			
		||||
# See the latter for an explanation.
 | 
			
		||||
 | 
			
		||||
# Names should be added to this file as
 | 
			
		||||
#	Name or Organization <email address>
 | 
			
		||||
# The email address is not required for organizations.
 | 
			
		||||
 | 
			
		||||
# Please keep the list sorted.
 | 
			
		||||
 | 
			
		||||
Anthony Martin <ality@pbrane.org>
 | 
			
		||||
Firmansyah Adiputra <frm.adiputra@gmail.com>
 | 
			
		||||
Google Inc.
 | 
			
		||||
Scott Lawrence <bytbox@gmail.com>
 | 
			
		||||
Tor Andersson <tor.andersson@gmail.com>
 | 
			
		||||
							
								
								
									
										39
									
								
								nexgb/CONTRIBUTORS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								nexgb/CONTRIBUTORS
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
Andrew Gallant is the maintainer of this fork. What follows is the original
 | 
			
		||||
list of contributors for the x-go-binding.
 | 
			
		||||
 | 
			
		||||
# This is the official list of people who can contribute
 | 
			
		||||
# (and typically have contributed) code to the XGB repository.
 | 
			
		||||
# The AUTHORS file lists the copyright holders; this file
 | 
			
		||||
# lists people.  For example, Google employees are listed here
 | 
			
		||||
# but not in AUTHORS, because Google holds the copyright.
 | 
			
		||||
#
 | 
			
		||||
# The submission process automatically checks to make sure
 | 
			
		||||
# that people submitting code are listed in this file (by email address).
 | 
			
		||||
#
 | 
			
		||||
# Names should be added to this file only after verifying that
 | 
			
		||||
# the individual or the individual's organization has agreed to
 | 
			
		||||
# the appropriate Contributor License Agreement, found here:
 | 
			
		||||
#
 | 
			
		||||
#     http://code.google.com/legal/individual-cla-v1.0.html
 | 
			
		||||
#     http://code.google.com/legal/corporate-cla-v1.0.html
 | 
			
		||||
#
 | 
			
		||||
# The agreement for individuals can be filled out on the web.
 | 
			
		||||
#
 | 
			
		||||
# When adding J Random Contributor's name to this file,
 | 
			
		||||
# either J's name or J's organization's name should be
 | 
			
		||||
# added to the AUTHORS file, depending on whether the
 | 
			
		||||
# individual or corporate CLA was used.
 | 
			
		||||
 | 
			
		||||
# Names should be added to this file like so:
 | 
			
		||||
#     Name <email address>
 | 
			
		||||
 | 
			
		||||
# Please keep the list sorted.
 | 
			
		||||
 | 
			
		||||
Anthony Martin <ality@pbrane.org>
 | 
			
		||||
Firmansyah Adiputra <frm.adiputra@gmail.com>
 | 
			
		||||
Ian Lance Taylor <iant@golang.org>
 | 
			
		||||
Nigel Tao <nigeltao@golang.org>
 | 
			
		||||
Robert Griesemer <gri@golang.org>
 | 
			
		||||
Russ Cox <rsc@golang.org>
 | 
			
		||||
Scott Lawrence <bytbox@gmail.com>
 | 
			
		||||
Tor Andersson <tor.andersson@gmail.com>
 | 
			
		||||
							
								
								
									
										42
									
								
								nexgb/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								nexgb/LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
// Copyright (c) 2009 The XGB Authors. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// Redistribution and use in source and binary forms, with or without
 | 
			
		||||
// modification, are permitted provided that the following conditions are
 | 
			
		||||
// met:
 | 
			
		||||
//
 | 
			
		||||
//    * Redistributions of source code must retain the above copyright
 | 
			
		||||
// notice, this list of conditions and the following disclaimer.
 | 
			
		||||
//    * Redistributions in binary form must reproduce the above
 | 
			
		||||
// copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
// in the documentation and/or other materials provided with the
 | 
			
		||||
// distribution.
 | 
			
		||||
//    * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
// contributors may be used to endorse or promote products derived from
 | 
			
		||||
// this software without specific prior written permission.
 | 
			
		||||
//
 | 
			
		||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
//
 | 
			
		||||
// Subject to the terms and conditions of this License, Google hereby
 | 
			
		||||
// grants to You a perpetual, worldwide, non-exclusive, no-charge,
 | 
			
		||||
// royalty-free, irrevocable (except as stated in this section) patent
 | 
			
		||||
// license to make, have made, use, offer to sell, sell, import, and
 | 
			
		||||
// otherwise transfer this implementation of XGB, where such license
 | 
			
		||||
// applies only to those patent claims licensable by Google that are
 | 
			
		||||
// necessarily infringed by use of this implementation of XGB. If You
 | 
			
		||||
// institute patent litigation against any entity (including a
 | 
			
		||||
// cross-claim or counterclaim in a lawsuit) alleging that this
 | 
			
		||||
// implementation of XGB or a Contribution incorporated within this
 | 
			
		||||
// implementation of XGB constitutes direct or contributory patent
 | 
			
		||||
// infringement, then any patent licenses granted to You under this
 | 
			
		||||
// License for this implementation of XGB shall terminate as of the date
 | 
			
		||||
// such litigation is filed.
 | 
			
		||||
							
								
								
									
										19
									
								
								nexgb/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								nexgb/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
XPROTO=/usr/share/xcb
 | 
			
		||||
all: xproto xinerama
 | 
			
		||||
 | 
			
		||||
xproto:
 | 
			
		||||
	python2 go_client.py $(XPROTO)/xproto.xml
 | 
			
		||||
	gofmt -w xproto.go
 | 
			
		||||
 | 
			
		||||
xinerama:
 | 
			
		||||
	python2 go_client.py $(XPROTO)/xinerama.xml
 | 
			
		||||
	gofmt -w xinerama.go
 | 
			
		||||
 | 
			
		||||
randr:
 | 
			
		||||
	python2 go_client.py $(XPROTO)/randr.xml
 | 
			
		||||
	gofmt -w randr.go
 | 
			
		||||
 | 
			
		||||
render:
 | 
			
		||||
	python2 go_client.py $(XPROTO)/render.xml
 | 
			
		||||
	gofmt -w render.go
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										33
									
								
								nexgb/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								nexgb/README
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
BurntSushi's Fork
 | 
			
		||||
=================
 | 
			
		||||
I've forked the XGB repository from Google Code due to inactivty upstream.
 | 
			
		||||
 | 
			
		||||
Much of the code has been rewritten in an effort to support thread safety
 | 
			
		||||
and multiple extensions. Namely, go_client.py has been thrown away in favor
 | 
			
		||||
of an xgbgen package.
 | 
			
		||||
 | 
			
		||||
The biggest parts that *haven't* been rewritten by me are the connection and
 | 
			
		||||
authentication handshakes. They're inherently messy, and there's really no
 | 
			
		||||
reason to re-work them.
 | 
			
		||||
 | 
			
		||||
I like to release my code under the WTFPL, but since I'm starting with someone
 | 
			
		||||
else's work, I'm leaving the original license/contributor/author information
 | 
			
		||||
in tact.
 | 
			
		||||
 | 
			
		||||
I suppose I can legitimately release xgbgen under the WTFPL.
 | 
			
		||||
 | 
			
		||||
What follows is the original README:
 | 
			
		||||
 | 
			
		||||
XGB README
 | 
			
		||||
==========
 | 
			
		||||
XGB is the X protocol Go language Binding.
 | 
			
		||||
 | 
			
		||||
It is the Go equivalent of XCB, the X protocol C-language Binding
 | 
			
		||||
(http://xcb.freedesktop.org/).
 | 
			
		||||
 | 
			
		||||
Unless otherwise noted, the XGB source files are distributed
 | 
			
		||||
under the BSD-style license found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
Contributions should follow the same procedure as for the Go project:
 | 
			
		||||
http://golang.org/doc/contribute.html
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										111
									
								
								nexgb/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								nexgb/auth.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
// Copyright 2009 The XGB Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package xgb
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getU16BE(r io.Reader, b []byte) (uint16, error) {
 | 
			
		||||
	_, err := io.ReadFull(r, b[0:2])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return uint16(b[0])<<8 + uint16(b[1]), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getBytes(r io.Reader, b []byte) ([]byte, error) {
 | 
			
		||||
	n, err := getU16BE(r, b)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if int(n) > len(b) {
 | 
			
		||||
		return nil, errors.New("bytes too long for buffer")
 | 
			
		||||
	}
 | 
			
		||||
	_, err = io.ReadFull(r, b[0:n])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return b[0:n], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getString(r io.Reader, b []byte) (string, error) {
 | 
			
		||||
	b, err := getBytes(r, b)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return string(b), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readAuthority reads the X authority file for the DISPLAY.
 | 
			
		||||
// If hostname == "" or hostname == "localhost",
 | 
			
		||||
// readAuthority uses the system's hostname (as returned by os.Hostname) instead.
 | 
			
		||||
func readAuthority(hostname, display string) (name string, data []byte, err error) {
 | 
			
		||||
	// b is a scratch buffer to use and should be at least 256 bytes long
 | 
			
		||||
	// (i.e. it should be able to hold a hostname).
 | 
			
		||||
	var b [256]byte
 | 
			
		||||
 | 
			
		||||
	// As per /usr/include/X11/Xauth.h.
 | 
			
		||||
	const familyLocal = 256
 | 
			
		||||
 | 
			
		||||
	if len(hostname) == 0 || hostname == "localhost" {
 | 
			
		||||
		hostname, err = os.Hostname()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fname := os.Getenv("XAUTHORITY")
 | 
			
		||||
	if len(fname) == 0 {
 | 
			
		||||
		home := os.Getenv("HOME")
 | 
			
		||||
		if len(home) == 0 {
 | 
			
		||||
			err = errors.New("Xauthority not found: $XAUTHORITY, $HOME not set")
 | 
			
		||||
			return "", nil, err
 | 
			
		||||
		}
 | 
			
		||||
		fname = home + "/.Xauthority"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r, err := os.Open(fname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer r.Close()
 | 
			
		||||
 | 
			
		||||
	br := bufio.NewReader(r)
 | 
			
		||||
	for {
 | 
			
		||||
		family, err := getU16BE(br, b[0:2])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		addr, err := getString(br, b[0:])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		disp, err := getString(br, b[0:])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		name0, err := getString(br, b[0:])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data0, err := getBytes(br, b[0:])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if family == familyLocal && addr == hostname && disp == display {
 | 
			
		||||
			return name0, data0, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										484
									
								
								nexgb/xgb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										484
									
								
								nexgb/xgb.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,484 @@
 | 
			
		||||
// Copyright 2009 The XGB Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// The XGB package implements the X11 core protocol.
 | 
			
		||||
// It is based on XCB: http://xcb.freedesktop.org/
 | 
			
		||||
package xgb
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	readBuffer  = 100
 | 
			
		||||
	writeBuffer = 100
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A Conn represents a connection to an X server.
 | 
			
		||||
// Only one goroutine should use a Conn's methods at a time.
 | 
			
		||||
type Conn struct {
 | 
			
		||||
	host          string
 | 
			
		||||
	conn          net.Conn
 | 
			
		||||
	nextId        Id
 | 
			
		||||
	nextCookie    uint16
 | 
			
		||||
	cookies       map[uint16]*Cookie
 | 
			
		||||
	events        queue
 | 
			
		||||
	err           error
 | 
			
		||||
	display       string
 | 
			
		||||
	defaultScreen int
 | 
			
		||||
	scratch       [32]byte
 | 
			
		||||
	Setup         SetupInfo
 | 
			
		||||
	extensions    map[string]byte
 | 
			
		||||
 | 
			
		||||
	requestChan       chan *Request
 | 
			
		||||
	requestCookieChan chan *Cookie
 | 
			
		||||
	replyChan         chan bool
 | 
			
		||||
	eventChan         chan bool
 | 
			
		||||
	errorChan         chan bool
 | 
			
		||||
 | 
			
		||||
	newIdLock   sync.Mutex
 | 
			
		||||
	writeLock   sync.Mutex
 | 
			
		||||
	dequeueLock sync.Mutex
 | 
			
		||||
	cookieLock  sync.Mutex
 | 
			
		||||
	extLock     sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Id is used for all X identifiers, such as windows, pixmaps, and GCs.
 | 
			
		||||
type Id uint32
 | 
			
		||||
 | 
			
		||||
// Request is used to abstract the difference between a request
 | 
			
		||||
// that expects a reply and a request that doesn't expect a reply.
 | 
			
		||||
type Request struct {
 | 
			
		||||
	buf        []byte
 | 
			
		||||
	cookieChan chan *Cookie
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newRequest(buf []byte, needsReply bool) *Request {
 | 
			
		||||
	req := &Request{
 | 
			
		||||
		buf:        buf,
 | 
			
		||||
		cookieChan: nil,
 | 
			
		||||
	}
 | 
			
		||||
	if needsReply {
 | 
			
		||||
		req.cookieChan = make(chan *Cookie)
 | 
			
		||||
	}
 | 
			
		||||
	return req
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cookies are the sequence numbers used to pair replies up with their requests
 | 
			
		||||
type Cookie struct {
 | 
			
		||||
	id        uint16
 | 
			
		||||
	replyChan chan []byte
 | 
			
		||||
	errorChan chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCookie(id uint16) *Cookie {
 | 
			
		||||
	return &Cookie{
 | 
			
		||||
		id:        id,
 | 
			
		||||
		replyChan: make(chan []byte, 1),
 | 
			
		||||
		errorChan: make(chan error, 1),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Event is an interface that can contain any of the events returned by the server.
 | 
			
		||||
// Use a type assertion switch to extract the Event structs.
 | 
			
		||||
type Event interface{}
 | 
			
		||||
 | 
			
		||||
// Error contains protocol errors returned to us by the X server.
 | 
			
		||||
type Error struct {
 | 
			
		||||
	Detail uint8
 | 
			
		||||
	Major  uint8
 | 
			
		||||
	Minor  uint16
 | 
			
		||||
	Cookie uint16
 | 
			
		||||
	Id     Id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Error) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Bad%s (major=%d minor=%d cookie=%d id=0x%x)",
 | 
			
		||||
		errorNames[e.Detail], e.Major, e.Minor, e.Cookie, e.Id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewID generates a new unused ID for use with requests like CreateWindow.
 | 
			
		||||
func (c *Conn) NewId() Id {
 | 
			
		||||
	c.newIdLock.Lock()
 | 
			
		||||
	defer c.newIdLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	id := c.nextId
 | 
			
		||||
	// TODO: handle ID overflow
 | 
			
		||||
	c.nextId++
 | 
			
		||||
	return id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterExtension adds the respective extension's major op code to
 | 
			
		||||
// the extensions map.
 | 
			
		||||
func (c *Conn) RegisterExtension(name string) error {
 | 
			
		||||
	nameUpper := strings.ToUpper(name)
 | 
			
		||||
	reply, err := c.QueryExtension(nameUpper)
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case err != nil:
 | 
			
		||||
		return err
 | 
			
		||||
	case !reply.Present:
 | 
			
		||||
		return errors.New(fmt.Sprintf("No extension named '%s' is present.",
 | 
			
		||||
			nameUpper))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.extLock.Lock()
 | 
			
		||||
	c.extensions[nameUpper] = reply.MajorOpcode
 | 
			
		||||
	c.extLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A simple queue used to stow away events.
 | 
			
		||||
type queue struct {
 | 
			
		||||
	data [][]byte
 | 
			
		||||
	a, b int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (q *queue) queue(item []byte) {
 | 
			
		||||
	if q.b == len(q.data) {
 | 
			
		||||
		if q.a > 0 {
 | 
			
		||||
			copy(q.data, q.data[q.a:q.b])
 | 
			
		||||
			q.a, q.b = 0, q.b-q.a
 | 
			
		||||
		} else {
 | 
			
		||||
			newData := make([][]byte, (len(q.data)*3)/2)
 | 
			
		||||
			copy(newData, q.data)
 | 
			
		||||
			q.data = newData
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	q.data[q.b] = item
 | 
			
		||||
	q.b++
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (q *queue) dequeue(c *Conn) []byte {
 | 
			
		||||
	c.dequeueLock.Lock()
 | 
			
		||||
	defer c.dequeueLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	if q.a < q.b {
 | 
			
		||||
		item := q.data[q.a]
 | 
			
		||||
		q.a++
 | 
			
		||||
		return item
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newWriteChan creates the channel required for writing to the net.Conn.
 | 
			
		||||
func (c *Conn) newRequestChannels() {
 | 
			
		||||
	c.requestChan = make(chan *Request, writeBuffer)
 | 
			
		||||
	c.requestCookieChan = make(chan *Cookie, 1)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for request := range c.requestChan {
 | 
			
		||||
			cookieNum := c.nextCookie
 | 
			
		||||
			c.nextCookie++
 | 
			
		||||
 | 
			
		||||
			if request.cookieChan != nil {
 | 
			
		||||
				cookie := newCookie(cookieNum)
 | 
			
		||||
				c.cookies[cookieNum] = cookie
 | 
			
		||||
				request.cookieChan <- cookie
 | 
			
		||||
			}
 | 
			
		||||
			if _, err := c.conn.Write(request.buf); err != nil {
 | 
			
		||||
				fmt.Fprintf(os.Stderr, "x protocol write error: %s\n", err)
 | 
			
		||||
				close(c.requestChan)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// request is a buffered write to net.Conn.
 | 
			
		||||
func (c *Conn) request(buf []byte, needsReply bool) *Cookie {
 | 
			
		||||
	req := newRequest(buf, needsReply)
 | 
			
		||||
	c.requestChan <- req
 | 
			
		||||
 | 
			
		||||
	if req.cookieChan != nil {
 | 
			
		||||
		cookie := <-req.cookieChan
 | 
			
		||||
		close(req.cookieChan)
 | 
			
		||||
		return cookie
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) sendRequest(needsReply bool, bufs ...[]byte) *Cookie {
 | 
			
		||||
	if len(bufs) == 1 {
 | 
			
		||||
		return c.request(bufs[0], needsReply)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	total := make([]byte, 0)
 | 
			
		||||
	for _, buf := range bufs {
 | 
			
		||||
		total = append(total, buf...)
 | 
			
		||||
	}
 | 
			
		||||
	return c.request(total, needsReply)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) newReadChannels() {
 | 
			
		||||
	c.eventChan = make(chan bool, readBuffer)
 | 
			
		||||
 | 
			
		||||
	onError := func() {
 | 
			
		||||
		panic("read error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			buf := make([]byte, 32)
 | 
			
		||||
			if _, err := io.ReadFull(c.conn, buf); err != nil {
 | 
			
		||||
				fmt.Fprintf(os.Stderr, "x protocol read error: %s\n", err)
 | 
			
		||||
				onError()
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch buf[0] {
 | 
			
		||||
			case 0:
 | 
			
		||||
				err := &Error{
 | 
			
		||||
					Detail: buf[1],
 | 
			
		||||
					Cookie: uint16(get16(buf[2:])),
 | 
			
		||||
					Id:     Id(get32(buf[4:])),
 | 
			
		||||
					Minor:  get16(buf[8:]),
 | 
			
		||||
					Major:  buf[10],
 | 
			
		||||
				}
 | 
			
		||||
				if cookie, ok := c.cookies[err.Cookie]; ok {
 | 
			
		||||
					cookie.errorChan <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					fmt.Fprintf(os.Stderr, "x protocol error: %s\n", err)
 | 
			
		||||
				}
 | 
			
		||||
			case 1:
 | 
			
		||||
				seq := uint16(get16(buf[2:]))
 | 
			
		||||
				if _, ok := c.cookies[seq]; !ok {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				size := get32(buf[4:])
 | 
			
		||||
				if size > 0 {
 | 
			
		||||
					bigbuf := make([]byte, 32+size*4, 32+size*4)
 | 
			
		||||
					copy(bigbuf[0:32], buf)
 | 
			
		||||
					if _, err := io.ReadFull(c.conn, bigbuf[32:]); err != nil {
 | 
			
		||||
						fmt.Fprintf(os.Stderr,
 | 
			
		||||
							"x protocol read error: %s\n", err)
 | 
			
		||||
						onError()
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
					c.cookies[seq].replyChan <- bigbuf
 | 
			
		||||
				} else {
 | 
			
		||||
					c.cookies[seq].replyChan <- buf
 | 
			
		||||
				}
 | 
			
		||||
			default:
 | 
			
		||||
				c.events.queue(buf)
 | 
			
		||||
				select {
 | 
			
		||||
				case c.eventChan <- true:
 | 
			
		||||
				default:
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) waitForReply(cookie *Cookie) ([]byte, error) {
 | 
			
		||||
	if cookie == nil {
 | 
			
		||||
		panic("nil cookie")
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := c.cookies[cookie.id]; !ok {
 | 
			
		||||
		panic("waiting for a cookie that will never come")
 | 
			
		||||
	}
 | 
			
		||||
	select {
 | 
			
		||||
	case reply := <-cookie.replyChan:
 | 
			
		||||
		return reply, nil
 | 
			
		||||
	case err := <-cookie.errorChan:
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WaitForEvent returns the next event from the server.
 | 
			
		||||
// It will block until an event is available.
 | 
			
		||||
func (c *Conn) WaitForEvent() (Event, error) {
 | 
			
		||||
	for {
 | 
			
		||||
		if reply := c.events.dequeue(c); reply != nil {
 | 
			
		||||
			return parseEvent(reply)
 | 
			
		||||
		}
 | 
			
		||||
		if !<-c.eventChan {
 | 
			
		||||
			return nil, errors.New("Event channel has been closed.")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PollForEvent returns the next event from the server if one is available in the internal queue.
 | 
			
		||||
// It will not read from the connection, so you must call WaitForEvent to receive new events.
 | 
			
		||||
// Only use this function to empty the queue without blocking.
 | 
			
		||||
func (c *Conn) PollForEvent() (Event, error) {
 | 
			
		||||
	if reply := c.events.dequeue(c); reply != nil {
 | 
			
		||||
		return parseEvent(reply)
 | 
			
		||||
	}
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial connects to the X server given in the 'display' string.
 | 
			
		||||
// If 'display' is empty it will be taken from os.Getenv("DISPLAY").
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
//	Dial(":1")                 // connect to net.Dial("unix", "", "/tmp/.X11-unix/X1")
 | 
			
		||||
//	Dial("/tmp/launch-123/:0") // connect to net.Dial("unix", "", "/tmp/launch-123/:0")
 | 
			
		||||
//	Dial("hostname:2.1")       // connect to net.Dial("tcp", "", "hostname:6002")
 | 
			
		||||
//	Dial("tcp/hostname:1.0")   // connect to net.Dial("tcp", "", "hostname:6001")
 | 
			
		||||
func Dial(display string) (*Conn, error) {
 | 
			
		||||
	c, err := connect(display)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get authentication data
 | 
			
		||||
	authName, authData, err := readAuthority(c.host, c.display)
 | 
			
		||||
	noauth := false
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Could not get authority info: %v\n", err)
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Trying connection without authority info...\n")
 | 
			
		||||
		authName = ""
 | 
			
		||||
		authData = []byte{}
 | 
			
		||||
		noauth = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1".
 | 
			
		||||
	if !noauth && (authName != "MIT-MAGIC-COOKIE-1" || len(authData) != 16) {
 | 
			
		||||
		return nil, errors.New("unsupported auth protocol " + authName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf := make([]byte, 12+pad(len(authName))+pad(len(authData)))
 | 
			
		||||
	buf[0] = 0x6c
 | 
			
		||||
	buf[1] = 0
 | 
			
		||||
	put16(buf[2:], 11)
 | 
			
		||||
	put16(buf[4:], 0)
 | 
			
		||||
	put16(buf[6:], uint16(len(authName)))
 | 
			
		||||
	put16(buf[8:], uint16(len(authData)))
 | 
			
		||||
	put16(buf[10:], 0)
 | 
			
		||||
	copy(buf[12:], []byte(authName))
 | 
			
		||||
	copy(buf[12+pad(len(authName)):], authData)
 | 
			
		||||
	if _, err = c.conn.Write(buf); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	head := make([]byte, 8)
 | 
			
		||||
	if _, err = io.ReadFull(c.conn, head[0:8]); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	code := head[0]
 | 
			
		||||
	reasonLen := head[1]
 | 
			
		||||
	major := get16(head[2:])
 | 
			
		||||
	minor := get16(head[4:])
 | 
			
		||||
	dataLen := get16(head[6:])
 | 
			
		||||
 | 
			
		||||
	if major != 11 || minor != 0 {
 | 
			
		||||
		return nil, errors.New(fmt.Sprintf("x protocol version mismatch: %d.%d", major, minor))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf = make([]byte, int(dataLen)*4+8, int(dataLen)*4+8)
 | 
			
		||||
	copy(buf, head)
 | 
			
		||||
	if _, err = io.ReadFull(c.conn, buf[8:]); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if code == 0 {
 | 
			
		||||
		reason := buf[8 : 8+reasonLen]
 | 
			
		||||
		return nil, errors.New(fmt.Sprintf("x protocol authentication refused: %s", string(reason)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getSetupInfo(buf, &c.Setup)
 | 
			
		||||
 | 
			
		||||
	if c.defaultScreen >= len(c.Setup.Roots) {
 | 
			
		||||
		c.defaultScreen = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.nextId = Id(c.Setup.ResourceIdBase)
 | 
			
		||||
	c.nextCookie = 1
 | 
			
		||||
	c.cookies = make(map[uint16]*Cookie)
 | 
			
		||||
	c.events = queue{make([][]byte, 100), 0, 0}
 | 
			
		||||
	c.extensions = make(map[string]byte)
 | 
			
		||||
 | 
			
		||||
	c.newReadChannels()
 | 
			
		||||
	c.newRequestChannels()
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes the connection to the X server.
 | 
			
		||||
func (c *Conn) Close() { c.conn.Close() }
 | 
			
		||||
 | 
			
		||||
func connect(display string) (*Conn, error) {
 | 
			
		||||
	if len(display) == 0 {
 | 
			
		||||
		display = os.Getenv("DISPLAY")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	display0 := display
 | 
			
		||||
	if len(display) == 0 {
 | 
			
		||||
		return nil, errors.New("empty display string")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	colonIdx := strings.LastIndex(display, ":")
 | 
			
		||||
	if colonIdx < 0 {
 | 
			
		||||
		return nil, errors.New("bad display string: " + display0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var protocol, socket string
 | 
			
		||||
	c := new(Conn)
 | 
			
		||||
 | 
			
		||||
	if display[0] == '/' {
 | 
			
		||||
		socket = display[0:colonIdx]
 | 
			
		||||
	} else {
 | 
			
		||||
		slashIdx := strings.LastIndex(display, "/")
 | 
			
		||||
		if slashIdx >= 0 {
 | 
			
		||||
			protocol = display[0:slashIdx]
 | 
			
		||||
			c.host = display[slashIdx+1 : colonIdx]
 | 
			
		||||
		} else {
 | 
			
		||||
			c.host = display[0:colonIdx]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	display = display[colonIdx+1 : len(display)]
 | 
			
		||||
	if len(display) == 0 {
 | 
			
		||||
		return nil, errors.New("bad display string: " + display0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var scr string
 | 
			
		||||
	dotIdx := strings.LastIndex(display, ".")
 | 
			
		||||
	if dotIdx < 0 {
 | 
			
		||||
		c.display = display[0:]
 | 
			
		||||
	} else {
 | 
			
		||||
		c.display = display[0:dotIdx]
 | 
			
		||||
		scr = display[dotIdx+1:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dispnum, err := strconv.Atoi(c.display)
 | 
			
		||||
	if err != nil || dispnum < 0 {
 | 
			
		||||
		return nil, errors.New("bad display string: " + display0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(scr) != 0 {
 | 
			
		||||
		c.defaultScreen, err = strconv.Atoi(scr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, errors.New("bad display string: " + display0)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Connect to server
 | 
			
		||||
	if len(socket) != 0 {
 | 
			
		||||
		c.conn, err = net.Dial("unix", socket+":"+c.display)
 | 
			
		||||
	} else if len(c.host) != 0 {
 | 
			
		||||
		if protocol == "" {
 | 
			
		||||
			protocol = "tcp"
 | 
			
		||||
		}
 | 
			
		||||
		c.conn, err = net.Dial(protocol, c.host+":"+strconv.Itoa(6000+dispnum))
 | 
			
		||||
	} else {
 | 
			
		||||
		c.conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+c.display)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.New("cannot connect to " + display0 + ": " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										103
									
								
								nexgb/xgb_help.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								nexgb/xgb_help.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
package xgb
 | 
			
		||||
 | 
			
		||||
// getExtensionOpcode retrieves the extension opcode from the extensions map.
 | 
			
		||||
// If one doesn't exist, just return 0. An X error will likely result.
 | 
			
		||||
func (c *Conn) getExtensionOpcode(name string) byte {
 | 
			
		||||
	return c.extensions[name]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) bytesPadding(buf []byte) []byte {
 | 
			
		||||
	return append(buf, make([]byte, pad(len(buf))-len(buf))...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) bytesString(str string) []byte {
 | 
			
		||||
	return c.bytesPadding([]byte(str))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) bytesStrList(list []Str, length int) []byte {
 | 
			
		||||
	buf := make([]byte, 0)
 | 
			
		||||
	for _, str := range list {
 | 
			
		||||
		buf = append(buf, []byte(str.Name)...)
 | 
			
		||||
	}
 | 
			
		||||
	return c.bytesPadding(buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) bytesUInt32List(list []uint32) []byte {
 | 
			
		||||
	buf := make([]byte, len(list)*4)
 | 
			
		||||
	for i, item := range list {
 | 
			
		||||
		put32(buf[i*4:], item)
 | 
			
		||||
	}
 | 
			
		||||
	return c.bytesPadding(buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) bytesIdList(list []Id, length int) []byte {
 | 
			
		||||
	buf := make([]byte, length*4)
 | 
			
		||||
	for i, item := range list {
 | 
			
		||||
		put32(buf[i*4:], uint32(item))
 | 
			
		||||
	}
 | 
			
		||||
	return c.bytesPadding(buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pad a length to align on 4 bytes.
 | 
			
		||||
func pad(n int) int { return (n + 3) & ^3 }
 | 
			
		||||
 | 
			
		||||
func put16(buf []byte, v uint16) {
 | 
			
		||||
	buf[0] = byte(v)
 | 
			
		||||
	buf[1] = byte(v >> 8)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func put32(buf []byte, v uint32) {
 | 
			
		||||
	buf[0] = byte(v)
 | 
			
		||||
	buf[1] = byte(v >> 8)
 | 
			
		||||
	buf[2] = byte(v >> 16)
 | 
			
		||||
	buf[3] = byte(v >> 24)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func get16(buf []byte) uint16 {
 | 
			
		||||
	v := uint16(buf[0])
 | 
			
		||||
	v |= uint16(buf[1]) << 8
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func get32(buf []byte) uint32 {
 | 
			
		||||
	v := uint32(buf[0])
 | 
			
		||||
	v |= uint32(buf[1]) << 8
 | 
			
		||||
	v |= uint32(buf[2]) << 16
 | 
			
		||||
	v |= uint32(buf[3]) << 24
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Voodoo to count the number of bits set in a value list mask.
 | 
			
		||||
func popCount(mask0 int) int {
 | 
			
		||||
	mask := uint32(mask0)
 | 
			
		||||
	n := 0
 | 
			
		||||
	for i := uint32(0); i < 32; i++ {
 | 
			
		||||
		if mask&(1<<i) != 0 {
 | 
			
		||||
			n++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultScreen returns the Screen info for the default screen, which is
 | 
			
		||||
// 0 or the one given in the display argument to Dial.
 | 
			
		||||
func (c *Conn) DefaultScreen() *ScreenInfo { return &c.Setup.Roots[c.defaultScreen] }
 | 
			
		||||
 | 
			
		||||
// ClientMessageData holds the data from a client message,
 | 
			
		||||
// duplicated in three forms because Go doesn't have unions.
 | 
			
		||||
type ClientMessageData struct {
 | 
			
		||||
	Data8  [20]byte
 | 
			
		||||
	Data16 [10]uint16
 | 
			
		||||
	Data32 [5]uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getClientMessageData(b []byte, v *ClientMessageData) int {
 | 
			
		||||
	copy(v.Data8[:], b)
 | 
			
		||||
	for i := 0; i < 10; i++ {
 | 
			
		||||
		v.Data16[i] = get16(b[i*2:])
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < 5; i++ {
 | 
			
		||||
		v.Data32[i] = get32(b[i*4:])
 | 
			
		||||
	}
 | 
			
		||||
	return 20
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								nexgb/xgbgen/context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								nexgb/xgbgen/context.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Context struct {
 | 
			
		||||
	xml *XML
 | 
			
		||||
	out *bytes.Buffer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newContext() *Context {
 | 
			
		||||
	return &Context{
 | 
			
		||||
		xml: &XML{},
 | 
			
		||||
		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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TypePrefix searches the parsed XML for a type matching 'needle'.
 | 
			
		||||
// It then returns the appropriate prefix to be used in source code.
 | 
			
		||||
// Note that the core X protocol *is* a namespace, but does not have a prefix.
 | 
			
		||||
// Also note that you should probably check the BaseTypeMap and TypeMap
 | 
			
		||||
// before calling this function.
 | 
			
		||||
func (c *Context) TypePrefix(needle Type) string {
 | 
			
		||||
	// If this is xproto, quit. No prefixes needed.
 | 
			
		||||
	if c.xml.Header == "xproto" {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// First check for the type in the current namespace.
 | 
			
		||||
	if c.xml.HasType(needle) {
 | 
			
		||||
		return strings.Title(c.xml.Header)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now check each of the imports...
 | 
			
		||||
	for _, imp := range c.xml.Imports {
 | 
			
		||||
		if imp.xml.Header != "xproto" && imp.xml.HasType(needle) {
 | 
			
		||||
			return strings.Title(imp.xml.Header)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Translate is the big daddy of them all. It takes in an XML byte slice
 | 
			
		||||
// and writes Go code to the 'out' buffer.
 | 
			
		||||
func (c *Context) Translate(xmlBytes []byte) {
 | 
			
		||||
	err := xml.Unmarshal(xmlBytes, c.xml)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Parse all imports
 | 
			
		||||
	c.xml.Imports.Eval()
 | 
			
		||||
 | 
			
		||||
	// Make sure all top level enumerations have expressions
 | 
			
		||||
	// (For when there are empty items.)
 | 
			
		||||
	c.xml.Enums.Eval()
 | 
			
		||||
 | 
			
		||||
	// It's Morphin' Time!
 | 
			
		||||
	c.xml.Morph(c)
 | 
			
		||||
 | 
			
		||||
	// for _, req := range c.xml.Requests { 
 | 
			
		||||
		// if req.Name != "CreateContext" && req.Name != "MakeCurrent" { 
 | 
			
		||||
			// continue 
 | 
			
		||||
		// } 
 | 
			
		||||
		// log.Println(req.Name) 
 | 
			
		||||
		// for _, field := range req.Fields { 
 | 
			
		||||
			// log.Println("\t", field.XMLName.Local, field.Type.Morph(c)) 
 | 
			
		||||
		// } 
 | 
			
		||||
	// } 
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										255
									
								
								nexgb/xgbgen/go.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								nexgb/xgbgen/go.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,255 @@
 | 
			
		||||
package main
 | 
			
		||||
/*
 | 
			
		||||
	To the best of my ability, these are all of the Go specific formatting
 | 
			
		||||
	functions. If I've designed xgbgen correctly, this should be only the
 | 
			
		||||
	place that you change things to generate code for a new language.
 | 
			
		||||
 | 
			
		||||
	This file is organized as follows:
 | 
			
		||||
 | 
			
		||||
	* Imports and helper variables.
 | 
			
		||||
	* Manual type and name override maps.
 | 
			
		||||
	* Helper morphing functions.
 | 
			
		||||
	* Morphing functions for each "unit".
 | 
			
		||||
	* Morphing functions for collections of "units".
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
// Manual type and name overrides.
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
// 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",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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{ }
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
// Helper functions that aide in morphing repetive constructs.
 | 
			
		||||
// i.e., "structure contents", expressions, type and identifier names, etc.
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
// Morph changes every TYPE (not names) into something suitable
 | 
			
		||||
// for your language.
 | 
			
		||||
func (typ Type) Morph(c *Context) string {
 | 
			
		||||
	t := string(typ)
 | 
			
		||||
 | 
			
		||||
	// If this is a base type, then write the raw Go type.
 | 
			
		||||
	if newt, ok := BaseTypeMap[t]; ok {
 | 
			
		||||
		return newt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If it's in the type map, use that translation.
 | 
			
		||||
	if newt, ok := TypeMap[t]; ok {
 | 
			
		||||
		return newt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If it's a resource type, just use 'Id'.
 | 
			
		||||
	if c.xml.IsResource(typ) {
 | 
			
		||||
		return "Id"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 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 splitAndTitle(namespace) + splitAndTitle(rest)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Since there is no namespace, we need to look for a namespace
 | 
			
		||||
	// in the current context.
 | 
			
		||||
	return c.TypePrefix(typ) + splitAndTitle(t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Morph changes every identifier (NOT type) into something suitable
 | 
			
		||||
// for your language.
 | 
			
		||||
func (name Name) Morph(c *Context) string {
 | 
			
		||||
	n := string(name)
 | 
			
		||||
 | 
			
		||||
	// If it's in the name map, use that translation.
 | 
			
		||||
	if newn, ok := NameMap[n]; ok {
 | 
			
		||||
		return newn
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return splitAndTitle(n)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
// Per element morphing.
 | 
			
		||||
// Below are functions that morph a single unit.
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
 | 
			
		||||
// Import morphing.
 | 
			
		||||
func (imp *Import) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// import \"%s\"", imp.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Enum morphing.
 | 
			
		||||
func (enum *Enum) Morph(c *Context) {
 | 
			
		||||
	c.Putln("const (")
 | 
			
		||||
	for _, item := range enum.Items {
 | 
			
		||||
		c.Putln("%s%s = %d", enum.Name.Morph(c), item.Name.Morph(c),
 | 
			
		||||
			item.Expr.Eval())
 | 
			
		||||
	}
 | 
			
		||||
	c.Putln(")\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Xid morphing.
 | 
			
		||||
func (xid *Xid) Morph(c *Context) {
 | 
			
		||||
	// Don't emit anything for xid types for now.
 | 
			
		||||
	// We're going to force them all to simply be 'Id'
 | 
			
		||||
	// to avoid excessive type converting.
 | 
			
		||||
	// c.Putln("type %s Id", xid.Name.Morph(c)) 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TypeDef morphing.
 | 
			
		||||
func (typedef *TypeDef) Morph(c *Context) {
 | 
			
		||||
	c.Putln("type %s %s", typedef.Old.Morph(c), typedef.New.Morph(c))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Struct morphing.
 | 
			
		||||
func (strct *Struct) Morph(c *Context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Union morphing.
 | 
			
		||||
func (union *Union) Morph(c *Context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Request morphing.
 | 
			
		||||
func (request *Request) Morph(c *Context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Event morphing.
 | 
			
		||||
func (ev *Event) Morph(c *Context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EventCopy morphing.
 | 
			
		||||
func (evcopy *EventCopy) Morph(c *Context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error morphing.
 | 
			
		||||
func (err *Error) Morph(c *Context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrorCopy morphing.
 | 
			
		||||
func (errcopy *ErrorCopy) Morph(c *Context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
// Collection morphing.
 | 
			
		||||
// Below are functions that morph a collections of units.
 | 
			
		||||
// Most of these can probably remain unchanged, but they are useful if you
 | 
			
		||||
// need to group all of some "unit" in a single block or something.
 | 
			
		||||
/******************************************************************************/
 | 
			
		||||
func (imports Imports) Morph(c *Context) {
 | 
			
		||||
	if len(imports) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Putln("// Imports are not required for XGB since everything is in")
 | 
			
		||||
	c.Putln("// a single package. Still these may be useful for ")
 | 
			
		||||
	c.Putln("// reference purposes.")
 | 
			
		||||
	for _, imp := range imports {
 | 
			
		||||
		imp.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (enums Enums) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// Enums\n")
 | 
			
		||||
	for _, enum := range enums {
 | 
			
		||||
		enum.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (xids Xids) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// Xids\n")
 | 
			
		||||
	for _, xid := range xids {
 | 
			
		||||
		xid.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (typedefs TypeDefs) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// TypeDefs\n")
 | 
			
		||||
	for _, typedef := range typedefs {
 | 
			
		||||
		typedef.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (strct Structs) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// Structs\n")
 | 
			
		||||
	for _, typedef := range strct {
 | 
			
		||||
		typedef.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (union Unions) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// Unions\n")
 | 
			
		||||
	for _, typedef := range union {
 | 
			
		||||
		typedef.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (request Requests) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// Requests\n")
 | 
			
		||||
	for _, typedef := range request {
 | 
			
		||||
		typedef.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (event Events) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// Events\n")
 | 
			
		||||
	for _, typedef := range event {
 | 
			
		||||
		typedef.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (evcopy EventCopies) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// Event Copies\n")
 | 
			
		||||
	for _, typedef := range evcopy {
 | 
			
		||||
		typedef.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err Errors) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// Errors\n")
 | 
			
		||||
	for _, typedef := range err {
 | 
			
		||||
		typedef.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (errcopy ErrorCopies) Morph(c *Context) {
 | 
			
		||||
	c.Putln("// Error copies\n")
 | 
			
		||||
	for _, typedef := range errcopy {
 | 
			
		||||
		typedef.Morph(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										64
									
								
								nexgb/xgbgen/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								nexgb/xgbgen/main.go
									
									
									
									
									
										Normal file
									
								
							@@ -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.Translate(xmlBytes)
 | 
			
		||||
 | 
			
		||||
	if !*gofmt {
 | 
			
		||||
		c.out.WriteTo(os.Stdout)
 | 
			
		||||
	} else {
 | 
			
		||||
		cmdGofmt := exec.Command("gofmt")
 | 
			
		||||
		cmdGofmt.Stdin = c.out
 | 
			
		||||
		cmdGofmt.Stdout = os.Stdout
 | 
			
		||||
		err = cmdGofmt.Run()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								nexgb/xgbgen/misc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								nexgb/xgbgen/misc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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, leave it be.
 | 
			
		||||
	if i := strings.Index(s, "_"); i == -1 {
 | 
			
		||||
		return 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, "")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								nexgb/xgbgen/xgbgen
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								nexgb/xgbgen/xgbgen
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										298
									
								
								nexgb/xgbgen/xml.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								nexgb/xgbgen/xml.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,298 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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 Imports `xml:"import"`
 | 
			
		||||
	Enums Enums `xml:"enum"`
 | 
			
		||||
	Xids Xids `xml:"xidtype"`
 | 
			
		||||
	XidUnions Xids `xml:"xidunion"`
 | 
			
		||||
	TypeDefs TypeDefs `xml:"typedef"`
 | 
			
		||||
	EventCopies EventCopies `xml:"eventcopy"`
 | 
			
		||||
	ErrorCopies ErrorCopies `xml:"errorcopy"`
 | 
			
		||||
 | 
			
		||||
	// Here are the complex ones, i.e., anything with "structure contents"
 | 
			
		||||
	Structs Structs `xml:"struct"`
 | 
			
		||||
	Unions Unions `xml:"union"`
 | 
			
		||||
	Requests Requests `xml:"request"`
 | 
			
		||||
	Events Events `xml:"event"`
 | 
			
		||||
	Errors Errors `xml:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Morph cascades down all of the XML and calls each type's corresponding
 | 
			
		||||
// Morph function with itself as an argument (the context).
 | 
			
		||||
func (x *XML) Morph(c *Context) {
 | 
			
		||||
	// Start the header...
 | 
			
		||||
	c.Putln("package xgb")
 | 
			
		||||
	c.Putln("/*")
 | 
			
		||||
	c.Putln("\tX protocol API for '%s.xml'.", c.xml.Header)
 | 
			
		||||
	c.Putln("\tThis file is automatically generated. Edit at your own peril!")
 | 
			
		||||
	c.Putln("\tGenerated on %s",
 | 
			
		||||
		time.Now().Format("Jan 2, 2006 at 3:04:05pm MST"))
 | 
			
		||||
	c.Putln("*/")
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.Imports.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.Enums.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.Xids.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.XidUnions.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.TypeDefs.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.Structs.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.Unions.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.Requests.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.Events.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.Errors.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.EventCopies.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
 | 
			
		||||
	x.ErrorCopies.Morph(c)
 | 
			
		||||
	c.Putln("")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsResource returns true if the 'needle' type is a resource type.
 | 
			
		||||
// i.e., an "xid"
 | 
			
		||||
func (x *XML) IsResource(needle Type) bool {
 | 
			
		||||
	for _, xid := range x.Xids {
 | 
			
		||||
		if needle == xid.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, xidunion := range x.XidUnions {
 | 
			
		||||
		if needle == xidunion.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, imp := range x.Imports {
 | 
			
		||||
		if imp.xml.IsResource(needle) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasType returns true if the 'needle' type can be found in the protocol
 | 
			
		||||
// description represented by 'x'.
 | 
			
		||||
func (x *XML) HasType(needle Type) bool {
 | 
			
		||||
	for _, enum := range x.Enums {
 | 
			
		||||
		if needle == enum.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, xid := range x.Xids {
 | 
			
		||||
		if needle == xid.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, xidunion := range x.XidUnions {
 | 
			
		||||
		if needle == xidunion.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, typedef := range x.TypeDefs {
 | 
			
		||||
		if needle == typedef.New {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, evcopy := range x.EventCopies {
 | 
			
		||||
		if needle == evcopy.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, errcopy := range x.ErrorCopies {
 | 
			
		||||
		if needle == errcopy.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, strct := range x.Structs {
 | 
			
		||||
		if needle == strct.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, union := range x.Unions {
 | 
			
		||||
		if needle == union.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, ev := range x.Events {
 | 
			
		||||
		if needle == ev.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, err := range x.Errors {
 | 
			
		||||
		if needle == err.Name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Name string
 | 
			
		||||
 | 
			
		||||
type Type string
 | 
			
		||||
 | 
			
		||||
type Imports []*Import
 | 
			
		||||
 | 
			
		||||
func (imports Imports) 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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Import struct {
 | 
			
		||||
	Name string `xml:",chardata"`
 | 
			
		||||
	xml *XML `xml:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Enums []Enum
 | 
			
		||||
 | 
			
		||||
// Eval on the list of all enum types goes through and forces every enum
 | 
			
		||||
// item to have a valid expression.
 | 
			
		||||
// This is necessary because when an item is empty, it is defined to have
 | 
			
		||||
// the value of "one more than that of the previous item, or 0 for the first
 | 
			
		||||
// item".
 | 
			
		||||
func (enums Enums) Eval() {
 | 
			
		||||
	for _, enum := range enums {
 | 
			
		||||
		nextValue := uint(0)
 | 
			
		||||
		for _, item := range enum.Items {
 | 
			
		||||
			if item.Expr == nil {
 | 
			
		||||
				item.Expr = newValueExpression(nextValue)
 | 
			
		||||
				nextValue++
 | 
			
		||||
			} else {
 | 
			
		||||
				nextValue = item.Expr.Eval() + 1
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Enum struct {
 | 
			
		||||
	Name Type `xml:"name,attr"`
 | 
			
		||||
	Items []*EnumItem `xml:"item"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type EnumItem struct {
 | 
			
		||||
	Name Name `xml:"name,attr"`
 | 
			
		||||
	Expr *Expression `xml:",any"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Xids []*Xid
 | 
			
		||||
 | 
			
		||||
type Xid struct {
 | 
			
		||||
	XMLName xml.Name
 | 
			
		||||
	Name Type `xml:"name,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TypeDefs []*TypeDef
 | 
			
		||||
 | 
			
		||||
type TypeDef struct {
 | 
			
		||||
	Old Type `xml:"oldname,attr"`
 | 
			
		||||
	New Type `xml:"newname,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type EventCopies []*EventCopy
 | 
			
		||||
 | 
			
		||||
type EventCopy struct {
 | 
			
		||||
	Name Type `xml:"name,attr"`
 | 
			
		||||
	Number string `xml:"number,attr"`
 | 
			
		||||
	Ref Type `xml:"ref,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ErrorCopies []*ErrorCopy
 | 
			
		||||
 | 
			
		||||
type ErrorCopy struct {
 | 
			
		||||
	Name Type `xml:"name,attr"`
 | 
			
		||||
	Number string `xml:"number,attr"`
 | 
			
		||||
	Ref Type `xml:"ref,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Structs []*Struct
 | 
			
		||||
 | 
			
		||||
type Struct struct {
 | 
			
		||||
	Name Type `xml:"name,attr"`
 | 
			
		||||
	Fields []*Field `xml:",any"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Unions []*Union
 | 
			
		||||
 | 
			
		||||
type Union struct {
 | 
			
		||||
	Name Type `xml:"name,attr"`
 | 
			
		||||
	Fields []*Field `xml:",any"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Requests []*Request
 | 
			
		||||
 | 
			
		||||
type Request struct {
 | 
			
		||||
	Name Type `xml:"name,attr"`
 | 
			
		||||
	Opcode int `xml:"opcode,attr"`
 | 
			
		||||
	Combine bool `xml:"combine-adjacent,attr"`
 | 
			
		||||
	Fields []*Field `xml:",any"`
 | 
			
		||||
	Reply *Reply `xml:"reply"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Reply struct {
 | 
			
		||||
	Fields []*Field `xml:",any"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Events []*Event
 | 
			
		||||
 | 
			
		||||
type Event struct {
 | 
			
		||||
	Name Type `xml:"name,attr"`
 | 
			
		||||
	Number int `xml:"number,attr"`
 | 
			
		||||
	NoSequence bool `xml:"no-sequence-number,true"`
 | 
			
		||||
	Fields []*Field `xml:",any"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Errors []*Error
 | 
			
		||||
 | 
			
		||||
type Error struct {
 | 
			
		||||
	Name Type `xml:"name,attr"`
 | 
			
		||||
	Number int `xml:"number,attr"`
 | 
			
		||||
	Fields []*Field `xml:",any"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										160
									
								
								nexgb/xgbgen/xml_expression.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								nexgb/xgbgen/xml_expression.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Expression struct {
 | 
			
		||||
	XMLName xml.Name
 | 
			
		||||
 | 
			
		||||
	Exprs []*Expression `xml:",any"`
 | 
			
		||||
 | 
			
		||||
	Data string `xml:",chardata"`
 | 
			
		||||
	Op string `xml:"op,attr"`
 | 
			
		||||
	Ref string `xml:"ref,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newValueExpression(v uint) *Expression {
 | 
			
		||||
	return &Expression{
 | 
			
		||||
		XMLName: xml.Name{Local: "value"},
 | 
			
		||||
		Data: fmt.Sprintf("%d", v),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String is for debugging. For actual use, please use 'Morph'.
 | 
			
		||||
func (e *Expression) String() string {
 | 
			
		||||
	switch e.XMLName.Local {
 | 
			
		||||
	case "op":
 | 
			
		||||
		return fmt.Sprintf("(%s %s %s)", e.Exprs[0], e.Op, e.Exprs[1])
 | 
			
		||||
	case "unop":
 | 
			
		||||
		return fmt.Sprintf("(%s (%s))", e.Op, e.Exprs[0])
 | 
			
		||||
	case "popcount":
 | 
			
		||||
		return fmt.Sprintf("popcount(%s)", e.Exprs[0])
 | 
			
		||||
	case "fieldref":
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case "value":
 | 
			
		||||
		return fmt.Sprintf("%s", e.Data)
 | 
			
		||||
	case "bit":
 | 
			
		||||
		return fmt.Sprintf("(1 << %s)", e.Data)
 | 
			
		||||
	case "enumref":
 | 
			
		||||
		return fmt.Sprintf("%s%s", e.Ref, e.Data)
 | 
			
		||||
	case "sumof":
 | 
			
		||||
		return fmt.Sprintf("sum(%s)", e.Ref)
 | 
			
		||||
	default:
 | 
			
		||||
		log.Panicf("Unrecognized expression element: %s", e.XMLName.Local)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Eval is used to *attempt* to compute a concrete value for a particular
 | 
			
		||||
// expression. This is used in the initial setup to instantiate values for
 | 
			
		||||
// empty items in enums.
 | 
			
		||||
// We can't compute a concrete value for expressions that rely on a context,
 | 
			
		||||
// i.e., some field value.
 | 
			
		||||
func (e *Expression) Eval() uint {
 | 
			
		||||
	switch e.XMLName.Local {
 | 
			
		||||
	case "op":
 | 
			
		||||
		if len(e.Exprs) != 2 {
 | 
			
		||||
			log.Panicf("'op' found %d expressions; expected 2.", len(e.Exprs))
 | 
			
		||||
		}
 | 
			
		||||
		return e.BinaryOp(e.Exprs[0], e.Exprs[1]).Eval()
 | 
			
		||||
	case "unop":
 | 
			
		||||
		if len(e.Exprs) != 1 {
 | 
			
		||||
			log.Panicf("'unop' found %d expressions; expected 1.", len(e.Exprs))
 | 
			
		||||
		}
 | 
			
		||||
		return e.UnaryOp(e.Exprs[0]).Eval()
 | 
			
		||||
	case "popcount":
 | 
			
		||||
		if len(e.Exprs) != 1 {
 | 
			
		||||
			log.Panicf("'popcount' found %d expressions; expected 1.",
 | 
			
		||||
				len(e.Exprs))
 | 
			
		||||
		}
 | 
			
		||||
		return popCount(e.Exprs[0].Eval())
 | 
			
		||||
	case "value":
 | 
			
		||||
		val, err := strconv.Atoi(e.Data)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Panicf("Could not convert '%s' in 'value' expression to int.",
 | 
			
		||||
				e.Data)
 | 
			
		||||
		}
 | 
			
		||||
		return uint(val)
 | 
			
		||||
	case "bit":
 | 
			
		||||
		bit, err := strconv.Atoi(e.Data)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Panicf("Could not convert '%s' in 'bit' expression to int.",
 | 
			
		||||
				e.Data)
 | 
			
		||||
		}
 | 
			
		||||
		if bit < 0 || bit > 31 {
 | 
			
		||||
			log.Panicf("A 'bit' literal must be in the range [0, 31], but " +
 | 
			
		||||
				" is %d", bit)
 | 
			
		||||
		}
 | 
			
		||||
		return 1 << uint(bit)
 | 
			
		||||
	case "fieldref":
 | 
			
		||||
		log.Panicf("Cannot compute concrete value of 'fieldref' in " +
 | 
			
		||||
			"expression '%s'.", e)
 | 
			
		||||
	case "enumref":
 | 
			
		||||
		log.Panicf("Cannot compute concrete value of 'enumref' in " +
 | 
			
		||||
			"expression '%s'.", e)
 | 
			
		||||
	case "sumof":
 | 
			
		||||
		log.Panicf("Cannot compute concrete value of 'sumof' in " +
 | 
			
		||||
			"expression '%s'.", e)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Panicf("Unrecognized tag '%s' in expression context. Expected one of " +
 | 
			
		||||
		"op, fieldref, value, bit, enumref, unop, sumof or popcount.",
 | 
			
		||||
		e.XMLName.Local)
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Expression) BinaryOp(operand1, operand2 *Expression) *Expression {
 | 
			
		||||
	if e.XMLName.Local != "op" {
 | 
			
		||||
		log.Panicf("Cannot perform binary operation on non-op expression: %s",
 | 
			
		||||
			e.XMLName.Local)
 | 
			
		||||
	}
 | 
			
		||||
	if len(e.Op) == 0 {
 | 
			
		||||
		log.Panicf("Cannot perform binary operation without operator for: %s",
 | 
			
		||||
			e.XMLName.Local)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wrap := newValueExpression
 | 
			
		||||
	switch e.Op {
 | 
			
		||||
	case "+":
 | 
			
		||||
		return wrap(operand1.Eval() + operand2.Eval())
 | 
			
		||||
	case "-":
 | 
			
		||||
		return wrap(operand1.Eval() + operand2.Eval())
 | 
			
		||||
	case "*":
 | 
			
		||||
		return wrap(operand1.Eval() * operand2.Eval())
 | 
			
		||||
	case "/":
 | 
			
		||||
		return wrap(operand1.Eval() / operand2.Eval())
 | 
			
		||||
	case "&":
 | 
			
		||||
		return wrap(operand1.Eval() & operand2.Eval())
 | 
			
		||||
	case "<<":
 | 
			
		||||
		return wrap(operand1.Eval() << operand2.Eval())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Panicf("Invalid binary operator '%s' for '%s' expression.",
 | 
			
		||||
		e.Op, e.XMLName.Local)
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Expression) UnaryOp(operand *Expression) *Expression {
 | 
			
		||||
	if e.XMLName.Local != "unop" {
 | 
			
		||||
		log.Panicf("Cannot perform unary operation on non-unop expression: %s",
 | 
			
		||||
			e.XMLName.Local)
 | 
			
		||||
	}
 | 
			
		||||
	if len(e.Op) == 0 {
 | 
			
		||||
		log.Panicf("Cannot perform unary operation without operator for: %s",
 | 
			
		||||
			e.XMLName.Local)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch e.Op {
 | 
			
		||||
	case "~":
 | 
			
		||||
		return newValueExpression(^operand.Eval())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Panicf("Invalid unary operator '%s' for '%s' expression.",
 | 
			
		||||
		e.Op, e.XMLName.Local)
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										147
									
								
								nexgb/xgbgen/xml_fields.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								nexgb/xgbgen/xml_fields.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
			
		||||
package main
 | 
			
		||||
/*
 | 
			
		||||
	A series of fields should be taken as "structure contents", and *not*
 | 
			
		||||
	just the single 'field' elements. Namely, 'fields' subsumes 'field'
 | 
			
		||||
	elements.
 | 
			
		||||
 | 
			
		||||
	More particularly, 'fields' corresponds to list, in order, of any of the
 | 
			
		||||
	follow elements: pad, field, list, localfield, exprfield, valueparm
 | 
			
		||||
	and switch.
 | 
			
		||||
 | 
			
		||||
	Thus, the 'Field' type must contain the union of information corresponding
 | 
			
		||||
	to all aforementioned fields.
 | 
			
		||||
 | 
			
		||||
	This would ideally be a better job for interfaces, but I could not figure
 | 
			
		||||
	out how to make them jive with Go's XML package. (And I don't really feel
 | 
			
		||||
	up to type translation.)
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Field struct {
 | 
			
		||||
	XMLName xml.Name
 | 
			
		||||
 | 
			
		||||
	// For 'pad' element
 | 
			
		||||
	Bytes int `xml:"bytes,attr"`
 | 
			
		||||
 | 
			
		||||
	// For 'field', 'list', 'localfield', 'exprfield' and 'switch' elements.
 | 
			
		||||
	Name string `xml:"name,attr"`
 | 
			
		||||
 | 
			
		||||
	// For 'field', 'list', 'localfield', and 'exprfield' elements.
 | 
			
		||||
	Type Type `xml:"type,attr"`
 | 
			
		||||
 | 
			
		||||
	// For 'list', 'exprfield' and 'switch' elements.
 | 
			
		||||
	Expr *Expression `xml:",any"`
 | 
			
		||||
 | 
			
		||||
	// For 'valueparm' element.
 | 
			
		||||
	ValueMaskType Type `xml:"value-mask-type,attr"`
 | 
			
		||||
	ValueMaskName string `xml:"value-mask-name,attr"`
 | 
			
		||||
	ValueListName string `xml:"value-list-name,attr"`
 | 
			
		||||
 | 
			
		||||
	// For 'switch' element.
 | 
			
		||||
	Bitcases []*Bitcase `xml:"bitcase"`
 | 
			
		||||
 | 
			
		||||
	// I don't know which elements these are for. The documentation is vague.
 | 
			
		||||
	// They also seem to be completely optional.
 | 
			
		||||
	OptEnum Type `xml:"enum,attr"`
 | 
			
		||||
	OptMask Type `xml:"mask,attr"`
 | 
			
		||||
	OptAltEnum Type `xml:"altenum,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String is for debugging purposes.
 | 
			
		||||
func (f *Field) String() string {
 | 
			
		||||
	switch f.XMLName.Local {
 | 
			
		||||
	case "pad":
 | 
			
		||||
		return fmt.Sprintf("pad (%d bytes)", f.Bytes)
 | 
			
		||||
	case "field":
 | 
			
		||||
		return fmt.Sprintf("field (type = '%s', name = '%s')", f.Type, f.Name)
 | 
			
		||||
	case "list":
 | 
			
		||||
		return fmt.Sprintf("list (type = '%s', name = '%s', length = '%s')",
 | 
			
		||||
			f.Type, f.Name, f.Expr)
 | 
			
		||||
	case "localfield":
 | 
			
		||||
		return fmt.Sprintf("localfield (type = '%s', name = '%s')",
 | 
			
		||||
			f.Type, f.Name)
 | 
			
		||||
	case "exprfield":
 | 
			
		||||
		return fmt.Sprintf("exprfield (type = '%s', name = '%s', expr = '%s')",
 | 
			
		||||
			f.Type, f.Name, f.Expr)
 | 
			
		||||
	case "valueparam":
 | 
			
		||||
		return fmt.Sprintf("valueparam (type = '%s', name = '%s', list = '%s')",
 | 
			
		||||
			f.ValueMaskType, f.ValueMaskName, f.ValueListName)
 | 
			
		||||
	case "switch":
 | 
			
		||||
		bitcases := make([]string, len(f.Bitcases))
 | 
			
		||||
		for i, bitcase := range f.Bitcases {
 | 
			
		||||
			bitcases[i] = bitcase.StringPrefix("\t")
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Sprintf("switch (name = '%s', expr = '%s')\n\t%s",
 | 
			
		||||
			f.Name, f.Expr, strings.Join(bitcases, "\n\t"))
 | 
			
		||||
	default:
 | 
			
		||||
		log.Panicf("Unrecognized field element: %s", f.XMLName.Local)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 Bitcase struct {
 | 
			
		||||
	Fields []*Field `xml:",any"`
 | 
			
		||||
 | 
			
		||||
	// All the different expressions.
 | 
			
		||||
	// When it comes time to choose one, use the 'Expr' method.
 | 
			
		||||
	ExprOp *Expression `xml:"op"`
 | 
			
		||||
	ExprUnOp *Expression `xml:"unop"`
 | 
			
		||||
	ExprField *Expression `xml:"fieldref"`
 | 
			
		||||
	ExprValue *Expression `xml:"value"`
 | 
			
		||||
	ExprBit *Expression `xml:"bit"`
 | 
			
		||||
	ExprEnum *Expression `xml:"enumref"`
 | 
			
		||||
	ExprSum *Expression `xml:"sumof"`
 | 
			
		||||
	ExprPop *Expression `xml:"popcount"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StringPrefix is for debugging purposes only.
 | 
			
		||||
// StringPrefix takes a string to prefix to every extra line for formatting.
 | 
			
		||||
func (b *Bitcase) StringPrefix(prefix string) string {
 | 
			
		||||
	fields := make([]string, len(b.Fields))
 | 
			
		||||
	for i, field := range b.Fields {
 | 
			
		||||
		fields[i] = fmt.Sprintf("%s%s", prefix, field)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s\n\t%s%s", b.Expr(), prefix,
 | 
			
		||||
		strings.Join(fields, "\n\t"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Expr chooses the only non-nil Expr* field from Bitcase.
 | 
			
		||||
// Panic if there is more than one non-nil expression.
 | 
			
		||||
func (b *Bitcase) Expr() *Expression {
 | 
			
		||||
	choices := []*Expression{
 | 
			
		||||
		b.ExprOp, b.ExprUnOp, b.ExprField, b.ExprValue,
 | 
			
		||||
		b.ExprBit, b.ExprEnum, b.ExprSum, b.ExprPop,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var choice *Expression = 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
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user