Merge aarzilli/xgb, branch xcb1.12 as nexgb
History has been linearized and rewritten to stay under the new subdirectory. I want to make changes incompatible to BurntSushi/xgb. The history begs for being thrown away entirely because of its quality and because it doesn't cover the Google period but it is still useful for copyright tracking.
This commit is contained in:
commit
3173202cc1
|
@ -0,0 +1,2 @@
|
||||||
|
xgbgen/xgbgen
|
||||||
|
.*.swp
|
|
@ -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>
|
|
@ -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>
|
|
@ -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.
|
|
@ -0,0 +1,80 @@
|
||||||
|
# This Makefile is used by the developer. It is not needed in any way to build
|
||||||
|
# a checkout of the XGB repository.
|
||||||
|
# It will be useful, however, if you are hacking at the code generator.
|
||||||
|
# i.e., after making a change to the code generator, run 'make' in the
|
||||||
|
# xgb directory. This will build xgbgen and regenerate each sub-package.
|
||||||
|
# 'make test' will then run any appropriate tests (just tests xproto right now).
|
||||||
|
# 'make bench' will test a couple of benchmarks.
|
||||||
|
# 'make build-all' will then try to build each extension. This isn't strictly
|
||||||
|
# necessary, but it's a good idea to make sure each sub-package is a valid
|
||||||
|
# Go package.
|
||||||
|
|
||||||
|
# My path to the X protocol XML descriptions.
|
||||||
|
ifndef XPROTO
|
||||||
|
XPROTO=/usr/share/xcb
|
||||||
|
endif
|
||||||
|
|
||||||
|
# All of the XML files in my /usr/share/xcb directory EXCEPT XKB. -_-
|
||||||
|
# This is intended to build xgbgen and generate Go code for each supported
|
||||||
|
# extension.
|
||||||
|
all: build-xgbgen \
|
||||||
|
bigreq.xml composite.xml damage.xml dpms.xml dri2.xml \
|
||||||
|
ge.xml glx.xml randr.xml record.xml render.xml res.xml \
|
||||||
|
screensaver.xml shape.xml shm.xml xc_misc.xml \
|
||||||
|
xevie.xml xf86dri.xml xf86vidmode.xml xfixes.xml xinerama.xml \
|
||||||
|
xprint.xml xproto.xml xselinux.xml xtest.xml \
|
||||||
|
xvmc.xml xv.xml
|
||||||
|
|
||||||
|
build-xgbgen:
|
||||||
|
(cd xgbgen && go build)
|
||||||
|
|
||||||
|
# Builds each individual sub-package to make sure its valid Go code.
|
||||||
|
build-all: bigreq.b composite.b damage.b dpms.b dri2.b ge.b glx.b randr.b \
|
||||||
|
record.b render.b res.b screensaver.b shape.b shm.b xcmisc.b \
|
||||||
|
xevie.b xf86dri.b xf86vidmode.b xfixes.b xinerama.b \
|
||||||
|
xprint.b xproto.b xselinux.b xtest.b xv.b xvmc.b
|
||||||
|
|
||||||
|
%.b:
|
||||||
|
(cd $* ; go build)
|
||||||
|
|
||||||
|
# Installs each individual sub-package.
|
||||||
|
install: bigreq.i composite.i damage.i dpms.i dri2.i ge.i glx.i randr.i \
|
||||||
|
record.i render.i res.i screensaver.i shape.i shm.i xcmisc.i \
|
||||||
|
xevie.i xf86dri.i xf86vidmode.i xfixes.i xinerama.i \
|
||||||
|
xprint.i xproto.i xselinux.i xtest.i xv.i xvmc.i
|
||||||
|
go install
|
||||||
|
|
||||||
|
%.i:
|
||||||
|
(cd $* ; go install)
|
||||||
|
|
||||||
|
# xc_misc is special because it has an underscore.
|
||||||
|
# There's probably a way to do this better, but Makefiles aren't my strong suit.
|
||||||
|
xc_misc.xml: build-xgbgen
|
||||||
|
mkdir -p xcmisc
|
||||||
|
xgbgen/xgbgen --proto-path $(XPROTO) $(XPROTO)/xc_misc.xml > xcmisc/xcmisc.go
|
||||||
|
|
||||||
|
%.xml: build-xgbgen
|
||||||
|
mkdir -p $*
|
||||||
|
xgbgen/xgbgen --proto-path $(XPROTO) $(XPROTO)/$*.xml > $*/$*.go
|
||||||
|
|
||||||
|
# Just test the xproto core protocol for now.
|
||||||
|
test:
|
||||||
|
(cd xproto ; go test)
|
||||||
|
|
||||||
|
# Force all xproto benchmarks to run and no tests.
|
||||||
|
bench:
|
||||||
|
(cd xproto ; go test -run 'nomatch' -bench '.*' -cpu 1,2,3,6)
|
||||||
|
|
||||||
|
# gofmt all non-auto-generated code.
|
||||||
|
# (auto-generated code is already gofmt'd.)
|
||||||
|
# Also do a column check (80 cols) after a gofmt.
|
||||||
|
# But don't check columns on auto-generated code, since I don't care if they
|
||||||
|
# break 80 cols.
|
||||||
|
gofmt:
|
||||||
|
gofmt -w *.go xgbgen/*.go examples/*.go examples/*/*.go xproto/xproto_test.go
|
||||||
|
colcheck *.go xgbgen/*.go examples/*.go examples/*/*.go xproto/xproto_test.go
|
||||||
|
|
||||||
|
push:
|
||||||
|
git push origin master
|
||||||
|
git push github master
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
XGB is the X Go Binding, which is a low-level API to communicate with the
|
||||||
|
core X protocol and many of the X extensions. It is closely modeled after
|
||||||
|
XCB and xpyb.
|
||||||
|
|
||||||
|
It is thread safe and gets immediate improvement from parallelism when
|
||||||
|
GOMAXPROCS > 1. (See the benchmarks in xproto/xproto_test.go for evidence.)
|
||||||
|
|
||||||
|
Please see doc.go for more info.
|
||||||
|
|
||||||
|
Note that unless you know you need XGB, you can probably make your life
|
||||||
|
easier by using a slightly higher level library: xgbutil.
|
||||||
|
|
||||||
|
Quick Usage
|
||||||
|
===========
|
||||||
|
go get github.com/BurntSushi/xgb
|
||||||
|
go run go/path/src/github.com/BurntSushi/xgb/examples/create-window/main.go
|
||||||
|
|
||||||
|
BurntSushi's Fork
|
||||||
|
=================
|
||||||
|
I've forked the XGB repository from Google Code due to inactivty upstream.
|
||||||
|
|
||||||
|
Godoc documentation can be found here:
|
||||||
|
http://godoc.burntsushi.net/pkg/github.com/BurntSushi/xgb/
|
||||||
|
|
||||||
|
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. The rest of XGB has been completely rewritten.
|
||||||
|
|
||||||
|
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. To be fair, it is
|
||||||
|
at least as complex as XGB itself. *sigh*
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
I like to keep all my code to 80 columns or less. I have plenty of screen real
|
||||||
|
estate, but enjoy 80 columns so that I can have multiple code windows open side
|
||||||
|
to side and not be plagued by the ugly auto-wrapping of a text editor.
|
||||||
|
|
||||||
|
If you don't oblige me, I will fix any patch you submit to abide 80 columns.
|
||||||
|
|
||||||
|
Note that this style restriction does not preclude gofmt, but introduces a few
|
||||||
|
peculiarities. The first is that gofmt will occasionally add spacing (typically
|
||||||
|
to comments) that ends up going over 80 columns. Either shorten the comment or
|
||||||
|
put it on its own line.
|
||||||
|
|
||||||
|
The second and more common hiccup is when a function definition extends beyond
|
||||||
|
80 columns. If one adds line breaks to keep it below 80 columns, gofmt will
|
||||||
|
indent all subsequent lines in a function definition to the same indentation
|
||||||
|
level of the function body. This results in a less-than-ideal separation
|
||||||
|
between function definition and function body. To remedy this, simply add a
|
||||||
|
line break like so:
|
||||||
|
|
||||||
|
func RestackWindowExtra(xu *xgbutil.XUtil, win xproto.Window, stackMode int,
|
||||||
|
sibling xproto.Window, source int) error {
|
||||||
|
|
||||||
|
return ClientEvent(xu, win, "_NET_RESTACK_WINDOW", source, int(sibling),
|
||||||
|
stackMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
Something similar should also be applied to long 'if' or 'for' conditionals,
|
||||||
|
although it would probably be preferrable to break up the conditional to
|
||||||
|
smaller chunks with a few helper variables.
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
package xgb
|
||||||
|
|
||||||
|
/*
|
||||||
|
auth.go contains functions to facilitate the parsing of .Xauthority files.
|
||||||
|
|
||||||
|
It is largely unmodified from the original XGB package that I forked.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// readAuthority reads the X authority file for the DISPLAY.
|
||||||
|
// If hostname == "" or hostname == "localhost",
|
||||||
|
// then use 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).
|
||||||
|
b := make([]byte, 256)
|
||||||
|
|
||||||
|
// As per /usr/include/X11/Xauth.h.
|
||||||
|
const familyLocal = 256
|
||||||
|
const familyWild = 65535
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
for {
|
||||||
|
var family uint16
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &family); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := getString(r, b)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
disp, err := getString(r, b)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name0, err := getString(r, b)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data0, err := getBytes(r, b)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addrmatch := (family == familyWild) ||
|
||||||
|
(family == familyLocal && addr == hostname)
|
||||||
|
dispmatch := (disp == "") || (disp == display)
|
||||||
|
|
||||||
|
if addrmatch && dispmatch {
|
||||||
|
return name0, data0, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBytes(r io.Reader, b []byte) ([]byte, error) {
|
||||||
|
var n uint16
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &n); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if n > uint16(len(b)) {
|
||||||
|
return nil, errors.New("bytes too long for buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(r, b[0:n]); 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
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
// Package bigreq is the X client API for the BIG-REQUESTS extension.
|
||||||
|
package bigreq
|
||||||
|
|
||||||
|
// This file is automatically generated from bigreq.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the BIG-REQUESTS extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 12, "BIG-REQUESTS").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named BIG-REQUESTS could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["BIG-REQUESTS"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["BIG-REQUESTS"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["BIG-REQUESTS"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["BIG-REQUESTS"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["BIG-REQUESTS"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// EnableCookie is a cookie used only for Enable requests.
|
||||||
|
type EnableCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling EnableCookie.Reply()
|
||||||
|
func Enable(c *xgb.Conn) EnableCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["BIG-REQUESTS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Enable' using the uninitialized extension 'BIG-REQUESTS'. bigreq.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(enableRequest(c), cookie)
|
||||||
|
return EnableCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func EnableUnchecked(c *xgb.Conn) EnableCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["BIG-REQUESTS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Enable' using the uninitialized extension 'BIG-REQUESTS'. bigreq.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(enableRequest(c), cookie)
|
||||||
|
return EnableCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableReply represents the data returned from a Enable request.
|
||||||
|
type EnableReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
MaximumRequestLength uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a Enable request.
|
||||||
|
func (cook EnableCookie) Reply() (*EnableReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return enableReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableReply reads a byte slice into a EnableReply value.
|
||||||
|
func enableReply(buf []byte) *EnableReply {
|
||||||
|
v := new(EnableReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MaximumRequestLength = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Enable
|
||||||
|
// enableRequest writes a Enable request to a byte slice.
|
||||||
|
func enableRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["BIG-REQUESTS"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
|
@ -0,0 +1,721 @@
|
||||||
|
// Package composite is the X client API for the Composite extension.
|
||||||
|
package composite
|
||||||
|
|
||||||
|
// This file is automatically generated from composite.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xfixes"
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the Composite extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 9, "Composite").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named Composite could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["Composite"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["Composite"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["Composite"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["Composite"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["Composite"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
RedirectAutomatic = 0
|
||||||
|
RedirectManual = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// CreateRegionFromBorderClipCookie is a cookie used only for CreateRegionFromBorderClip requests.
|
||||||
|
type CreateRegionFromBorderClipCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRegionFromBorderClip sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func CreateRegionFromBorderClip(c *xgb.Conn, Region xfixes.Region, Window xproto.Window) CreateRegionFromBorderClipCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'CreateRegionFromBorderClip' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(createRegionFromBorderClipRequest(c, Region, Window), cookie)
|
||||||
|
return CreateRegionFromBorderClipCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRegionFromBorderClipChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using CreateRegionFromBorderClipCookie.Check()
|
||||||
|
func CreateRegionFromBorderClipChecked(c *xgb.Conn, Region xfixes.Region, Window xproto.Window) CreateRegionFromBorderClipCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'CreateRegionFromBorderClip' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(createRegionFromBorderClipRequest(c, Region, Window), cookie)
|
||||||
|
return CreateRegionFromBorderClipCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook CreateRegionFromBorderClipCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for CreateRegionFromBorderClip
|
||||||
|
// createRegionFromBorderClipRequest writes a CreateRegionFromBorderClip request to a byte slice.
|
||||||
|
func createRegionFromBorderClipRequest(c *xgb.Conn, Region xfixes.Region, Window xproto.Window) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Composite"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 5 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Region))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOverlayWindowCookie is a cookie used only for GetOverlayWindow requests.
|
||||||
|
type GetOverlayWindowCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOverlayWindow sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetOverlayWindowCookie.Reply()
|
||||||
|
func GetOverlayWindow(c *xgb.Conn, Window xproto.Window) GetOverlayWindowCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetOverlayWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getOverlayWindowRequest(c, Window), cookie)
|
||||||
|
return GetOverlayWindowCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOverlayWindowUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetOverlayWindowUnchecked(c *xgb.Conn, Window xproto.Window) GetOverlayWindowCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetOverlayWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getOverlayWindowRequest(c, Window), cookie)
|
||||||
|
return GetOverlayWindowCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOverlayWindowReply represents the data returned from a GetOverlayWindow request.
|
||||||
|
type GetOverlayWindowReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
OverlayWin xproto.Window
|
||||||
|
// padding: 20 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetOverlayWindow request.
|
||||||
|
func (cook GetOverlayWindowCookie) Reply() (*GetOverlayWindowReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getOverlayWindowReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOverlayWindowReply reads a byte slice into a GetOverlayWindowReply value.
|
||||||
|
func getOverlayWindowReply(buf []byte) *GetOverlayWindowReply {
|
||||||
|
v := new(GetOverlayWindowReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.OverlayWin = xproto.Window(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 20 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetOverlayWindow
|
||||||
|
// getOverlayWindowRequest writes a GetOverlayWindow request to a byte slice.
|
||||||
|
func getOverlayWindowRequest(c *xgb.Conn, Window xproto.Window) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Composite"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 7 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameWindowPixmapCookie is a cookie used only for NameWindowPixmap requests.
|
||||||
|
type NameWindowPixmapCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameWindowPixmap sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func NameWindowPixmap(c *xgb.Conn, Window xproto.Window, Pixmap xproto.Pixmap) NameWindowPixmapCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'NameWindowPixmap' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(nameWindowPixmapRequest(c, Window, Pixmap), cookie)
|
||||||
|
return NameWindowPixmapCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameWindowPixmapChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using NameWindowPixmapCookie.Check()
|
||||||
|
func NameWindowPixmapChecked(c *xgb.Conn, Window xproto.Window, Pixmap xproto.Pixmap) NameWindowPixmapCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'NameWindowPixmap' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(nameWindowPixmapRequest(c, Window, Pixmap), cookie)
|
||||||
|
return NameWindowPixmapCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook NameWindowPixmapCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for NameWindowPixmap
|
||||||
|
// nameWindowPixmapRequest writes a NameWindowPixmap request to a byte slice.
|
||||||
|
func nameWindowPixmapRequest(c *xgb.Conn, Window xproto.Window, Pixmap xproto.Pixmap) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Composite"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 6 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Pixmap))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionCookie is a cookie used only for QueryVersion requests.
|
||||||
|
type QueryVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
|
||||||
|
func QueryVersion(c *xgb.Conn, ClientMajorVersion uint32, ClientMinorVersion uint32) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func QueryVersionUnchecked(c *xgb.Conn, ClientMajorVersion uint32, ClientMinorVersion uint32) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionReply represents the data returned from a QueryVersion request.
|
||||||
|
type QueryVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
MajorVersion uint32
|
||||||
|
MinorVersion uint32
|
||||||
|
// padding: 16 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a QueryVersion request.
|
||||||
|
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return queryVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionReply reads a byte slice into a QueryVersionReply value.
|
||||||
|
func queryVersionReply(buf []byte) *QueryVersionReply {
|
||||||
|
v := new(QueryVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MajorVersion = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MinorVersion = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 16 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for QueryVersion
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion uint32, ClientMinorVersion uint32) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Composite"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], ClientMajorVersion)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], ClientMinorVersion)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectSubwindowsCookie is a cookie used only for RedirectSubwindows requests.
|
||||||
|
type RedirectSubwindowsCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectSubwindows sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func RedirectSubwindows(c *xgb.Conn, Window xproto.Window, Update byte) RedirectSubwindowsCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'RedirectSubwindows' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(redirectSubwindowsRequest(c, Window, Update), cookie)
|
||||||
|
return RedirectSubwindowsCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectSubwindowsChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using RedirectSubwindowsCookie.Check()
|
||||||
|
func RedirectSubwindowsChecked(c *xgb.Conn, Window xproto.Window, Update byte) RedirectSubwindowsCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'RedirectSubwindows' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(redirectSubwindowsRequest(c, Window, Update), cookie)
|
||||||
|
return RedirectSubwindowsCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook RedirectSubwindowsCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for RedirectSubwindows
|
||||||
|
// redirectSubwindowsRequest writes a RedirectSubwindows request to a byte slice.
|
||||||
|
func redirectSubwindowsRequest(c *xgb.Conn, Window xproto.Window, Update byte) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Composite"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 2 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
buf[b] = Update
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectWindowCookie is a cookie used only for RedirectWindow requests.
|
||||||
|
type RedirectWindowCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectWindow sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func RedirectWindow(c *xgb.Conn, Window xproto.Window, Update byte) RedirectWindowCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'RedirectWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(redirectWindowRequest(c, Window, Update), cookie)
|
||||||
|
return RedirectWindowCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectWindowChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using RedirectWindowCookie.Check()
|
||||||
|
func RedirectWindowChecked(c *xgb.Conn, Window xproto.Window, Update byte) RedirectWindowCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'RedirectWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(redirectWindowRequest(c, Window, Update), cookie)
|
||||||
|
return RedirectWindowCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook RedirectWindowCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for RedirectWindow
|
||||||
|
// redirectWindowRequest writes a RedirectWindow request to a byte slice.
|
||||||
|
func redirectWindowRequest(c *xgb.Conn, Window xproto.Window, Update byte) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Composite"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 1 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
buf[b] = Update
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseOverlayWindowCookie is a cookie used only for ReleaseOverlayWindow requests.
|
||||||
|
type ReleaseOverlayWindowCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseOverlayWindow sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func ReleaseOverlayWindow(c *xgb.Conn, Window xproto.Window) ReleaseOverlayWindowCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'ReleaseOverlayWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(releaseOverlayWindowRequest(c, Window), cookie)
|
||||||
|
return ReleaseOverlayWindowCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseOverlayWindowChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using ReleaseOverlayWindowCookie.Check()
|
||||||
|
func ReleaseOverlayWindowChecked(c *xgb.Conn, Window xproto.Window) ReleaseOverlayWindowCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'ReleaseOverlayWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(releaseOverlayWindowRequest(c, Window), cookie)
|
||||||
|
return ReleaseOverlayWindowCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook ReleaseOverlayWindowCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for ReleaseOverlayWindow
|
||||||
|
// releaseOverlayWindowRequest writes a ReleaseOverlayWindow request to a byte slice.
|
||||||
|
func releaseOverlayWindowRequest(c *xgb.Conn, Window xproto.Window) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Composite"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 8 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnredirectSubwindowsCookie is a cookie used only for UnredirectSubwindows requests.
|
||||||
|
type UnredirectSubwindowsCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnredirectSubwindows sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func UnredirectSubwindows(c *xgb.Conn, Window xproto.Window, Update byte) UnredirectSubwindowsCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'UnredirectSubwindows' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(unredirectSubwindowsRequest(c, Window, Update), cookie)
|
||||||
|
return UnredirectSubwindowsCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnredirectSubwindowsChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using UnredirectSubwindowsCookie.Check()
|
||||||
|
func UnredirectSubwindowsChecked(c *xgb.Conn, Window xproto.Window, Update byte) UnredirectSubwindowsCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'UnredirectSubwindows' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(unredirectSubwindowsRequest(c, Window, Update), cookie)
|
||||||
|
return UnredirectSubwindowsCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook UnredirectSubwindowsCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for UnredirectSubwindows
|
||||||
|
// unredirectSubwindowsRequest writes a UnredirectSubwindows request to a byte slice.
|
||||||
|
func unredirectSubwindowsRequest(c *xgb.Conn, Window xproto.Window, Update byte) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Composite"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 4 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
buf[b] = Update
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnredirectWindowCookie is a cookie used only for UnredirectWindow requests.
|
||||||
|
type UnredirectWindowCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnredirectWindow sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func UnredirectWindow(c *xgb.Conn, Window xproto.Window, Update byte) UnredirectWindowCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'UnredirectWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(unredirectWindowRequest(c, Window, Update), cookie)
|
||||||
|
return UnredirectWindowCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnredirectWindowChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using UnredirectWindowCookie.Check()
|
||||||
|
func UnredirectWindowChecked(c *xgb.Conn, Window xproto.Window, Update byte) UnredirectWindowCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Composite"]; !ok {
|
||||||
|
panic("Cannot issue request 'UnredirectWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(unredirectWindowRequest(c, Window, Update), cookie)
|
||||||
|
return UnredirectWindowCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook UnredirectWindowCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for UnredirectWindow
|
||||||
|
// unredirectWindowRequest writes a UnredirectWindow request to a byte slice.
|
||||||
|
func unredirectWindowRequest(c *xgb.Conn, Window xproto.Window, Update byte) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Composite"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 3 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
buf[b] = Update
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
package xgb
|
||||||
|
|
||||||
|
/*
|
||||||
|
conn.go contains a couple of functions that do some real dirty work related
|
||||||
|
to the initial connection handshake with X.
|
||||||
|
|
||||||
|
This code is largely unmodified from the original XGB package that I forked.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// connect connects to the X server given in the 'display' string,
|
||||||
|
// and does all the necessary setup handshaking.
|
||||||
|
// If 'display' is empty it will be taken from os.Getenv("DISPLAY").
|
||||||
|
// Note that you should read and understand the "Connection Setup" of the
|
||||||
|
// X Protocol Reference Manual before changing this function:
|
||||||
|
// http://goo.gl/4zGQg
|
||||||
|
func (c *Conn) connect(display string) error {
|
||||||
|
err := c.dial(display)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.postConnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect init from to the net.Conn,
|
||||||
|
func (c *Conn) connectNet(netConn net.Conn) error {
|
||||||
|
c.conn = netConn
|
||||||
|
return c.postConnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the postConnect action after Conn get it's underly net.Conn
|
||||||
|
func (c *Conn) postConnect() error {
|
||||||
|
// Get authentication data
|
||||||
|
authName, authData, err := readAuthority(c.host, c.display)
|
||||||
|
noauth := false
|
||||||
|
if err != nil {
|
||||||
|
Logger.Printf("Could not get authority info: %v", err)
|
||||||
|
Logger.Println("Trying connection without authority info...")
|
||||||
|
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 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 err
|
||||||
|
}
|
||||||
|
|
||||||
|
head := make([]byte, 8)
|
||||||
|
if _, err = io.ReadFull(c.conn, head[0:8]); err != nil {
|
||||||
|
return 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 fmt.Errorf("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 err
|
||||||
|
}
|
||||||
|
|
||||||
|
if code == 0 {
|
||||||
|
reason := buf[8 : 8+reasonLen]
|
||||||
|
return fmt.Errorf("x protocol authentication refused: %s",
|
||||||
|
string(reason))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unfortunately, it isn't really feasible to read the setup bytes here,
|
||||||
|
// since the code to do so is in a different package.
|
||||||
|
// Users must call 'xproto.Setup(X)' to get the setup info.
|
||||||
|
c.SetupBytes = buf
|
||||||
|
|
||||||
|
// But also read stuff that we *need* to get started.
|
||||||
|
c.setupResourceIdBase = Get32(buf[12:])
|
||||||
|
c.setupResourceIdMask = Get32(buf[16:])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dial initializes the actual net connection with X.
|
||||||
|
func (c *Conn) dial(display string) error {
|
||||||
|
if len(display) == 0 {
|
||||||
|
display = os.Getenv("DISPLAY")
|
||||||
|
}
|
||||||
|
|
||||||
|
display0 := display
|
||||||
|
if len(display) == 0 {
|
||||||
|
return errors.New("empty display string")
|
||||||
|
}
|
||||||
|
|
||||||
|
colonIdx := strings.LastIndex(display, ":")
|
||||||
|
if colonIdx < 0 {
|
||||||
|
return errors.New("bad display string: " + display0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var protocol, socket string
|
||||||
|
|
||||||
|
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 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:]
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
c.DisplayNumber, err = strconv.Atoi(c.display)
|
||||||
|
if err != nil || c.DisplayNumber < 0 {
|
||||||
|
return errors.New("bad display string: " + display0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(scr) != 0 {
|
||||||
|
c.DefaultScreen, err = strconv.Atoi(scr)
|
||||||
|
if err != nil {
|
||||||
|
return 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+c.DisplayNumber))
|
||||||
|
} else {
|
||||||
|
c.conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+c.display)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("cannot connect to " + display0 + ": " + err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
package xgb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cookie is the internal representation of a cookie, where one is generated
|
||||||
|
// for *every* request sent by XGB.
|
||||||
|
// 'cookie' is most frequently used by embedding it into a more specific
|
||||||
|
// kind of cookie, i.e., 'GetInputFocusCookie'.
|
||||||
|
type Cookie struct {
|
||||||
|
conn *Conn
|
||||||
|
Sequence uint16
|
||||||
|
replyChan chan []byte
|
||||||
|
errorChan chan error
|
||||||
|
pingChan chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCookie creates a new cookie with the correct channels initialized
|
||||||
|
// depending upon the values of 'checked' and 'reply'. Together, there are
|
||||||
|
// four different kinds of cookies. (See more detailed comments in the
|
||||||
|
// function for more info on those.)
|
||||||
|
// Note that a sequence number is not set until just before the request
|
||||||
|
// corresponding to this cookie is sent over the wire.
|
||||||
|
//
|
||||||
|
// Unless you're building requests from bytes by hand, this method should
|
||||||
|
// not be used.
|
||||||
|
func (c *Conn) NewCookie(checked, reply bool) *Cookie {
|
||||||
|
cookie := &Cookie{
|
||||||
|
conn: c,
|
||||||
|
Sequence: 0, // we add the sequence id just before sending a request
|
||||||
|
replyChan: nil,
|
||||||
|
errorChan: nil,
|
||||||
|
pingChan: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are four different kinds of cookies:
|
||||||
|
// Checked requests with replies get a reply channel and an error channel.
|
||||||
|
// Unchecked requests with replies get a reply channel and a ping channel.
|
||||||
|
// Checked requests w/o replies get a ping channel and an error channel.
|
||||||
|
// Unchecked requests w/o replies get no channels.
|
||||||
|
// The reply channel is used to send reply data.
|
||||||
|
// The error channel is used to send error data.
|
||||||
|
// The ping channel is used when one of the 'reply' or 'error' channels
|
||||||
|
// is missing but the other is present. The ping channel is way to force
|
||||||
|
// the blocking to stop and basically say "the error has been received
|
||||||
|
// in the main event loop" (when the ping channel is coupled with a reply
|
||||||
|
// channel) or "the request you made that has no reply was successful"
|
||||||
|
// (when the ping channel is coupled with an error channel).
|
||||||
|
if checked {
|
||||||
|
cookie.errorChan = make(chan error, 1)
|
||||||
|
if !reply {
|
||||||
|
cookie.pingChan = make(chan bool, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if reply {
|
||||||
|
cookie.replyChan = make(chan []byte, 1)
|
||||||
|
if !checked {
|
||||||
|
cookie.pingChan = make(chan bool, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply detects whether this is a checked or unchecked cookie, and calls
|
||||||
|
// 'replyChecked' or 'replyUnchecked' appropriately.
|
||||||
|
//
|
||||||
|
// Unless you're building requests from bytes by hand, this method should
|
||||||
|
// not be used.
|
||||||
|
func (c Cookie) Reply() ([]byte, error) {
|
||||||
|
// checked
|
||||||
|
if c.errorChan != nil {
|
||||||
|
return c.replyChecked()
|
||||||
|
}
|
||||||
|
return c.replyUnchecked()
|
||||||
|
}
|
||||||
|
|
||||||
|
// replyChecked waits for a response on either the replyChan or errorChan
|
||||||
|
// channels. If the former arrives, the bytes are returned with a nil error.
|
||||||
|
// If the latter arrives, no bytes are returned (nil) and the error received
|
||||||
|
// is returned.
|
||||||
|
//
|
||||||
|
// Unless you're building requests from bytes by hand, this method should
|
||||||
|
// not be used.
|
||||||
|
func (c Cookie) replyChecked() ([]byte, error) {
|
||||||
|
if c.replyChan == nil {
|
||||||
|
return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
|
||||||
|
"is not expecting a *reply* or an error.")
|
||||||
|
}
|
||||||
|
if c.errorChan == nil {
|
||||||
|
return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
|
||||||
|
"is not expecting a reply or an *error*.")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case reply := <-c.replyChan:
|
||||||
|
return reply, nil
|
||||||
|
case err := <-c.errorChan:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replyUnchecked waits for a response on either the replyChan or pingChan
|
||||||
|
// channels. If the former arrives, the bytes are returned with a nil error.
|
||||||
|
// If the latter arrives, no bytes are returned (nil) and a nil error
|
||||||
|
// is returned. (In the latter case, the corresponding error can be retrieved
|
||||||
|
// from (Wait|Poll)ForEvent asynchronously.)
|
||||||
|
// In all honesty, you *probably* don't want to use this method.
|
||||||
|
//
|
||||||
|
// Unless you're building requests from bytes by hand, this method should
|
||||||
|
// not be used.
|
||||||
|
func (c Cookie) replyUnchecked() ([]byte, error) {
|
||||||
|
if c.replyChan == nil {
|
||||||
|
return nil, errors.New("Cannot call 'replyUnchecked' on a cookie " +
|
||||||
|
"that is not expecting a *reply*.")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case reply := <-c.replyChan:
|
||||||
|
return reply, nil
|
||||||
|
case <-c.pingChan:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check is used for checked requests that have no replies. It is a mechanism
|
||||||
|
// by which to report "success" or "error" in a synchronous fashion. (Therefore,
|
||||||
|
// unchecked requests without replies cannot use this method.)
|
||||||
|
// If the request causes an error, it is sent to this cookie's errorChan.
|
||||||
|
// If the request was successful, there is no response from the server.
|
||||||
|
// Thus, pingChan is sent a value when the *next* reply is read.
|
||||||
|
// If no more replies are being processed, we force a round trip request with
|
||||||
|
// GetInputFocus.
|
||||||
|
//
|
||||||
|
// Unless you're building requests from bytes by hand, this method should
|
||||||
|
// not be used.
|
||||||
|
func (c Cookie) Check() error {
|
||||||
|
if c.replyChan != nil {
|
||||||
|
return errors.New("Cannot call 'Check' on a cookie that is " +
|
||||||
|
"expecting a *reply*. Use 'Reply' instead.")
|
||||||
|
}
|
||||||
|
if c.errorChan == nil {
|
||||||
|
return errors.New("Cannot call 'Check' on a cookie that is " +
|
||||||
|
"not expecting a possible *error*.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// First do a quick non-blocking check to see if we've been pinged.
|
||||||
|
select {
|
||||||
|
case err := <-c.errorChan:
|
||||||
|
return err
|
||||||
|
case <-c.pingChan:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now force a round trip and try again, but block this time.
|
||||||
|
c.conn.Sync()
|
||||||
|
select {
|
||||||
|
case err := <-c.errorChan:
|
||||||
|
return err
|
||||||
|
case <-c.pingChan:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,592 @@
|
||||||
|
// Package damage is the X client API for the DAMAGE extension.
|
||||||
|
package damage
|
||||||
|
|
||||||
|
// This file is automatically generated from damage.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xfixes"
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the DAMAGE extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 6, "DAMAGE").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named DAMAGE could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["DAMAGE"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["DAMAGE"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["DAMAGE"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["DAMAGE"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["DAMAGE"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BadBadDamage is the error number for a BadBadDamage.
|
||||||
|
const BadBadDamage = 0
|
||||||
|
|
||||||
|
type BadDamageError struct {
|
||||||
|
Sequence uint16
|
||||||
|
NiceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// BadDamageErrorNew constructs a BadDamageError value that implements xgb.Error from a byte slice.
|
||||||
|
func BadDamageErrorNew(buf []byte) xgb.Error {
|
||||||
|
v := BadDamageError{}
|
||||||
|
v.NiceName = "BadDamage"
|
||||||
|
|
||||||
|
b := 1 // skip error determinant
|
||||||
|
b += 1 // don't read error number
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceId returns the sequence id attached to the BadBadDamage error.
|
||||||
|
// This is mostly used internally.
|
||||||
|
func (err BadDamageError) SequenceId() uint16 {
|
||||||
|
return err.Sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
// BadId returns the 'BadValue' number if one exists for the BadBadDamage error. If no bad value exists, 0 is returned.
|
||||||
|
func (err BadDamageError) BadId() uint32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a rudimentary string representation of the BadBadDamage error.
|
||||||
|
|
||||||
|
func (err BadDamageError) Error() string {
|
||||||
|
fieldVals := make([]string, 0, 0)
|
||||||
|
fieldVals = append(fieldVals, "NiceName: "+err.NiceName)
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Sequence: %d", err.Sequence))
|
||||||
|
return "BadBadDamage {" + xgb.StringsJoin(fieldVals, ", ") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtErrorFuncs["DAMAGE"][0] = BadDamageErrorNew
|
||||||
|
}
|
||||||
|
|
||||||
|
type Damage uint32
|
||||||
|
|
||||||
|
func NewDamageId(c *xgb.Conn) (Damage, error) {
|
||||||
|
id, err := c.NewId()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return Damage(id), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify is the event number for a NotifyEvent.
|
||||||
|
const Notify = 0
|
||||||
|
|
||||||
|
type NotifyEvent struct {
|
||||||
|
Sequence uint16
|
||||||
|
Level byte
|
||||||
|
Drawable xproto.Drawable
|
||||||
|
Damage Damage
|
||||||
|
Timestamp xproto.Timestamp
|
||||||
|
Area xproto.Rectangle
|
||||||
|
Geometry xproto.Rectangle
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyEventNew constructs a NotifyEvent value that implements xgb.Event from a byte slice.
|
||||||
|
func NotifyEventNew(buf []byte) xgb.Event {
|
||||||
|
v := NotifyEvent{}
|
||||||
|
b := 1 // don't read event number
|
||||||
|
|
||||||
|
v.Level = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Drawable = xproto.Drawable(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Damage = Damage(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Timestamp = xproto.Timestamp(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Area = xproto.Rectangle{}
|
||||||
|
b += xproto.RectangleRead(buf[b:], &v.Area)
|
||||||
|
|
||||||
|
v.Geometry = xproto.Rectangle{}
|
||||||
|
b += xproto.RectangleRead(buf[b:], &v.Geometry)
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes writes a NotifyEvent value to a byte slice.
|
||||||
|
func (v NotifyEvent) Bytes() []byte {
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
b := 0
|
||||||
|
|
||||||
|
// write event number
|
||||||
|
buf[b] = 0
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = v.Level
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 2 // skip sequence number
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(v.Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(v.Damage))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(v.Timestamp))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
{
|
||||||
|
structBytes := v.Area.Bytes()
|
||||||
|
copy(buf[b:], structBytes)
|
||||||
|
b += len(structBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
structBytes := v.Geometry.Bytes()
|
||||||
|
copy(buf[b:], structBytes)
|
||||||
|
b += len(structBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceId returns the sequence id attached to the Notify event.
|
||||||
|
// Events without a sequence number (KeymapNotify) return 0.
|
||||||
|
// This is mostly used internally.
|
||||||
|
func (v NotifyEvent) SequenceId() uint16 {
|
||||||
|
return v.Sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is a rudimentary string representation of NotifyEvent.
|
||||||
|
func (v NotifyEvent) String() string {
|
||||||
|
fieldVals := make([]string, 0, 6)
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Sequence: %d", v.Sequence))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Level: %d", v.Level))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Drawable: %d", v.Drawable))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Damage: %d", v.Damage))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Timestamp: %d", v.Timestamp))
|
||||||
|
return "Notify {" + xgb.StringsJoin(fieldVals, ", ") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["DAMAGE"][0] = NotifyEventNew
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReportLevelRawRectangles = 0
|
||||||
|
ReportLevelDeltaRectangles = 1
|
||||||
|
ReportLevelBoundingBox = 2
|
||||||
|
ReportLevelNonEmpty = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// AddCookie is a cookie used only for Add requests.
|
||||||
|
type AddCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func Add(c *xgb.Conn, Drawable xproto.Drawable, Region xfixes.Region) AddCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Add' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(addRequest(c, Drawable, Region), cookie)
|
||||||
|
return AddCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using AddCookie.Check()
|
||||||
|
func AddChecked(c *xgb.Conn, Drawable xproto.Drawable, Region xfixes.Region) AddCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Add' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(addRequest(c, Drawable, Region), cookie)
|
||||||
|
return AddCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook AddCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Add
|
||||||
|
// addRequest writes a Add request to a byte slice.
|
||||||
|
func addRequest(c *xgb.Conn, Drawable xproto.Drawable, Region xfixes.Region) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DAMAGE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 4 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Region))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCookie is a cookie used only for Create requests.
|
||||||
|
type CreateCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func Create(c *xgb.Conn, Damage Damage, Drawable xproto.Drawable, Level byte) CreateCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Create' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(createRequest(c, Damage, Drawable, Level), cookie)
|
||||||
|
return CreateCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using CreateCookie.Check()
|
||||||
|
func CreateChecked(c *xgb.Conn, Damage Damage, Drawable xproto.Drawable, Level byte) CreateCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Create' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(createRequest(c, Damage, Drawable, Level), cookie)
|
||||||
|
return CreateCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook CreateCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Create
|
||||||
|
// createRequest writes a Create request to a byte slice.
|
||||||
|
func createRequest(c *xgb.Conn, Damage Damage, Drawable xproto.Drawable, Level byte) []byte {
|
||||||
|
size := 16
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DAMAGE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 1 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Damage))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
buf[b] = Level
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroyCookie is a cookie used only for Destroy requests.
|
||||||
|
type DestroyCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func Destroy(c *xgb.Conn, Damage Damage) DestroyCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Destroy' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(destroyRequest(c, Damage), cookie)
|
||||||
|
return DestroyCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroyChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using DestroyCookie.Check()
|
||||||
|
func DestroyChecked(c *xgb.Conn, Damage Damage) DestroyCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Destroy' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(destroyRequest(c, Damage), cookie)
|
||||||
|
return DestroyCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook DestroyCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Destroy
|
||||||
|
// destroyRequest writes a Destroy request to a byte slice.
|
||||||
|
func destroyRequest(c *xgb.Conn, Damage Damage) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DAMAGE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 2 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Damage))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionCookie is a cookie used only for QueryVersion requests.
|
||||||
|
type QueryVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
|
||||||
|
func QueryVersion(c *xgb.Conn, ClientMajorVersion uint32, ClientMinorVersion uint32) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func QueryVersionUnchecked(c *xgb.Conn, ClientMajorVersion uint32, ClientMinorVersion uint32) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionReply represents the data returned from a QueryVersion request.
|
||||||
|
type QueryVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
MajorVersion uint32
|
||||||
|
MinorVersion uint32
|
||||||
|
// padding: 16 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a QueryVersion request.
|
||||||
|
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return queryVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionReply reads a byte slice into a QueryVersionReply value.
|
||||||
|
func queryVersionReply(buf []byte) *QueryVersionReply {
|
||||||
|
v := new(QueryVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MajorVersion = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MinorVersion = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 16 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for QueryVersion
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion uint32, ClientMinorVersion uint32) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DAMAGE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], ClientMajorVersion)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], ClientMinorVersion)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubtractCookie is a cookie used only for Subtract requests.
|
||||||
|
type SubtractCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func Subtract(c *xgb.Conn, Damage Damage, Repair xfixes.Region, Parts xfixes.Region) SubtractCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Subtract' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(subtractRequest(c, Damage, Repair, Parts), cookie)
|
||||||
|
return SubtractCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubtractChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using SubtractCookie.Check()
|
||||||
|
func SubtractChecked(c *xgb.Conn, Damage Damage, Repair xfixes.Region, Parts xfixes.Region) SubtractCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DAMAGE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Subtract' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(subtractRequest(c, Damage, Repair, Parts), cookie)
|
||||||
|
return SubtractCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook SubtractCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Subtract
|
||||||
|
// subtractRequest writes a Subtract request to a byte slice.
|
||||||
|
func subtractRequest(c *xgb.Conn, Damage Damage, Repair xfixes.Region, Parts xfixes.Region) []byte {
|
||||||
|
size := 16
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DAMAGE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 3 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Damage))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Repair))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Parts))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
Package XGB provides the X Go Binding, which is a low-level API to communicate
|
||||||
|
with the core X protocol and many of the X extensions.
|
||||||
|
|
||||||
|
It is *very* closely modeled on XCB, so that experience with XCB (or xpyb) is
|
||||||
|
easily translatable to XGB. That is, it uses the same cookie/reply model
|
||||||
|
and is thread safe. There are otherwise no major differences (in the API).
|
||||||
|
|
||||||
|
Most uses of XGB typically fall under the realm of window manager and GUI kit
|
||||||
|
development, but other applications (like pagers, panels, tilers, etc.) may
|
||||||
|
also require XGB. Moreover, it is a near certainty that if you need to work
|
||||||
|
with X, xgbutil will be of great use to you as well:
|
||||||
|
https://github.com/BurntSushi/xgbutil
|
||||||
|
|
||||||
|
Example
|
||||||
|
|
||||||
|
This is an extremely terse example that demonstrates how to connect to X,
|
||||||
|
create a window, listen to StructureNotify events and Key{Press,Release}
|
||||||
|
events, map the window, and print out all events received. An example with
|
||||||
|
accompanying documentation can be found in examples/create-window.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
X, err := xgb.NewConn()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wid, _ := xproto.NewWindowId(X)
|
||||||
|
screen := xproto.Setup(X).DefaultScreen(X)
|
||||||
|
xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root,
|
||||||
|
0, 0, 500, 500, 0,
|
||||||
|
xproto.WindowClassInputOutput, screen.RootVisual,
|
||||||
|
xproto.CwBackPixel | xproto.CwEventMask,
|
||||||
|
[]uint32{ // values must be in the order defined by the protocol
|
||||||
|
0xffffffff,
|
||||||
|
xproto.EventMaskStructureNotify |
|
||||||
|
xproto.EventMaskKeyPress |
|
||||||
|
xproto.EventMaskKeyRelease})
|
||||||
|
|
||||||
|
xproto.MapWindow(X, wid)
|
||||||
|
for {
|
||||||
|
ev, xerr := X.WaitForEvent()
|
||||||
|
if ev == nil && xerr == nil {
|
||||||
|
fmt.Println("Both event and error are nil. Exiting...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ev != nil {
|
||||||
|
fmt.Printf("Event: %s\n", ev)
|
||||||
|
}
|
||||||
|
if xerr != nil {
|
||||||
|
fmt.Printf("Error: %s\n", xerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Xinerama Example
|
||||||
|
|
||||||
|
This is another small example that shows how to query Xinerama for geometry
|
||||||
|
information of each active head. Accompanying documentation for this example
|
||||||
|
can be found in examples/xinerama.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
"github.com/BurntSushi/xgb/xinerama"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
X, err := xgb.NewConn()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the Xinerama extension.
|
||||||
|
// The appropriate 'Init' function must be run for *every*
|
||||||
|
// extension before any of its requests can be used.
|
||||||
|
err = xinerama.Init(X)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reply, err := xinerama.QueryScreens(X).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Number of heads: %d\n", reply.Number)
|
||||||
|
for i, screen := range reply.ScreenInfo {
|
||||||
|
fmt.Printf("%d :: X: %d, Y: %d, Width: %d, Height: %d\n",
|
||||||
|
i, screen.XOrg, screen.YOrg, screen.Width, screen.Height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Parallelism
|
||||||
|
|
||||||
|
XGB can benefit greatly from parallelism due to its concurrent design. For
|
||||||
|
evidence of this claim, please see the benchmarks in xproto/xproto_test.go.
|
||||||
|
|
||||||
|
Tests
|
||||||
|
|
||||||
|
xproto/xproto_test.go contains a number of contrived tests that stress
|
||||||
|
particular corners of XGB that I presume could be problem areas. Namely:
|
||||||
|
requests with no replies, requests with replies, checked errors, unchecked
|
||||||
|
errors, sequence number wrapping, cookie buffer flushing (i.e., forcing a round
|
||||||
|
trip every N requests made that don't have a reply), getting/setting properties
|
||||||
|
and creating a window and listening to StructureNotify events.
|
||||||
|
|
||||||
|
Code Generator
|
||||||
|
|
||||||
|
Both XCB and xpyb use the same Python module (xcbgen) for a code generator. XGB
|
||||||
|
(before this fork) used the same code generator as well, but in my attempt to
|
||||||
|
add support for more extensions, I found the code generator extremely difficult
|
||||||
|
to work with. Therefore, I re-wrote the code generator in Go. It can be found
|
||||||
|
in its own sub-package, xgbgen, of xgb. My design of xgbgen includes a rough
|
||||||
|
consideration that it could be used for other languages.
|
||||||
|
|
||||||
|
What works
|
||||||
|
|
||||||
|
I am reasonably confident that the core X protocol is in full working form. I've
|
||||||
|
also tested the Xinerama and RandR extensions sparingly. Many of the other
|
||||||
|
existing extensions have Go source generated (and are compilable) and are
|
||||||
|
included in this package, but I am currently unsure of their status. They
|
||||||
|
*should* work.
|
||||||
|
|
||||||
|
What does not work
|
||||||
|
|
||||||
|
XKB is the only extension that intentionally does not work, although I suspect
|
||||||
|
that GLX also does not work (however, there is Go source code for GLX that
|
||||||
|
compiles, unlike XKB). I don't currently have any intention of getting XKB
|
||||||
|
working, due to its complexity and my current mental incapacity to test it.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package xgb
|
|
@ -0,0 +1,715 @@
|
||||||
|
// Package dpms is the X client API for the DPMS extension.
|
||||||
|
package dpms
|
||||||
|
|
||||||
|
// This file is automatically generated from dpms.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the DPMS extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 4, "DPMS").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named DPMS could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["DPMS"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["DPMS"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["DPMS"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["DPMS"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["DPMS"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
DPMSModeOn = 0
|
||||||
|
DPMSModeStandby = 1
|
||||||
|
DPMSModeSuspend = 2
|
||||||
|
DPMSModeOff = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// CapableCookie is a cookie used only for Capable requests.
|
||||||
|
type CapableCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capable sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling CapableCookie.Reply()
|
||||||
|
func Capable(c *xgb.Conn) CapableCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Capable' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(capableRequest(c), cookie)
|
||||||
|
return CapableCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CapableUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func CapableUnchecked(c *xgb.Conn) CapableCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Capable' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(capableRequest(c), cookie)
|
||||||
|
return CapableCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CapableReply represents the data returned from a Capable request.
|
||||||
|
type CapableReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
Capable bool
|
||||||
|
// padding: 23 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a Capable request.
|
||||||
|
func (cook CapableCookie) Reply() (*CapableReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return capableReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// capableReply reads a byte slice into a CapableReply value.
|
||||||
|
func capableReply(buf []byte) *CapableReply {
|
||||||
|
v := new(CapableReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
if buf[b] == 1 {
|
||||||
|
v.Capable = true
|
||||||
|
} else {
|
||||||
|
v.Capable = false
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 23 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Capable
|
||||||
|
// capableRequest writes a Capable request to a byte slice.
|
||||||
|
func capableRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DPMS"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 1 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableCookie is a cookie used only for Disable requests.
|
||||||
|
type DisableCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func Disable(c *xgb.Conn) DisableCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Disable' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(disableRequest(c), cookie)
|
||||||
|
return DisableCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using DisableCookie.Check()
|
||||||
|
func DisableChecked(c *xgb.Conn) DisableCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Disable' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(disableRequest(c), cookie)
|
||||||
|
return DisableCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook DisableCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Disable
|
||||||
|
// disableRequest writes a Disable request to a byte slice.
|
||||||
|
func disableRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DPMS"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 5 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableCookie is a cookie used only for Enable requests.
|
||||||
|
type EnableCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func Enable(c *xgb.Conn) EnableCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Enable' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(enableRequest(c), cookie)
|
||||||
|
return EnableCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using EnableCookie.Check()
|
||||||
|
func EnableChecked(c *xgb.Conn) EnableCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Enable' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(enableRequest(c), cookie)
|
||||||
|
return EnableCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook EnableCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Enable
|
||||||
|
// enableRequest writes a Enable request to a byte slice.
|
||||||
|
func enableRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DPMS"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 4 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceLevelCookie is a cookie used only for ForceLevel requests.
|
||||||
|
type ForceLevelCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceLevel sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func ForceLevel(c *xgb.Conn, PowerLevel uint16) ForceLevelCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'ForceLevel' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(forceLevelRequest(c, PowerLevel), cookie)
|
||||||
|
return ForceLevelCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceLevelChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using ForceLevelCookie.Check()
|
||||||
|
func ForceLevelChecked(c *xgb.Conn, PowerLevel uint16) ForceLevelCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'ForceLevel' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(forceLevelRequest(c, PowerLevel), cookie)
|
||||||
|
return ForceLevelCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook ForceLevelCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for ForceLevel
|
||||||
|
// forceLevelRequest writes a ForceLevel request to a byte slice.
|
||||||
|
func forceLevelRequest(c *xgb.Conn, PowerLevel uint16) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DPMS"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 6 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], PowerLevel)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimeoutsCookie is a cookie used only for GetTimeouts requests.
|
||||||
|
type GetTimeoutsCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimeouts sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetTimeoutsCookie.Reply()
|
||||||
|
func GetTimeouts(c *xgb.Conn) GetTimeoutsCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetTimeouts' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getTimeoutsRequest(c), cookie)
|
||||||
|
return GetTimeoutsCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimeoutsUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetTimeoutsUnchecked(c *xgb.Conn) GetTimeoutsCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetTimeouts' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getTimeoutsRequest(c), cookie)
|
||||||
|
return GetTimeoutsCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimeoutsReply represents the data returned from a GetTimeouts request.
|
||||||
|
type GetTimeoutsReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
StandbyTimeout uint16
|
||||||
|
SuspendTimeout uint16
|
||||||
|
OffTimeout uint16
|
||||||
|
// padding: 18 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetTimeouts request.
|
||||||
|
func (cook GetTimeoutsCookie) Reply() (*GetTimeoutsReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getTimeoutsReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTimeoutsReply reads a byte slice into a GetTimeoutsReply value.
|
||||||
|
func getTimeoutsReply(buf []byte) *GetTimeoutsReply {
|
||||||
|
v := new(GetTimeoutsReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.StandbyTimeout = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.SuspendTimeout = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.OffTimeout = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
b += 18 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetTimeouts
|
||||||
|
// getTimeoutsRequest writes a GetTimeouts request to a byte slice.
|
||||||
|
func getTimeoutsRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DPMS"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 2 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionCookie is a cookie used only for GetVersion requests.
|
||||||
|
type GetVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetVersionCookie.Reply()
|
||||||
|
func GetVersion(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) GetVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetVersion' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return GetVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetVersionUnchecked(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) GetVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetVersion' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return GetVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionReply represents the data returned from a GetVersion request.
|
||||||
|
type GetVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
ServerMajorVersion uint16
|
||||||
|
ServerMinorVersion uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetVersion request.
|
||||||
|
func (cook GetVersionCookie) Reply() (*GetVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVersionReply reads a byte slice into a GetVersionReply value.
|
||||||
|
func getVersionReply(buf []byte) *GetVersionReply {
|
||||||
|
v := new(GetVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.ServerMajorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.ServerMinorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetVersion
|
||||||
|
// getVersionRequest writes a GetVersion request to a byte slice.
|
||||||
|
func getVersionRequest(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DPMS"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], ClientMajorVersion)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], ClientMinorVersion)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoCookie is a cookie used only for Info requests.
|
||||||
|
type InfoCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling InfoCookie.Reply()
|
||||||
|
func Info(c *xgb.Conn) InfoCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Info' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(infoRequest(c), cookie)
|
||||||
|
return InfoCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func InfoUnchecked(c *xgb.Conn) InfoCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'Info' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(infoRequest(c), cookie)
|
||||||
|
return InfoCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoReply represents the data returned from a Info request.
|
||||||
|
type InfoReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
PowerLevel uint16
|
||||||
|
State bool
|
||||||
|
// padding: 21 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a Info request.
|
||||||
|
func (cook InfoCookie) Reply() (*InfoReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return infoReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// infoReply reads a byte slice into a InfoReply value.
|
||||||
|
func infoReply(buf []byte) *InfoReply {
|
||||||
|
v := new(InfoReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.PowerLevel = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
if buf[b] == 1 {
|
||||||
|
v.State = true
|
||||||
|
} else {
|
||||||
|
v.State = false
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 21 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Info
|
||||||
|
// infoRequest writes a Info request to a byte slice.
|
||||||
|
func infoRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DPMS"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 7 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTimeoutsCookie is a cookie used only for SetTimeouts requests.
|
||||||
|
type SetTimeoutsCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTimeouts sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func SetTimeouts(c *xgb.Conn, StandbyTimeout uint16, SuspendTimeout uint16, OffTimeout uint16) SetTimeoutsCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'SetTimeouts' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(setTimeoutsRequest(c, StandbyTimeout, SuspendTimeout, OffTimeout), cookie)
|
||||||
|
return SetTimeoutsCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTimeoutsChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using SetTimeoutsCookie.Check()
|
||||||
|
func SetTimeoutsChecked(c *xgb.Conn, StandbyTimeout uint16, SuspendTimeout uint16, OffTimeout uint16) SetTimeoutsCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["DPMS"]; !ok {
|
||||||
|
panic("Cannot issue request 'SetTimeouts' using the uninitialized extension 'DPMS'. dpms.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(setTimeoutsRequest(c, StandbyTimeout, SuspendTimeout, OffTimeout), cookie)
|
||||||
|
return SetTimeoutsCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook SetTimeoutsCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for SetTimeouts
|
||||||
|
// setTimeoutsRequest writes a SetTimeouts request to a byte slice.
|
||||||
|
func setTimeoutsRequest(c *xgb.Conn, StandbyTimeout uint16, SuspendTimeout uint16, OffTimeout uint16) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["DPMS"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 3 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], StandbyTimeout)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], SuspendTimeout)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], OffTimeout)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
||||||
|
atoms
|
||||||
|
*.info
|
||||||
|
*.prof
|
|
@ -0,0 +1,12 @@
|
||||||
|
atoms:
|
||||||
|
go build
|
||||||
|
|
||||||
|
test: atoms
|
||||||
|
./atoms --requests 500000 --cpu 1 \
|
||||||
|
--cpuprof cpu1.prof --memprof mem1.prof > atoms1.info 2>&1
|
||||||
|
./atoms --requests 500000 --cpu 2 \
|
||||||
|
--cpuprof cpu2.prof --memprof mem2.prof > atoms2.info 2>&1
|
||||||
|
./atoms --requests 500000 --cpu 3 \
|
||||||
|
--cpuprof cpu3.prof --memprof mem3.prof > atoms3.info 2>&1
|
||||||
|
./atoms --requests 500000 --cpu 6 \
|
||||||
|
--cpuprof cpu6.prof --memprof mem6.prof > atoms6.info 2>&1
|
|
@ -0,0 +1,91 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
flagRequests int
|
||||||
|
flagGOMAXPROCS int
|
||||||
|
flagCpuProfName string
|
||||||
|
flagMemProfName string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.IntVar(&flagRequests, "requests", 100000, "Number of atoms to intern.")
|
||||||
|
flag.IntVar(&flagGOMAXPROCS, "cpu", 1, "Value of GOMAXPROCS.")
|
||||||
|
flag.StringVar(&flagCpuProfName, "cpuprof", "cpu.prof",
|
||||||
|
"Name of CPU profile file.")
|
||||||
|
flag.StringVar(&flagMemProfName, "memprof", "mem.prof",
|
||||||
|
"Name of memory profile file.")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
runtime.GOMAXPROCS(flagGOMAXPROCS)
|
||||||
|
}
|
||||||
|
|
||||||
|
func seqNames(n int) []string {
|
||||||
|
names := make([]string, n)
|
||||||
|
for i := range names {
|
||||||
|
names[i] = fmt.Sprintf("NAME%d", i)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
X, err := xgb.NewConn()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
names := seqNames(flagRequests)
|
||||||
|
|
||||||
|
fcpu, err := os.Create(flagCpuProfName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer fcpu.Close()
|
||||||
|
pprof.StartCPUProfile(fcpu)
|
||||||
|
defer pprof.StopCPUProfile()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
cookies := make([]xproto.InternAtomCookie, flagRequests)
|
||||||
|
for i := 0; i < flagRequests; i++ {
|
||||||
|
cookies[i] = xproto.InternAtom(X,
|
||||||
|
false, uint16(len(names[i])), names[i])
|
||||||
|
}
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
cookie.Reply()
|
||||||
|
}
|
||||||
|
fmt.Printf("Exec time: %s\n\n", time.Since(start))
|
||||||
|
|
||||||
|
fmem, err := os.Create(flagMemProfName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer fmem.Close()
|
||||||
|
pprof.WriteHeapProfile(fmem)
|
||||||
|
|
||||||
|
memStats := &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(memStats)
|
||||||
|
|
||||||
|
// This isn't right. I'm not sure what's wrong.
|
||||||
|
lastGcTime := time.Unix(int64(memStats.LastGC/1000000000),
|
||||||
|
int64(memStats.LastGC-memStats.LastGC/1000000000))
|
||||||
|
|
||||||
|
fmt.Printf("Alloc: %d\n", memStats.Alloc)
|
||||||
|
fmt.Printf("TotalAlloc: %d\n", memStats.TotalAlloc)
|
||||||
|
fmt.Printf("LastGC: %s\n", lastGcTime)
|
||||||
|
fmt.Printf("PauseTotalNs: %d\n", memStats.PauseTotalNs)
|
||||||
|
fmt.Printf("PauseNs: %d\n", memStats.PauseNs)
|
||||||
|
fmt.Printf("NumGC: %d\n", memStats.NumGC)
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
// Example create-window shows how to create a window, map it, resize it,
|
||||||
|
// and listen to structure and key events (i.e., when the window is resized
|
||||||
|
// by the window manager, or when key presses/releases are made when the
|
||||||
|
// window has focus). The events are printed to stdout.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
X, err := xgb.NewConn()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// xproto.Setup retrieves the Setup information from the setup bytes
|
||||||
|
// gathered during connection.
|
||||||
|
setup := xproto.Setup(X)
|
||||||
|
|
||||||
|
// This is the default screen with all its associated info.
|
||||||
|
screen := setup.DefaultScreen(X)
|
||||||
|
|
||||||
|
// Any time a new resource (i.e., a window, pixmap, graphics context, etc.)
|
||||||
|
// is created, we need to generate a resource identifier.
|
||||||
|
// If the resource is a window, then use xproto.NewWindowId. If it's for
|
||||||
|
// a pixmap, then use xproto.NewPixmapId. And so on...
|
||||||
|
wid, _ := xproto.NewWindowId(X)
|
||||||
|
|
||||||
|
// CreateWindow takes a boatload of parameters.
|
||||||
|
xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root,
|
||||||
|
0, 0, 500, 500, 0,
|
||||||
|
xproto.WindowClassInputOutput, screen.RootVisual, 0, []uint32{})
|
||||||
|
|
||||||
|
// This call to ChangeWindowAttributes could be factored out and
|
||||||
|
// included with the above CreateWindow call, but it is left here for
|
||||||
|
// instructive purposes. It tells X to send us events when the 'structure'
|
||||||
|
// of the window is changed (i.e., when it is resized, mapped, unmapped,
|
||||||
|
// etc.) and when a key press or a key release has been made when the
|
||||||
|
// window has focus.
|
||||||
|
// We also set the 'BackPixel' to white so that the window isn't butt ugly.
|
||||||
|
xproto.ChangeWindowAttributes(X, wid,
|
||||||
|
xproto.CwBackPixel|xproto.CwEventMask,
|
||||||
|
[]uint32{ // values must be in the order defined by the protocol
|
||||||
|
0xffffffff,
|
||||||
|
xproto.EventMaskStructureNotify |
|
||||||
|
xproto.EventMaskKeyPress |
|
||||||
|
xproto.EventMaskKeyRelease})
|
||||||
|
|
||||||
|
// MapWindow makes the window we've created appear on the screen.
|
||||||
|
// We demonstrated the use of a 'checked' request here.
|
||||||
|
// A checked request is a fancy way of saying, "do error handling
|
||||||
|
// synchronously." Namely, if there is a problem with the MapWindow request,
|
||||||
|
// we'll get the error *here*. If we were to do a normal unchecked
|
||||||
|
// request (like the above CreateWindow and ChangeWindowAttributes
|
||||||
|
// requests), then we would only see the error arrive in the main event
|
||||||
|
// loop.
|
||||||
|
//
|
||||||
|
// Typically, checked requests are useful when you need to make sure they
|
||||||
|
// succeed. Since they are synchronous, they incur a round trip cost before
|
||||||
|
// the program can continue, but this is only going to be noticeable if
|
||||||
|
// you're issuing tons of requests in succession.
|
||||||
|
//
|
||||||
|
// Note that requests without replies are by default unchecked while
|
||||||
|
// requests *with* replies are checked by default.
|
||||||
|
err = xproto.MapWindowChecked(X, wid).Check()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Checked Error for mapping window %d: %s\n", wid, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Map window %d successful!\n", wid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an example of an invalid MapWindow request and what an error
|
||||||
|
// looks like.
|
||||||
|
err = xproto.MapWindowChecked(X, 0).Check()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Checked Error for mapping window 0x1: %s\n", err)
|
||||||
|
} else { // neva
|
||||||
|
fmt.Printf("Map window 0x1 successful!\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the main event loop.
|
||||||
|
for {
|
||||||
|
// WaitForEvent either returns an event or an error and never both.
|
||||||
|
// If both are nil, then something went wrong and the loop should be
|
||||||
|
// halted.
|
||||||
|
//
|
||||||
|
// An error can only be seen here as a response to an unchecked
|
||||||
|
// request.
|
||||||
|
ev, xerr := X.WaitForEvent()
|
||||||
|
if ev == nil && xerr == nil {
|
||||||
|
fmt.Println("Both event and error are nil. Exiting...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ev != nil {
|
||||||
|
fmt.Printf("Event: %s\n", ev)
|
||||||
|
}
|
||||||
|
if xerr != nil {
|
||||||
|
fmt.Printf("Error: %s\n", xerr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
Package examples contains a few different use cases of XGB, like creating
|
||||||
|
a window, reading properties, and querying for information about multiple
|
||||||
|
heads using the Xinerama or RandR extensions.
|
||||||
|
|
||||||
|
If you're looking to get started quickly, I recommend checking out the
|
||||||
|
create-window example first. It is the most documented and probably covers
|
||||||
|
some of the more common bare bones cases of creating windows and responding
|
||||||
|
to events.
|
||||||
|
|
||||||
|
If you're looking to query information about your window manager,
|
||||||
|
get-active-window is a start. However, to do anything extensive requires
|
||||||
|
a lot of boiler plate. To that end, I'd recommend use of my higher level
|
||||||
|
library, xgbutil: https://github.com/BurntSushi/xgbutil
|
||||||
|
|
||||||
|
There are also examples of using the Xinerama and RandR extensions, if you're
|
||||||
|
interested in querying information about your active heads. In RandR's case,
|
||||||
|
you can also reconfigure your heads, but the example doesn't cover that.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package documentation
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Example get-active-window reads the _NET_ACTIVE_WINDOW property of the root
|
||||||
|
// window and uses the result (a window id) to get the name of the window.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
X, err := xgb.NewConn()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the window id of the root window.
|
||||||
|
setup := xproto.Setup(X)
|
||||||
|
root := setup.DefaultScreen(X).Root
|
||||||
|
|
||||||
|
// Get the atom id (i.e., intern an atom) of "_NET_ACTIVE_WINDOW".
|
||||||
|
aname := "_NET_ACTIVE_WINDOW"
|
||||||
|
activeAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
|
||||||
|
aname).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the atom id (i.e., intern an atom) of "_NET_WM_NAME".
|
||||||
|
aname = "_NET_WM_NAME"
|
||||||
|
nameAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
|
||||||
|
aname).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual value of _NET_ACTIVE_WINDOW.
|
||||||
|
// Note that 'reply.Value' is just a slice of bytes, so we use an
|
||||||
|
// XGB helper function, 'Get32', to pull an unsigned 32-bit integer out
|
||||||
|
// of the byte slice. We then convert it to an X resource id so it can
|
||||||
|
// be used to get the name of the window in the next GetProperty request.
|
||||||
|
reply, err := xproto.GetProperty(X, false, root, activeAtom.Atom,
|
||||||
|
xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
windowId := xproto.Window(xgb.Get32(reply.Value))
|
||||||
|
fmt.Printf("Active window id: %X\n", windowId)
|
||||||
|
|
||||||
|
// Now get the value of _NET_WM_NAME for the active window.
|
||||||
|
// Note that this time, we simply convert the resulting byte slice,
|
||||||
|
// reply.Value, to a string.
|
||||||
|
reply, err = xproto.GetProperty(X, false, windowId, nameAtom.Atom,
|
||||||
|
xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Active window name: %s\n", string(reply.Value))
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Example randr uses the randr protocol to get information about the active
|
||||||
|
// heads. It also listens for events that are sent when the head configuration
|
||||||
|
// changes. Since it listens to events, you'll have to manually kill this
|
||||||
|
// process when you're done (i.e., ctrl+c.)
|
||||||
|
//
|
||||||
|
// While this program is running, if you use 'xrandr' to reconfigure your
|
||||||
|
// heads, you should see event information dumped to standard out.
|
||||||
|
//
|
||||||
|
// For more information, please see the RandR protocol spec:
|
||||||
|
// http://www.x.org/releases/X11R7.6/doc/randrproto/randrproto.txt
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
"github.com/BurntSushi/xgb/randr"
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
X, _ := xgb.NewConn()
|
||||||
|
|
||||||
|
// Every extension must be initialized before it can be used.
|
||||||
|
err := randr.Init(X)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the root window on the default screen.
|
||||||
|
root := xproto.Setup(X).DefaultScreen(X).Root
|
||||||
|
|
||||||
|
// Gets the current screen resources. Screen resources contains a list
|
||||||
|
// of names, crtcs, outputs and modes, among other things.
|
||||||
|
resources, err := randr.GetScreenResources(X, root).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through all of the outputs and show some of their info.
|
||||||
|
for _, output := range resources.Outputs {
|
||||||
|
info, err := randr.GetOutputInfo(X, output, 0).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Connection == randr.ConnectionConnected {
|
||||||
|
bestMode := info.Modes[0]
|
||||||
|
for _, mode := range resources.Modes {
|
||||||
|
if mode.Id == uint32(bestMode) {
|
||||||
|
fmt.Printf("Width: %d, Height: %d\n",
|
||||||
|
mode.Width, mode.Height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n")
|
||||||
|
|
||||||
|
// Iterate through all of the crtcs and show some of their info.
|
||||||
|
for _, crtc := range resources.Crtcs {
|
||||||
|
info, err := randr.GetCrtcInfo(X, crtc, 0).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("X: %d, Y: %d, Width: %d, Height: %d\n",
|
||||||
|
info.X, info.Y, info.Width, info.Height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell RandR to send us events. (I think these are all of them, as of 1.3.)
|
||||||
|
err = randr.SelectInputChecked(X, root,
|
||||||
|
randr.NotifyMaskScreenChange|
|
||||||
|
randr.NotifyMaskCrtcChange|
|
||||||
|
randr.NotifyMaskOutputChange|
|
||||||
|
randr.NotifyMaskOutputProperty).Check()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen to events and just dump them to standard out.
|
||||||
|
// A more involved approach will have to read the 'U' field of
|
||||||
|
// RandrNotifyEvent, which is a union (really a struct) of type
|
||||||
|
// RanrNotifyDataUnion.
|
||||||
|
for {
|
||||||
|
ev, err := X.WaitForEvent()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ev)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Example xinerama shows how to query the geometry of all active heads.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
"github.com/BurntSushi/xgb/xinerama"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
X, err := xgb.NewConn()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the Xinerama extension.
|
||||||
|
// The appropriate 'Init' function must be run for *every*
|
||||||
|
// extension before any of its requests can be used.
|
||||||
|
err = xinerama.Init(X)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue a request to get the screen information.
|
||||||
|
reply, err := xinerama.QueryScreens(X).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reply.Number is the number of active heads, while reply.ScreenInfo
|
||||||
|
// is a slice of XineramaScreenInfo containing the rectangle geometry
|
||||||
|
// of each head.
|
||||||
|
fmt.Printf("Number of heads: %d\n", reply.Number)
|
||||||
|
for i, screen := range reply.ScreenInfo {
|
||||||
|
fmt.Printf("%d :: X: %d, Y: %d, Width: %d, Height: %d\n",
|
||||||
|
i, screen.XOrg, screen.YOrg, screen.Width, screen.Height)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
// Package ge is the X client API for the Generic Event Extension extension.
|
||||||
|
package ge
|
||||||
|
|
||||||
|
// This file is automatically generated from ge.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the Generic Event Extension extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 23, "Generic Event Extension").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named Generic Event Extension could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["Generic Event Extension"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["Generic Event Extension"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["Generic Event Extension"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["Generic Event Extension"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["Generic Event Extension"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// QueryVersionCookie is a cookie used only for QueryVersion requests.
|
||||||
|
type QueryVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
|
||||||
|
func QueryVersion(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Generic Event Extension"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'Generic Event Extension'. ge.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func QueryVersionUnchecked(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["Generic Event Extension"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'Generic Event Extension'. ge.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionReply represents the data returned from a QueryVersion request.
|
||||||
|
type QueryVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
MajorVersion uint16
|
||||||
|
MinorVersion uint16
|
||||||
|
// padding: 20 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a QueryVersion request.
|
||||||
|
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return queryVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionReply reads a byte slice into a QueryVersionReply value.
|
||||||
|
func queryVersionReply(buf []byte) *QueryVersionReply {
|
||||||
|
v := new(QueryVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MajorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.MinorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
b += 20 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for QueryVersion
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["Generic Event Extension"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], ClientMajorVersion)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], ClientMinorVersion)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,105 @@
|
||||||
|
package xgb
|
||||||
|
|
||||||
|
/*
|
||||||
|
help.go is meant to contain a rough hodge podge of functions that are mainly
|
||||||
|
used in the auto generated code. Indeed, several functions here are simple
|
||||||
|
wrappers so that the sub-packages don't need to be smart about which stdlib
|
||||||
|
packages to import.
|
||||||
|
|
||||||
|
Also, the 'Get..' and 'Put..' functions are used through the core xgb package
|
||||||
|
too. (xgbutil uses them too.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringsJoin is an alias to strings.Join. It allows us to avoid having to
|
||||||
|
// import 'strings' in each of the generated Go files.
|
||||||
|
func StringsJoin(ss []string, sep string) string {
|
||||||
|
return strings.Join(ss, sep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is so we don't need to import 'fmt' in the generated Go files.
|
||||||
|
func Sprintf(format string, v ...interface{}) string {
|
||||||
|
return fmt.Sprintf(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf is just a wrapper for fmt.Errorf. Exists for the same reason
|
||||||
|
// that 'stringsJoin' and 'sprintf' exists.
|
||||||
|
func Errorf(format string, v ...interface{}) error {
|
||||||
|
return fmt.Errorf(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad a length to align on 4 bytes.
|
||||||
|
func Pad(n int) int {
|
||||||
|
return (n + 3) & ^3
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopCount counts 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put16 takes a 16 bit integer and copies it into a byte slice.
|
||||||
|
func Put16(buf []byte, v uint16) {
|
||||||
|
buf[0] = byte(v)
|
||||||
|
buf[1] = byte(v >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put32 takes a 32 bit integer and copies it into a byte slice.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put64 takes a 64 bit integer and copies it into a byte slice.
|
||||||
|
func Put64(buf []byte, v uint64) {
|
||||||
|
buf[0] = byte(v)
|
||||||
|
buf[1] = byte(v >> 8)
|
||||||
|
buf[2] = byte(v >> 16)
|
||||||
|
buf[3] = byte(v >> 24)
|
||||||
|
buf[4] = byte(v >> 32)
|
||||||
|
buf[5] = byte(v >> 40)
|
||||||
|
buf[6] = byte(v >> 48)
|
||||||
|
buf[7] = byte(v >> 56)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get16 constructs a 16 bit integer from the beginning of a byte slice.
|
||||||
|
func Get16(buf []byte) uint16 {
|
||||||
|
v := uint16(buf[0])
|
||||||
|
v |= uint16(buf[1]) << 8
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get32 constructs a 32 bit integer from the beginning of a byte slice.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get64 constructs a 64 bit integer from the beginning of a byte slice.
|
||||||
|
func Get64(buf []byte) uint64 {
|
||||||
|
v := uint64(buf[0])
|
||||||
|
v |= uint64(buf[1]) << 8
|
||||||
|
v |= uint64(buf[2]) << 16
|
||||||
|
v |= uint64(buf[3]) << 24
|
||||||
|
v |= uint64(buf[4]) << 32
|
||||||
|
v |= uint64(buf[5]) << 40
|
||||||
|
v |= uint64(buf[6]) << 48
|
||||||
|
v |= uint64(buf[7]) << 56
|
||||||
|
return v
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,696 @@
|
||||||
|
// Package screensaver is the X client API for the MIT-SCREEN-SAVER extension.
|
||||||
|
package screensaver
|
||||||
|
|
||||||
|
// This file is automatically generated from screensaver.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the MIT-SCREEN-SAVER extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 16, "MIT-SCREEN-SAVER").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named MIT-SCREEN-SAVER could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["MIT-SCREEN-SAVER"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["MIT-SCREEN-SAVER"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["MIT-SCREEN-SAVER"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["MIT-SCREEN-SAVER"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["MIT-SCREEN-SAVER"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
EventNotifyMask = 1
|
||||||
|
EventCycleMask = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KindBlanked = 0
|
||||||
|
KindInternal = 1
|
||||||
|
KindExternal = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Notify is the event number for a NotifyEvent.
|
||||||
|
const Notify = 0
|
||||||
|
|
||||||
|
type NotifyEvent struct {
|
||||||
|
Sequence uint16
|
||||||
|
State byte
|
||||||
|
Time xproto.Timestamp
|
||||||
|
Root xproto.Window
|
||||||
|
Window xproto.Window
|
||||||
|
Kind byte
|
||||||
|
Forced bool
|
||||||
|
// padding: 14 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyEventNew constructs a NotifyEvent value that implements xgb.Event from a byte slice.
|
||||||
|
func NotifyEventNew(buf []byte) xgb.Event {
|
||||||
|
v := NotifyEvent{}
|
||||||
|
b := 1 // don't read event number
|
||||||
|
|
||||||
|
v.State = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Time = xproto.Timestamp(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Root = xproto.Window(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Kind = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
if buf[b] == 1 {
|
||||||
|
v.Forced = true
|
||||||
|
} else {
|
||||||
|
v.Forced = false
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 14 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes writes a NotifyEvent value to a byte slice.
|
||||||
|
func (v NotifyEvent) Bytes() []byte {
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
b := 0
|
||||||
|
|
||||||
|
// write event number
|
||||||
|
buf[b] = 0
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = v.State
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 2 // skip sequence number
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(v.Time))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(v.Root))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(v.Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
buf[b] = v.Kind
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
if v.Forced {
|
||||||
|
buf[b] = 1
|
||||||
|
} else {
|
||||||
|
buf[b] = 0
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 14 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceId returns the sequence id attached to the Notify event.
|
||||||
|
// Events without a sequence number (KeymapNotify) return 0.
|
||||||
|
// This is mostly used internally.
|
||||||
|
func (v NotifyEvent) SequenceId() uint16 {
|
||||||
|
return v.Sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is a rudimentary string representation of NotifyEvent.
|
||||||
|
func (v NotifyEvent) String() string {
|
||||||
|
fieldVals := make([]string, 0, 7)
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Sequence: %d", v.Sequence))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("State: %d", v.State))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Time: %d", v.Time))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Root: %d", v.Root))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Window: %d", v.Window))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Kind: %d", v.Kind))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Forced: %t", v.Forced))
|
||||||
|
return "Notify {" + xgb.StringsJoin(fieldVals, ", ") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["MIT-SCREEN-SAVER"][0] = NotifyEventNew
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
StateOff = 0
|
||||||
|
StateOn = 1
|
||||||
|
StateCycle = 2
|
||||||
|
StateDisabled = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// QueryInfoCookie is a cookie used only for QueryInfo requests.
|
||||||
|
type QueryInfoCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryInfo sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling QueryInfoCookie.Reply()
|
||||||
|
func QueryInfo(c *xgb.Conn, Drawable xproto.Drawable) QueryInfoCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryInfo' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(queryInfoRequest(c, Drawable), cookie)
|
||||||
|
return QueryInfoCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryInfoUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func QueryInfoUnchecked(c *xgb.Conn, Drawable xproto.Drawable) QueryInfoCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryInfo' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(queryInfoRequest(c, Drawable), cookie)
|
||||||
|
return QueryInfoCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryInfoReply represents the data returned from a QueryInfo request.
|
||||||
|
type QueryInfoReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
State byte
|
||||||
|
SaverWindow xproto.Window
|
||||||
|
MsUntilServer uint32
|
||||||
|
MsSinceUserInput uint32
|
||||||
|
EventMask uint32
|
||||||
|
Kind byte
|
||||||
|
// padding: 7 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a QueryInfo request.
|
||||||
|
func (cook QueryInfoCookie) Reply() (*QueryInfoReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return queryInfoReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryInfoReply reads a byte slice into a QueryInfoReply value.
|
||||||
|
func queryInfoReply(buf []byte) *QueryInfoReply {
|
||||||
|
v := new(QueryInfoReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
v.State = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.SaverWindow = xproto.Window(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MsUntilServer = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MsSinceUserInput = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.EventMask = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Kind = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 7 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for QueryInfo
|
||||||
|
// queryInfoRequest writes a QueryInfo request to a byte slice.
|
||||||
|
func queryInfoRequest(c *xgb.Conn, Drawable xproto.Drawable) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SCREEN-SAVER"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 1 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionCookie is a cookie used only for QueryVersion requests.
|
||||||
|
type QueryVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
|
||||||
|
func QueryVersion(c *xgb.Conn, ClientMajorVersion byte, ClientMinorVersion byte) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func QueryVersionUnchecked(c *xgb.Conn, ClientMajorVersion byte, ClientMinorVersion byte) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionReply represents the data returned from a QueryVersion request.
|
||||||
|
type QueryVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
ServerMajorVersion uint16
|
||||||
|
ServerMinorVersion uint16
|
||||||
|
// padding: 20 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a QueryVersion request.
|
||||||
|
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return queryVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionReply reads a byte slice into a QueryVersionReply value.
|
||||||
|
func queryVersionReply(buf []byte) *QueryVersionReply {
|
||||||
|
v := new(QueryVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.ServerMajorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.ServerMinorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
b += 20 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for QueryVersion
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion byte, ClientMinorVersion byte) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SCREEN-SAVER"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
buf[b] = ClientMajorVersion
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = ClientMinorVersion
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 2 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectInputCookie is a cookie used only for SelectInput requests.
|
||||||
|
type SelectInputCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectInput sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func SelectInput(c *xgb.Conn, Drawable xproto.Drawable, EventMask uint32) SelectInputCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'SelectInput' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(selectInputRequest(c, Drawable, EventMask), cookie)
|
||||||
|
return SelectInputCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectInputChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using SelectInputCookie.Check()
|
||||||
|
func SelectInputChecked(c *xgb.Conn, Drawable xproto.Drawable, EventMask uint32) SelectInputCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'SelectInput' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(selectInputRequest(c, Drawable, EventMask), cookie)
|
||||||
|
return SelectInputCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook SelectInputCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for SelectInput
|
||||||
|
// selectInputRequest writes a SelectInput request to a byte slice.
|
||||||
|
func selectInputRequest(c *xgb.Conn, Drawable xproto.Drawable, EventMask uint32) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SCREEN-SAVER"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 2 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], EventMask)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAttributesCookie is a cookie used only for SetAttributes requests.
|
||||||
|
type SetAttributesCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAttributes sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func SetAttributes(c *xgb.Conn, Drawable xproto.Drawable, X int16, Y int16, Width uint16, Height uint16, BorderWidth uint16, Class byte, Depth byte, Visual xproto.Visualid, ValueMask uint32, ValueList []uint32) SetAttributesCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'SetAttributes' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(setAttributesRequest(c, Drawable, X, Y, Width, Height, BorderWidth, Class, Depth, Visual, ValueMask, ValueList), cookie)
|
||||||
|
return SetAttributesCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAttributesChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using SetAttributesCookie.Check()
|
||||||
|
func SetAttributesChecked(c *xgb.Conn, Drawable xproto.Drawable, X int16, Y int16, Width uint16, Height uint16, BorderWidth uint16, Class byte, Depth byte, Visual xproto.Visualid, ValueMask uint32, ValueList []uint32) SetAttributesCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'SetAttributes' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(setAttributesRequest(c, Drawable, X, Y, Width, Height, BorderWidth, Class, Depth, Visual, ValueMask, ValueList), cookie)
|
||||||
|
return SetAttributesCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook SetAttributesCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for SetAttributes
|
||||||
|
// setAttributesRequest writes a SetAttributes request to a byte slice.
|
||||||
|
func setAttributesRequest(c *xgb.Conn, Drawable xproto.Drawable, X int16, Y int16, Width uint16, Height uint16, BorderWidth uint16, Class byte, Depth byte, Visual xproto.Visualid, ValueMask uint32, ValueList []uint32) []byte {
|
||||||
|
size := xgb.Pad((28 + xgb.Pad((4 * xgb.PopCount(int(ValueMask))))))
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SCREEN-SAVER"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 3 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(X))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(Y))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], Width)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], Height)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], BorderWidth)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
buf[b] = Class
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = Depth
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Visual))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], ValueMask)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
for i := 0; i < xgb.PopCount(int(ValueMask)); i++ {
|
||||||
|
xgb.Put32(buf[b:], ValueList[i])
|
||||||
|
b += 4
|
||||||
|
}
|
||||||
|
b = xgb.Pad(b)
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuspendCookie is a cookie used only for Suspend requests.
|
||||||
|
type SuspendCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func Suspend(c *xgb.Conn, Suspend bool) SuspendCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'Suspend' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(suspendRequest(c, Suspend), cookie)
|
||||||
|
return SuspendCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuspendChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using SuspendCookie.Check()
|
||||||
|
func SuspendChecked(c *xgb.Conn, Suspend bool) SuspendCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'Suspend' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(suspendRequest(c, Suspend), cookie)
|
||||||
|
return SuspendCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook SuspendCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Suspend
|
||||||
|
// suspendRequest writes a Suspend request to a byte slice.
|
||||||
|
func suspendRequest(c *xgb.Conn, Suspend bool) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SCREEN-SAVER"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 5 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
if Suspend {
|
||||||
|
buf[b] = 1
|
||||||
|
} else {
|
||||||
|
buf[b] = 0
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsetAttributesCookie is a cookie used only for UnsetAttributes requests.
|
||||||
|
type UnsetAttributesCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsetAttributes sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func UnsetAttributes(c *xgb.Conn, Drawable xproto.Drawable) UnsetAttributesCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'UnsetAttributes' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(unsetAttributesRequest(c, Drawable), cookie)
|
||||||
|
return UnsetAttributesCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsetAttributesChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using UnsetAttributesCookie.Check()
|
||||||
|
func UnsetAttributesChecked(c *xgb.Conn, Drawable xproto.Drawable) UnsetAttributesCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SCREEN-SAVER"]; !ok {
|
||||||
|
panic("Cannot issue request 'UnsetAttributes' using the uninitialized extension 'MIT-SCREEN-SAVER'. screensaver.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(unsetAttributesRequest(c, Drawable), cookie)
|
||||||
|
return UnsetAttributesCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook UnsetAttributesCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for UnsetAttributes
|
||||||
|
// unsetAttributesRequest writes a UnsetAttributes request to a byte slice.
|
||||||
|
func unsetAttributesRequest(c *xgb.Conn, Drawable xproto.Drawable) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SCREEN-SAVER"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 4 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,945 @@
|
||||||
|
// Package shm is the X client API for the MIT-SHM extension.
|
||||||
|
package shm
|
||||||
|
|
||||||
|
// This file is automatically generated from shm.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the MIT-SHM extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 7, "MIT-SHM").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named MIT-SHM could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["MIT-SHM"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["MIT-SHM"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["MIT-SHM"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["MIT-SHM"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["MIT-SHM"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BadBadSeg is the error number for a BadBadSeg.
|
||||||
|
const BadBadSeg = 0
|
||||||
|
|
||||||
|
type BadSegError xproto.ValueError
|
||||||
|
|
||||||
|
// BadSegErrorNew constructs a BadSegError value that implements xgb.Error from a byte slice.
|
||||||
|
func BadSegErrorNew(buf []byte) xgb.Error {
|
||||||
|
v := BadSegError(xproto.ValueErrorNew(buf).(xproto.ValueError))
|
||||||
|
v.NiceName = "BadSeg"
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceId returns the sequence id attached to the BadBadSeg error.
|
||||||
|
// This is mostly used internally.
|
||||||
|
func (err BadSegError) SequenceId() uint16 {
|
||||||
|
return err.Sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
// BadId returns the 'BadValue' number if one exists for the BadBadSeg error. If no bad value exists, 0 is returned.
|
||||||
|
func (err BadSegError) BadId() uint32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a rudimentary string representation of the BadBadSeg error.
|
||||||
|
func (err BadSegError) Error() string {
|
||||||
|
fieldVals := make([]string, 0, 4)
|
||||||
|
fieldVals = append(fieldVals, "NiceName: "+err.NiceName)
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Sequence: %d", err.Sequence))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("BadValue: %d", err.BadValue))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("MinorOpcode: %d", err.MinorOpcode))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("MajorOpcode: %d", err.MajorOpcode))
|
||||||
|
return "BadBadSeg {" + xgb.StringsJoin(fieldVals, ", ") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtErrorFuncs["MIT-SHM"][0] = BadSegErrorNew
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completion is the event number for a CompletionEvent.
|
||||||
|
const Completion = 0
|
||||||
|
|
||||||
|
type CompletionEvent struct {
|
||||||
|
Sequence uint16
|
||||||
|
// padding: 1 bytes
|
||||||
|
Drawable xproto.Drawable
|
||||||
|
MinorEvent uint16
|
||||||
|
MajorEvent byte
|
||||||
|
// padding: 1 bytes
|
||||||
|
Shmseg Seg
|
||||||
|
Offset uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompletionEventNew constructs a CompletionEvent value that implements xgb.Event from a byte slice.
|
||||||
|
func CompletionEventNew(buf []byte) xgb.Event {
|
||||||
|
v := CompletionEvent{}
|
||||||
|
b := 1 // don't read event number
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Drawable = xproto.Drawable(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MinorEvent = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.MajorEvent = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Shmseg = Seg(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Offset = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes writes a CompletionEvent value to a byte slice.
|
||||||
|
func (v CompletionEvent) Bytes() []byte {
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
b := 0
|
||||||
|
|
||||||
|
// write event number
|
||||||
|
buf[b] = 0
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
b += 2 // skip sequence number
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(v.Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], v.MinorEvent)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
buf[b] = v.MajorEvent
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(v.Shmseg))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], v.Offset)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceId returns the sequence id attached to the Completion event.
|
||||||
|
// Events without a sequence number (KeymapNotify) return 0.
|
||||||
|
// This is mostly used internally.
|
||||||
|
func (v CompletionEvent) SequenceId() uint16 {
|
||||||
|
return v.Sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is a rudimentary string representation of CompletionEvent.
|
||||||
|
func (v CompletionEvent) String() string {
|
||||||
|
fieldVals := make([]string, 0, 7)
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Sequence: %d", v.Sequence))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Drawable: %d", v.Drawable))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("MinorEvent: %d", v.MinorEvent))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("MajorEvent: %d", v.MajorEvent))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Shmseg: %d", v.Shmseg))
|
||||||
|
fieldVals = append(fieldVals, xgb.Sprintf("Offset: %d", v.Offset))
|
||||||
|
return "Completion {" + xgb.StringsJoin(fieldVals, ", ") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["MIT-SHM"][0] = CompletionEventNew
|
||||||
|
}
|
||||||
|
|
||||||
|
type Seg uint32
|
||||||
|
|
||||||
|
func NewSegId(c *xgb.Conn) (Seg, error) {
|
||||||
|
id, err := c.NewId()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return Seg(id), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// AttachCookie is a cookie used only for Attach requests.
|
||||||
|
type AttachCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func Attach(c *xgb.Conn, Shmseg Seg, Shmid uint32, ReadOnly bool) AttachCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'Attach' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(attachRequest(c, Shmseg, Shmid, ReadOnly), cookie)
|
||||||
|
return AttachCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttachChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using AttachCookie.Check()
|
||||||
|
func AttachChecked(c *xgb.Conn, Shmseg Seg, Shmid uint32, ReadOnly bool) AttachCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'Attach' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(attachRequest(c, Shmseg, Shmid, ReadOnly), cookie)
|
||||||
|
return AttachCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook AttachCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Attach
|
||||||
|
// attachRequest writes a Attach request to a byte slice.
|
||||||
|
func attachRequest(c *xgb.Conn, Shmseg Seg, Shmid uint32, ReadOnly bool) []byte {
|
||||||
|
size := 16
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SHM"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 1 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Shmseg))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Shmid)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
if ReadOnly {
|
||||||
|
buf[b] = 1
|
||||||
|
} else {
|
||||||
|
buf[b] = 0
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttachFdCookie is a cookie used only for AttachFd requests.
|
||||||
|
type AttachFdCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttachFd sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func AttachFd(c *xgb.Conn, Shmseg Seg, ReadOnly bool) AttachFdCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'AttachFd' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(attachFdRequest(c, Shmseg, ReadOnly), cookie)
|
||||||
|
return AttachFdCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttachFdChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using AttachFdCookie.Check()
|
||||||
|
func AttachFdChecked(c *xgb.Conn, Shmseg Seg, ReadOnly bool) AttachFdCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'AttachFd' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(attachFdRequest(c, Shmseg, ReadOnly), cookie)
|
||||||
|
return AttachFdCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook AttachFdCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for AttachFd
|
||||||
|
// attachFdRequest writes a AttachFd request to a byte slice.
|
||||||
|
func attachFdRequest(c *xgb.Conn, Shmseg Seg, ReadOnly bool) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SHM"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 6 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Shmseg))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
if ReadOnly {
|
||||||
|
buf[b] = 1
|
||||||
|
} else {
|
||||||
|
buf[b] = 0
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePixmapCookie is a cookie used only for CreatePixmap requests.
|
||||||
|
type CreatePixmapCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePixmap sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func CreatePixmap(c *xgb.Conn, Pid xproto.Pixmap, Drawable xproto.Drawable, Width uint16, Height uint16, Depth byte, Shmseg Seg, Offset uint32) CreatePixmapCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'CreatePixmap' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(createPixmapRequest(c, Pid, Drawable, Width, Height, Depth, Shmseg, Offset), cookie)
|
||||||
|
return CreatePixmapCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePixmapChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using CreatePixmapCookie.Check()
|
||||||
|
func CreatePixmapChecked(c *xgb.Conn, Pid xproto.Pixmap, Drawable xproto.Drawable, Width uint16, Height uint16, Depth byte, Shmseg Seg, Offset uint32) CreatePixmapCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'CreatePixmap' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(createPixmapRequest(c, Pid, Drawable, Width, Height, Depth, Shmseg, Offset), cookie)
|
||||||
|
return CreatePixmapCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook CreatePixmapCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for CreatePixmap
|
||||||
|
// createPixmapRequest writes a CreatePixmap request to a byte slice.
|
||||||
|
func createPixmapRequest(c *xgb.Conn, Pid xproto.Pixmap, Drawable xproto.Drawable, Width uint16, Height uint16, Depth byte, Shmseg Seg, Offset uint32) []byte {
|
||||||
|
size := 28
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SHM"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 5 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Pid))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], Width)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], Height)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
buf[b] = Depth
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Shmseg))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Offset)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSegmentCookie is a cookie used only for CreateSegment requests.
|
||||||
|
type CreateSegmentCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSegment sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling CreateSegmentCookie.Reply()
|
||||||
|
func CreateSegment(c *xgb.Conn, Shmseg Seg, Size uint32, ReadOnly bool) CreateSegmentCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'CreateSegment' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(createSegmentRequest(c, Shmseg, Size, ReadOnly), cookie)
|
||||||
|
return CreateSegmentCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSegmentUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func CreateSegmentUnchecked(c *xgb.Conn, Shmseg Seg, Size uint32, ReadOnly bool) CreateSegmentCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'CreateSegment' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(createSegmentRequest(c, Shmseg, Size, ReadOnly), cookie)
|
||||||
|
return CreateSegmentCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSegmentReply represents the data returned from a CreateSegment request.
|
||||||
|
type CreateSegmentReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
Nfd byte
|
||||||
|
// padding: 24 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a CreateSegment request.
|
||||||
|
func (cook CreateSegmentCookie) Reply() (*CreateSegmentReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return createSegmentReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createSegmentReply reads a byte slice into a CreateSegmentReply value.
|
||||||
|
func createSegmentReply(buf []byte) *CreateSegmentReply {
|
||||||
|
v := new(CreateSegmentReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
v.Nfd = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 24 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for CreateSegment
|
||||||
|
// createSegmentRequest writes a CreateSegment request to a byte slice.
|
||||||
|
func createSegmentRequest(c *xgb.Conn, Shmseg Seg, Size uint32, ReadOnly bool) []byte {
|
||||||
|
size := 16
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SHM"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 7 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Shmseg))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Size)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
if ReadOnly {
|
||||||
|
buf[b] = 1
|
||||||
|
} else {
|
||||||
|
buf[b] = 0
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachCookie is a cookie used only for Detach requests.
|
||||||
|
type DetachCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func Detach(c *xgb.Conn, Shmseg Seg) DetachCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'Detach' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(detachRequest(c, Shmseg), cookie)
|
||||||
|
return DetachCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using DetachCookie.Check()
|
||||||
|
func DetachChecked(c *xgb.Conn, Shmseg Seg) DetachCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'Detach' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(detachRequest(c, Shmseg), cookie)
|
||||||
|
return DetachCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook DetachCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Detach
|
||||||
|
// detachRequest writes a Detach request to a byte slice.
|
||||||
|
func detachRequest(c *xgb.Conn, Shmseg Seg) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SHM"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 2 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Shmseg))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImageCookie is a cookie used only for GetImage requests.
|
||||||
|
type GetImageCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImage sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetImageCookie.Reply()
|
||||||
|
func GetImage(c *xgb.Conn, Drawable xproto.Drawable, X int16, Y int16, Width uint16, Height uint16, PlaneMask uint32, Format byte, Shmseg Seg, Offset uint32) GetImageCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetImage' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getImageRequest(c, Drawable, X, Y, Width, Height, PlaneMask, Format, Shmseg, Offset), cookie)
|
||||||
|
return GetImageCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImageUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetImageUnchecked(c *xgb.Conn, Drawable xproto.Drawable, X int16, Y int16, Width uint16, Height uint16, PlaneMask uint32, Format byte, Shmseg Seg, Offset uint32) GetImageCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetImage' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getImageRequest(c, Drawable, X, Y, Width, Height, PlaneMask, Format, Shmseg, Offset), cookie)
|
||||||
|
return GetImageCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImageReply represents the data returned from a GetImage request.
|
||||||
|
type GetImageReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
Depth byte
|
||||||
|
Visual xproto.Visualid
|
||||||
|
Size uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetImage request.
|
||||||
|
func (cook GetImageCookie) Reply() (*GetImageReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getImageReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getImageReply reads a byte slice into a GetImageReply value.
|
||||||
|
func getImageReply(buf []byte) *GetImageReply {
|
||||||
|
v := new(GetImageReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
v.Depth = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Visual = xproto.Visualid(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Size = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetImage
|
||||||
|
// getImageRequest writes a GetImage request to a byte slice.
|
||||||
|
func getImageRequest(c *xgb.Conn, Drawable xproto.Drawable, X int16, Y int16, Width uint16, Height uint16, PlaneMask uint32, Format byte, Shmseg Seg, Offset uint32) []byte {
|
||||||
|
size := 32
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SHM"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 4 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(X))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(Y))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], Width)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], Height)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], PlaneMask)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
buf[b] = Format
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Shmseg))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Offset)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutImageCookie is a cookie used only for PutImage requests.
|
||||||
|
type PutImageCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutImage sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func PutImage(c *xgb.Conn, Drawable xproto.Drawable, Gc xproto.Gcontext, TotalWidth uint16, TotalHeight uint16, SrcX uint16, SrcY uint16, SrcWidth uint16, SrcHeight uint16, DstX int16, DstY int16, Depth byte, Format byte, SendEvent byte, Shmseg Seg, Offset uint32) PutImageCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'PutImage' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(putImageRequest(c, Drawable, Gc, TotalWidth, TotalHeight, SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, Depth, Format, SendEvent, Shmseg, Offset), cookie)
|
||||||
|
return PutImageCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutImageChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using PutImageCookie.Check()
|
||||||
|
func PutImageChecked(c *xgb.Conn, Drawable xproto.Drawable, Gc xproto.Gcontext, TotalWidth uint16, TotalHeight uint16, SrcX uint16, SrcY uint16, SrcWidth uint16, SrcHeight uint16, DstX int16, DstY int16, Depth byte, Format byte, SendEvent byte, Shmseg Seg, Offset uint32) PutImageCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'PutImage' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(putImageRequest(c, Drawable, Gc, TotalWidth, TotalHeight, SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, Depth, Format, SendEvent, Shmseg, Offset), cookie)
|
||||||
|
return PutImageCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook PutImageCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for PutImage
|
||||||
|
// putImageRequest writes a PutImage request to a byte slice.
|
||||||
|
func putImageRequest(c *xgb.Conn, Drawable xproto.Drawable, Gc xproto.Gcontext, TotalWidth uint16, TotalHeight uint16, SrcX uint16, SrcY uint16, SrcWidth uint16, SrcHeight uint16, DstX int16, DstY int16, Depth byte, Format byte, SendEvent byte, Shmseg Seg, Offset uint32) []byte {
|
||||||
|
size := 40
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SHM"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 3 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Drawable))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Gc))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], TotalWidth)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], TotalHeight)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], SrcX)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], SrcY)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], SrcWidth)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], SrcHeight)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(DstX))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(DstY))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
buf[b] = Depth
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = Format
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = SendEvent
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Shmseg))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Offset)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionCookie is a cookie used only for QueryVersion requests.
|
||||||
|
type QueryVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
|
||||||
|
func QueryVersion(c *xgb.Conn) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func QueryVersionUnchecked(c *xgb.Conn) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["MIT-SHM"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'MIT-SHM'. shm.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionReply represents the data returned from a QueryVersion request.
|
||||||
|
type QueryVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
SharedPixmaps bool
|
||||||
|
MajorVersion uint16
|
||||||
|
MinorVersion uint16
|
||||||
|
Uid uint16
|
||||||
|
Gid uint16
|
||||||
|
PixmapFormat byte
|
||||||
|
// padding: 15 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a QueryVersion request.
|
||||||
|
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return queryVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionReply reads a byte slice into a QueryVersionReply value.
|
||||||
|
func queryVersionReply(buf []byte) *QueryVersionReply {
|
||||||
|
v := new(QueryVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
if buf[b] == 1 {
|
||||||
|
v.SharedPixmaps = true
|
||||||
|
} else {
|
||||||
|
v.SharedPixmaps = false
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MajorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.MinorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Uid = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Gid = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.PixmapFormat = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 15 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for QueryVersion
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice.
|
||||||
|
func queryVersionRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["MIT-SHM"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package xgb
|
||||||
|
|
||||||
|
// Sync sends a round trip request and waits for the response.
|
||||||
|
// This forces all pending cookies to be dealt with.
|
||||||
|
// You actually shouldn't need to use this like you might with Xlib. Namely,
|
||||||
|
// buffers are automatically flushed using Go's channels and round trip requests
|
||||||
|
// are forced where appropriate automatically.
|
||||||
|
func (c *Conn) Sync() {
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(c.getInputFocusRequest(), cookie)
|
||||||
|
cookie.Reply() // wait for the buffer to clear
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInputFocusRequest writes the raw bytes to a buffer.
|
||||||
|
// It is duplicated from xproto/xproto.go.
|
||||||
|
func (c *Conn) getInputFocusRequest() []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
buf[b] = 43 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
|
@ -0,0 +1,361 @@
|
||||||
|
// Package xcmisc is the X client API for the XC-MISC extension.
|
||||||
|
package xcmisc
|
||||||
|
|
||||||
|
// This file is automatically generated from xc_misc.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the XC-MISC extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 7, "XC-MISC").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named XC-MISC could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["XC-MISC"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["XC-MISC"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["XC-MISC"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["XC-MISC"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["XC-MISC"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// GetVersionCookie is a cookie used only for GetVersion requests.
|
||||||
|
type GetVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetVersionCookie.Reply()
|
||||||
|
func GetVersion(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) GetVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XC-MISC"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetVersion' using the uninitialized extension 'XC-MISC'. xcmisc.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return GetVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetVersionUnchecked(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) GetVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XC-MISC"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetVersion' using the uninitialized extension 'XC-MISC'. xcmisc.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return GetVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionReply represents the data returned from a GetVersion request.
|
||||||
|
type GetVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
ServerMajorVersion uint16
|
||||||
|
ServerMinorVersion uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetVersion request.
|
||||||
|
func (cook GetVersionCookie) Reply() (*GetVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVersionReply reads a byte slice into a GetVersionReply value.
|
||||||
|
func getVersionReply(buf []byte) *GetVersionReply {
|
||||||
|
v := new(GetVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.ServerMajorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.ServerMinorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetVersion
|
||||||
|
// getVersionRequest writes a GetVersion request to a byte slice.
|
||||||
|
func getVersionRequest(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XC-MISC"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], ClientMajorVersion)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], ClientMinorVersion)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXIDListCookie is a cookie used only for GetXIDList requests.
|
||||||
|
type GetXIDListCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXIDList sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetXIDListCookie.Reply()
|
||||||
|
func GetXIDList(c *xgb.Conn, Count uint32) GetXIDListCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XC-MISC"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetXIDList' using the uninitialized extension 'XC-MISC'. xcmisc.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getXIDListRequest(c, Count), cookie)
|
||||||
|
return GetXIDListCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXIDListUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetXIDListUnchecked(c *xgb.Conn, Count uint32) GetXIDListCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XC-MISC"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetXIDList' using the uninitialized extension 'XC-MISC'. xcmisc.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getXIDListRequest(c, Count), cookie)
|
||||||
|
return GetXIDListCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXIDListReply represents the data returned from a GetXIDList request.
|
||||||
|
type GetXIDListReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
IdsLen uint32
|
||||||
|
// padding: 20 bytes
|
||||||
|
Ids []uint32 // size: xgb.Pad((int(IdsLen) * 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetXIDList request.
|
||||||
|
func (cook GetXIDListCookie) Reply() (*GetXIDListReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getXIDListReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getXIDListReply reads a byte slice into a GetXIDListReply value.
|
||||||
|
func getXIDListReply(buf []byte) *GetXIDListReply {
|
||||||
|
v := new(GetXIDListReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.IdsLen = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 20 // padding
|
||||||
|
|
||||||
|
v.Ids = make([]uint32, v.IdsLen)
|
||||||
|
for i := 0; i < int(v.IdsLen); i++ {
|
||||||
|
v.Ids[i] = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetXIDList
|
||||||
|
// getXIDListRequest writes a GetXIDList request to a byte slice.
|
||||||
|
func getXIDListRequest(c *xgb.Conn, Count uint32) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XC-MISC"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 2 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Count)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXIDRangeCookie is a cookie used only for GetXIDRange requests.
|
||||||
|
type GetXIDRangeCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXIDRange sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetXIDRangeCookie.Reply()
|
||||||
|
func GetXIDRange(c *xgb.Conn) GetXIDRangeCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XC-MISC"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetXIDRange' using the uninitialized extension 'XC-MISC'. xcmisc.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getXIDRangeRequest(c), cookie)
|
||||||
|
return GetXIDRangeCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXIDRangeUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetXIDRangeUnchecked(c *xgb.Conn) GetXIDRangeCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XC-MISC"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetXIDRange' using the uninitialized extension 'XC-MISC'. xcmisc.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getXIDRangeRequest(c), cookie)
|
||||||
|
return GetXIDRangeCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXIDRangeReply represents the data returned from a GetXIDRange request.
|
||||||
|
type GetXIDRangeReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
StartId uint32
|
||||||
|
Count uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetXIDRange request.
|
||||||
|
func (cook GetXIDRangeCookie) Reply() (*GetXIDRangeReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getXIDRangeReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getXIDRangeReply reads a byte slice into a GetXIDRangeReply value.
|
||||||
|
func getXIDRangeReply(buf []byte) *GetXIDRangeReply {
|
||||||
|
v := new(GetXIDRangeReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.StartId = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Count = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetXIDRange
|
||||||
|
// getXIDRangeRequest writes a GetXIDRange request to a byte slice.
|
||||||
|
func getXIDRangeRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XC-MISC"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 1 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
|
@ -0,0 +1,595 @@
|
||||||
|
// Package xevie is the X client API for the XEVIE extension.
|
||||||
|
package xevie
|
||||||
|
|
||||||
|
// This file is automatically generated from xevie.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the XEVIE extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 5, "XEVIE").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named XEVIE could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["XEVIE"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["XEVIE"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["XEVIE"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["XEVIE"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["XEVIE"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
DatatypeUnmodified = 0
|
||||||
|
DatatypeModified = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
// padding: 32 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventRead reads a byte slice into a Event value.
|
||||||
|
func EventRead(buf []byte, v *Event) int {
|
||||||
|
b := 0
|
||||||
|
|
||||||
|
b += 32 // padding
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventReadList reads a byte slice into a list of Event values.
|
||||||
|
func EventReadList(buf []byte, dest []Event) int {
|
||||||
|
b := 0
|
||||||
|
for i := 0; i < len(dest); i++ {
|
||||||
|
dest[i] = Event{}
|
||||||
|
b += EventRead(buf[b:], &dest[i])
|
||||||
|
}
|
||||||
|
return xgb.Pad(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes writes a Event value to a byte slice.
|
||||||
|
func (v Event) Bytes() []byte {
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
b := 0
|
||||||
|
|
||||||
|
b += 32 // padding
|
||||||
|
|
||||||
|
return buf[:b]
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventListBytes writes a list of Event values to a byte slice.
|
||||||
|
func EventListBytes(buf []byte, list []Event) int {
|
||||||
|
b := 0
|
||||||
|
var structBytes []byte
|
||||||
|
for _, item := range list {
|
||||||
|
structBytes = item.Bytes()
|
||||||
|
copy(buf[b:], structBytes)
|
||||||
|
b += len(structBytes)
|
||||||
|
}
|
||||||
|
return xgb.Pad(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// EndCookie is a cookie used only for End requests.
|
||||||
|
type EndCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// End sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling EndCookie.Reply()
|
||||||
|
func End(c *xgb.Conn, Cmap uint32) EndCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'End' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(endRequest(c, Cmap), cookie)
|
||||||
|
return EndCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func EndUnchecked(c *xgb.Conn, Cmap uint32) EndCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'End' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(endRequest(c, Cmap), cookie)
|
||||||
|
return EndCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndReply represents the data returned from a End request.
|
||||||
|
type EndReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
// padding: 24 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a End request.
|
||||||
|
func (cook EndCookie) Reply() (*EndReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return endReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// endReply reads a byte slice into a EndReply value.
|
||||||
|
func endReply(buf []byte) *EndReply {
|
||||||
|
v := new(EndReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 24 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for End
|
||||||
|
// endRequest writes a End request to a byte slice.
|
||||||
|
func endRequest(c *xgb.Conn, Cmap uint32) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XEVIE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 2 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Cmap)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionCookie is a cookie used only for QueryVersion requests.
|
||||||
|
type QueryVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
|
||||||
|
func QueryVersion(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func QueryVersionUnchecked(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionReply represents the data returned from a QueryVersion request.
|
||||||
|
type QueryVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
ServerMajorVersion uint16
|
||||||
|
ServerMinorVersion uint16
|
||||||
|
// padding: 20 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a QueryVersion request.
|
||||||
|
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return queryVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionReply reads a byte slice into a QueryVersionReply value.
|
||||||
|
func queryVersionReply(buf []byte) *QueryVersionReply {
|
||||||
|
v := new(QueryVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.ServerMajorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.ServerMinorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
b += 20 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for QueryVersion
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion uint16, ClientMinorVersion uint16) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XEVIE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], ClientMajorVersion)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], ClientMinorVersion)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectInputCookie is a cookie used only for SelectInput requests.
|
||||||
|
type SelectInputCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectInput sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling SelectInputCookie.Reply()
|
||||||
|
func SelectInput(c *xgb.Conn, EventMask uint32) SelectInputCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'SelectInput' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(selectInputRequest(c, EventMask), cookie)
|
||||||
|
return SelectInputCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectInputUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func SelectInputUnchecked(c *xgb.Conn, EventMask uint32) SelectInputCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'SelectInput' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(selectInputRequest(c, EventMask), cookie)
|
||||||
|
return SelectInputCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectInputReply represents the data returned from a SelectInput request.
|
||||||
|
type SelectInputReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
// padding: 24 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a SelectInput request.
|
||||||
|
func (cook SelectInputCookie) Reply() (*SelectInputReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return selectInputReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// selectInputReply reads a byte slice into a SelectInputReply value.
|
||||||
|
func selectInputReply(buf []byte) *SelectInputReply {
|
||||||
|
v := new(SelectInputReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 24 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for SelectInput
|
||||||
|
// selectInputRequest writes a SelectInput request to a byte slice.
|
||||||
|
func selectInputRequest(c *xgb.Conn, EventMask uint32) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XEVIE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 4 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], EventMask)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendCookie is a cookie used only for Send requests.
|
||||||
|
type SendCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling SendCookie.Reply()
|
||||||
|
func Send(c *xgb.Conn, Event Event, DataType uint32) SendCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Send' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(sendRequest(c, Event, DataType), cookie)
|
||||||
|
return SendCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func SendUnchecked(c *xgb.Conn, Event Event, DataType uint32) SendCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Send' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(sendRequest(c, Event, DataType), cookie)
|
||||||
|
return SendCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendReply represents the data returned from a Send request.
|
||||||
|
type SendReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
// padding: 24 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a Send request.
|
||||||
|
func (cook SendCookie) Reply() (*SendReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return sendReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendReply reads a byte slice into a SendReply value.
|
||||||
|
func sendReply(buf []byte) *SendReply {
|
||||||
|
v := new(SendReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 24 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Send
|
||||||
|
// sendRequest writes a Send request to a byte slice.
|
||||||
|
func sendRequest(c *xgb.Conn, Event Event, DataType uint32) []byte {
|
||||||
|
size := 104
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XEVIE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 3 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
{
|
||||||
|
structBytes := Event.Bytes()
|
||||||
|
copy(buf[b:], structBytes)
|
||||||
|
b += len(structBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], DataType)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 64 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartCookie is a cookie used only for Start requests.
|
||||||
|
type StartCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling StartCookie.Reply()
|
||||||
|
func Start(c *xgb.Conn, Screen uint32) StartCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Start' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(startRequest(c, Screen), cookie)
|
||||||
|
return StartCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func StartUnchecked(c *xgb.Conn, Screen uint32) StartCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XEVIE"]; !ok {
|
||||||
|
panic("Cannot issue request 'Start' using the uninitialized extension 'XEVIE'. xevie.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(startRequest(c, Screen), cookie)
|
||||||
|
return StartCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartReply represents the data returned from a Start request.
|
||||||
|
type StartReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
// padding: 24 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a Start request.
|
||||||
|
func (cook StartCookie) Reply() (*StartReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return startReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// startReply reads a byte slice into a StartReply value.
|
||||||
|
func startReply(buf []byte) *StartReply {
|
||||||
|
v := new(StartReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 24 // padding
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for Start
|
||||||
|
// startRequest writes a Start request to a byte slice.
|
||||||
|
func startRequest(c *xgb.Conn, Screen uint32) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XEVIE"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 1 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Screen)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,554 @@
|
||||||
|
package xgb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Where to log error-messages. Defaults to stderr.
|
||||||
|
// To disable logging, just set this to log.New(ioutil.Discard, "", 0)
|
||||||
|
Logger = log.New(os.Stderr, "XGB: ", log.Lshortfile)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// cookieBuffer represents the queue size of cookies existing at any
|
||||||
|
// point in time. The size of the buffer is really only important when
|
||||||
|
// there are many requests without replies made in sequence. Once the
|
||||||
|
// buffer fills, a round trip request is made to clear the buffer.
|
||||||
|
cookieBuffer = 1000
|
||||||
|
|
||||||
|
// xidBuffer represents the queue size of the xid channel.
|
||||||
|
// I don't think this value matters much, since xid generation is not
|
||||||
|
// that expensive.
|
||||||
|
xidBuffer = 5
|
||||||
|
|
||||||
|
// seqBuffer represents the queue size of the sequence number channel.
|
||||||
|
// I don't think this value matters much, since sequence number generation
|
||||||
|
// is not that expensive.
|
||||||
|
seqBuffer = 5
|
||||||
|
|
||||||
|
// reqBuffer represents the queue size of the number of requests that
|
||||||
|
// can be made until new ones block. This value seems OK.
|
||||||
|
reqBuffer = 100
|
||||||
|
|
||||||
|
// eventBuffer represents the queue size of the number of events or errors
|
||||||
|
// that can be loaded off the wire and not grabbed with WaitForEvent
|
||||||
|
// until reading an event blocks. This value should be big enough to handle
|
||||||
|
// bursts of events.
|
||||||
|
eventBuffer = 5000
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Conn represents a connection to an X server.
|
||||||
|
type Conn struct {
|
||||||
|
host string
|
||||||
|
conn net.Conn
|
||||||
|
display string
|
||||||
|
DisplayNumber int
|
||||||
|
DefaultScreen int
|
||||||
|
SetupBytes []byte
|
||||||
|
|
||||||
|
setupResourceIdBase uint32
|
||||||
|
setupResourceIdMask uint32
|
||||||
|
|
||||||
|
eventChan chan eventOrError
|
||||||
|
cookieChan chan *Cookie
|
||||||
|
xidChan chan xid
|
||||||
|
seqChan chan uint16
|
||||||
|
reqChan chan *request
|
||||||
|
closing chan chan struct{}
|
||||||
|
|
||||||
|
// ExtLock is a lock used whenever new extensions are initialized.
|
||||||
|
// It should not be used. It is exported for use in the extension
|
||||||
|
// sub-packages.
|
||||||
|
ExtLock sync.RWMutex
|
||||||
|
|
||||||
|
// Extensions is a map from extension name to major opcode. It should
|
||||||
|
// not be used. It is exported for use in the extension sub-packages.
|
||||||
|
Extensions map[string]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConn creates a new connection instance. It initializes locks, data
|
||||||
|
// structures, and performs the initial handshake. (The code for the handshake
|
||||||
|
// has been relegated to conn.go.)
|
||||||
|
func NewConn() (*Conn, error) {
|
||||||
|
return NewConnDisplay("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConnDisplay is just like NewConn, but allows a specific DISPLAY
|
||||||
|
// string to be used.
|
||||||
|
// If 'display' is empty it will be taken from os.Getenv("DISPLAY").
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// NewConn(":1") -> net.Dial("unix", "", "/tmp/.X11-unix/X1")
|
||||||
|
// NewConn("/tmp/launch-12/:0") -> net.Dial("unix", "", "/tmp/launch-12/:0")
|
||||||
|
// NewConn("hostname:2.1") -> net.Dial("tcp", "", "hostname:6002")
|
||||||
|
// NewConn("tcp/hostname:1.0") -> net.Dial("tcp", "", "hostname:6001")
|
||||||
|
func NewConnDisplay(display string) (*Conn, error) {
|
||||||
|
conn := &Conn{}
|
||||||
|
|
||||||
|
// First connect. This reads authority, checks DISPLAY environment
|
||||||
|
// variable, and loads the initial Setup info.
|
||||||
|
err := conn.connect(display)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return postNewConn(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConnDisplay is just like NewConn, but allows a specific net.Conn
|
||||||
|
// to be used.
|
||||||
|
func NewConnNet(netConn net.Conn) (*Conn, error) {
|
||||||
|
conn := &Conn{}
|
||||||
|
|
||||||
|
// First connect. This reads authority, checks DISPLAY environment
|
||||||
|
// variable, and loads the initial Setup info.
|
||||||
|
err := conn.connectNet(netConn)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return postNewConn(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func postNewConn(conn *Conn) (*Conn, error) {
|
||||||
|
conn.Extensions = make(map[string]byte)
|
||||||
|
|
||||||
|
conn.cookieChan = make(chan *Cookie, cookieBuffer)
|
||||||
|
conn.xidChan = make(chan xid, xidBuffer)
|
||||||
|
conn.seqChan = make(chan uint16, seqBuffer)
|
||||||
|
conn.reqChan = make(chan *request, reqBuffer)
|
||||||
|
conn.eventChan = make(chan eventOrError, eventBuffer)
|
||||||
|
conn.closing = make(chan chan struct{}, 1)
|
||||||
|
|
||||||
|
go conn.generateXIds()
|
||||||
|
go conn.generateSeqIds()
|
||||||
|
go conn.sendRequests()
|
||||||
|
go conn.readResponses()
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close gracefully closes the connection to the X server.
|
||||||
|
func (c *Conn) Close() {
|
||||||
|
close(c.reqChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
Bytes() []byte
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEventFun is the type of function use to construct events from raw bytes.
|
||||||
|
// It should not be used. It is exported for use in the extension sub-packages.
|
||||||
|
type NewEventFun func(buf []byte) Event
|
||||||
|
|
||||||
|
// NewEventFuncs is a map from event numbers to functions that create
|
||||||
|
// the corresponding event. It should not be used. It is exported for use
|
||||||
|
// in the extension sub-packages.
|
||||||
|
var NewEventFuncs = make(map[int]NewEventFun)
|
||||||
|
|
||||||
|
// NewExtEventFuncs is a temporary map that stores event constructor functions
|
||||||
|
// for each extension. When an extension is initialized, each event for that
|
||||||
|
// extension is added to the 'NewEventFuncs' map. It should not be used. It is
|
||||||
|
// exported for use in the extension sub-packages.
|
||||||
|
var NewExtEventFuncs = make(map[string]map[int]NewEventFun)
|
||||||
|
|
||||||
|
// Error is an interface that can contain any of the errors returned by
|
||||||
|
// the server. Use a type assertion switch to extract the Error structs.
|
||||||
|
type Error interface {
|
||||||
|
SequenceId() uint16
|
||||||
|
BadId() uint32
|
||||||
|
Error() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrorFun is the type of function use to construct errors from raw bytes.
|
||||||
|
// It should not be used. It is exported for use in the extension sub-packages.
|
||||||
|
type NewErrorFun func(buf []byte) Error
|
||||||
|
|
||||||
|
// NewErrorFuncs is a map from error numbers to functions that create
|
||||||
|
// the corresponding error. It should not be used. It is exported for use in
|
||||||
|
// the extension sub-packages.
|
||||||
|
var NewErrorFuncs = make(map[int]NewErrorFun)
|
||||||
|
|
||||||
|
// NewExtErrorFuncs is a temporary map that stores error constructor functions
|
||||||
|
// for each extension. When an extension is initialized, each error for that
|
||||||
|
// extension is added to the 'NewErrorFuncs' map. It should not be used. It is
|
||||||
|
// exported for use in the extension sub-packages.
|
||||||
|
var NewExtErrorFuncs = make(map[string]map[int]NewErrorFun)
|
||||||
|
|
||||||
|
// eventOrError corresponds to values that can be either an event or an
|
||||||
|
// error.
|
||||||
|
type eventOrError interface{}
|
||||||
|
|
||||||
|
// NewId generates a new unused ID for use with requests like CreateWindow.
|
||||||
|
// If no new ids can be generated, the id returned is 0 and error is non-nil.
|
||||||
|
// This shouldn't be used directly, and is exported for use in the extension
|
||||||
|
// sub-packages.
|
||||||
|
// If you need identifiers, use the appropriate constructor.
|
||||||
|
// e.g., For a window id, use xproto.NewWindowId. For
|
||||||
|
// a new pixmap id, use xproto.NewPixmapId. And so on.
|
||||||
|
func (c *Conn) NewId() (uint32, error) {
|
||||||
|
xid := <-c.xidChan
|
||||||
|
if xid.err != nil {
|
||||||
|
return 0, xid.err
|
||||||
|
}
|
||||||
|
return xid.id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// xid encapsulates a resource identifier being sent over the Conn.xidChan
|
||||||
|
// channel. If no new resource id can be generated, id is set to 0 and a
|
||||||
|
// non-nil error is set in xid.err.
|
||||||
|
type xid struct {
|
||||||
|
id uint32
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateXids sends new Ids down the channel for NewId to use.
|
||||||
|
// generateXids should be run in its own goroutine.
|
||||||
|
// This needs to be updated to use the XC Misc extension once we run out of
|
||||||
|
// new ids.
|
||||||
|
// Thanks to libxcb/src/xcb_xid.c. This code is greatly inspired by it.
|
||||||
|
func (conn *Conn) generateXIds() {
|
||||||
|
defer close(conn.xidChan)
|
||||||
|
|
||||||
|
// This requires some explanation. From the horse's mouth:
|
||||||
|
// "The resource-id-mask contains a single contiguous set of bits (at least
|
||||||
|
// 18). The client allocates resource IDs for types WINDOW, PIXMAP,
|
||||||
|
// CURSOR, FONT, GCONTEXT, and COLORMAP by choosing a value with only some
|
||||||
|
// subset of these bits set and ORing it with resource-id-base. Only values
|
||||||
|
// constructed in this way can be used to name newly created resources over
|
||||||
|
// this connection."
|
||||||
|
// So for example (using 8 bit integers), the mask might look like:
|
||||||
|
// 00111000
|
||||||
|
// So that valid values would be 00101000, 00110000, 00001000, and so on.
|
||||||
|
// Thus, the idea is to increment it by the place of the last least
|
||||||
|
// significant '1'. In this case, that value would be 00001000. To get
|
||||||
|
// that value, we can AND the original mask with its two's complement:
|
||||||
|
// 00111000 & 11001000 = 00001000.
|
||||||
|
// And we use that value to increment the last resource id to get a new one.
|
||||||
|
// (And then, of course, we OR it with resource-id-base.)
|
||||||
|
inc := conn.setupResourceIdMask & -conn.setupResourceIdMask
|
||||||
|
max := conn.setupResourceIdMask
|
||||||
|
last := uint32(0)
|
||||||
|
for {
|
||||||
|
// TODO: Use the XC Misc extension to look for released ids.
|
||||||
|
if last > 0 && last >= max-inc+1 {
|
||||||
|
conn.xidChan <- xid{
|
||||||
|
id: 0,
|
||||||
|
err: errors.New("There are no more available resource" +
|
||||||
|
"identifiers."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last += inc
|
||||||
|
conn.xidChan <- xid{
|
||||||
|
id: last | conn.setupResourceIdBase,
|
||||||
|
err: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSeqId fetches the next sequence id from the Conn.seqChan channel.
|
||||||
|
func (c *Conn) newSequenceId() uint16 {
|
||||||
|
return <-c.seqChan
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateSeqIds returns new sequence ids. It is meant to be run in its
|
||||||
|
// own goroutine.
|
||||||
|
// A sequence id is generated for *every* request. It's the identifier used
|
||||||
|
// to match up replies with requests.
|
||||||
|
// Since sequence ids can only be 16 bit integers we start over at zero when it
|
||||||
|
// comes time to wrap.
|
||||||
|
// N.B. As long as the cookie buffer is less than 2^16, there are no limitations
|
||||||
|
// on the number (or kind) of requests made in sequence.
|
||||||
|
func (c *Conn) generateSeqIds() {
|
||||||
|
defer close(c.seqChan)
|
||||||
|
|
||||||
|
seqid := uint16(1)
|
||||||
|
for {
|
||||||
|
c.seqChan <- seqid
|
||||||
|
if seqid == uint16((1<<16)-1) {
|
||||||
|
seqid = 0
|
||||||
|
} else {
|
||||||
|
seqid++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// request encapsulates a buffer of raw bytes (containing the request data)
|
||||||
|
// and a cookie, which when combined represents a single request.
|
||||||
|
// The cookie is used to match up the reply/error.
|
||||||
|
type request struct {
|
||||||
|
buf []byte
|
||||||
|
cookie *Cookie
|
||||||
|
|
||||||
|
// seq is closed when the request (cookie) has been sequenced by the Conn.
|
||||||
|
seq chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest takes the bytes and a cookie of a particular request, constructs
|
||||||
|
// a request type, and sends it over the Conn.reqChan channel.
|
||||||
|
// Note that the sequence number is added to the cookie after it is sent
|
||||||
|
// over the request channel, but before it is sent to X.
|
||||||
|
//
|
||||||
|
// Note that you may safely use NewRequest to send arbitrary byte requests
|
||||||
|
// to X. The resulting cookie can be used just like any normal cookie and
|
||||||
|
// abides by the same rules, except that for replies, you'll get back the
|
||||||
|
// raw byte data. This may be useful for performance critical sections where
|
||||||
|
// every allocation counts, since all X requests in XGB allocate a new byte
|
||||||
|
// slice. In contrast, NewRequest allocates one small request struct and
|
||||||
|
// nothing else. (Except when the cookie buffer is full and has to be flushed.)
|
||||||
|
//
|
||||||
|
// If you're using NewRequest manually, you'll need to use NewCookie to create
|
||||||
|
// a new cookie.
|
||||||
|
//
|
||||||
|
// In all likelihood, you should be able to copy and paste with some minor
|
||||||
|
// edits the generated code for the request you want to issue.
|
||||||
|
func (c *Conn) NewRequest(buf []byte, cookie *Cookie) {
|
||||||
|
seq := make(chan struct{})
|
||||||
|
c.reqChan <- &request{buf: buf, cookie: cookie, seq: seq}
|
||||||
|
<-seq
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendRequests is run as a single goroutine that takes requests and writes
|
||||||
|
// the bytes to the wire and adds the cookie to the cookie queue.
|
||||||
|
// It is meant to be run as its own goroutine.
|
||||||
|
func (c *Conn) sendRequests() {
|
||||||
|
defer close(c.cookieChan)
|
||||||
|
|
||||||
|
for req := range c.reqChan {
|
||||||
|
// ho there! if the cookie channel is nearly full, force a round
|
||||||
|
// trip to clear out the cookie buffer.
|
||||||
|
// Note that we circumvent the request channel, because we're *in*
|
||||||
|
// the request channel.
|
||||||
|
if len(c.cookieChan) == cookieBuffer-1 {
|
||||||
|
if err := c.noop(); err != nil {
|
||||||
|
// Shut everything down.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.cookie.Sequence = c.newSequenceId()
|
||||||
|
c.cookieChan <- req.cookie
|
||||||
|
c.writeBuffer(req.buf)
|
||||||
|
close(req.seq)
|
||||||
|
}
|
||||||
|
response := make(chan struct{})
|
||||||
|
c.closing <- response
|
||||||
|
c.noop() // Flush the response reading goroutine, ignore error.
|
||||||
|
<-response
|
||||||
|
c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// noop circumvents the usual request sending goroutines and forces a round
|
||||||
|
// trip request manually.
|
||||||
|
func (c *Conn) noop() error {
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
cookie.Sequence = c.newSequenceId()
|
||||||
|
c.cookieChan <- cookie
|
||||||
|
if err := c.writeBuffer(c.getInputFocusRequest()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cookie.Reply() // wait for the buffer to clear
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeBuffer is a convenience function for writing a byte slice to the wire.
|
||||||
|
func (c *Conn) writeBuffer(buf []byte) error {
|
||||||
|
if _, err := c.conn.Write(buf); err != nil {
|
||||||
|
Logger.Printf("A write error is unrecoverable: %s", err)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readResponses is a goroutine that reads events, errors and
|
||||||
|
// replies off the wire.
|
||||||
|
// When an event is read, it is always added to the event channel.
|
||||||
|
// When an error is read, if it corresponds to an existing checked cookie,
|
||||||
|
// it is sent to that cookie's error channel. Otherwise it is added to the
|
||||||
|
// event channel.
|
||||||
|
// When a reply is read, it is added to the corresponding cookie's reply
|
||||||
|
// channel. (It is an error if no such cookie exists in this case.)
|
||||||
|
// Finally, cookies that came "before" this reply are always cleaned up.
|
||||||
|
func (c *Conn) readResponses() {
|
||||||
|
defer close(c.eventChan)
|
||||||
|
|
||||||
|
var (
|
||||||
|
err Error
|
||||||
|
seq uint16
|
||||||
|
replyBytes []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case respond := <-c.closing:
|
||||||
|
respond <- struct{}{}
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
err, seq = nil, 0
|
||||||
|
if _, err := io.ReadFull(c.conn, buf); err != nil {
|
||||||
|
Logger.Printf("A read error is unrecoverable: %s", err)
|
||||||
|
c.eventChan <- err
|
||||||
|
c.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch buf[0] {
|
||||||
|
case 0: // This is an error
|
||||||
|
// Use the constructor function for this error (that is auto
|
||||||
|
// generated) by looking it up by the error number.
|
||||||
|
newErrFun, ok := NewErrorFuncs[int(buf[1])]
|
||||||
|
if !ok {
|
||||||
|
Logger.Printf("BUG: Could not find error constructor function "+
|
||||||
|
"for error with number %d.", buf[1])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = newErrFun(buf)
|
||||||
|
seq = err.SequenceId()
|
||||||
|
|
||||||
|
// This error is either sent to the event channel or a specific
|
||||||
|
// cookie's error channel below.
|
||||||
|
case 1: // This is a reply
|
||||||
|
seq = Get16(buf[2:])
|
||||||
|
|
||||||
|
// check to see if this reply has more bytes to be read
|
||||||
|
size := Get32(buf[4:])
|
||||||
|
if size > 0 {
|
||||||
|
byteCount := 32 + size*4
|
||||||
|
biggerBuf := make([]byte, byteCount)
|
||||||
|
copy(biggerBuf[:32], buf)
|
||||||
|
if _, err := io.ReadFull(c.conn, biggerBuf[32:]); err != nil {
|
||||||
|
Logger.Printf("A read error is unrecoverable: %s", err)
|
||||||
|
c.eventChan <- err
|
||||||
|
c.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
replyBytes = biggerBuf
|
||||||
|
} else {
|
||||||
|
replyBytes = buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// This reply is sent to its corresponding cookie below.
|
||||||
|
default: // This is an event
|
||||||
|
// Use the constructor function for this event (like for errors,
|
||||||
|
// and is also auto generated) by looking it up by the event number.
|
||||||
|
// Note that we AND the event number with 127 so that we ignore
|
||||||
|
// the most significant bit (which is set when it was sent from
|
||||||
|
// a SendEvent request).
|
||||||
|
evNum := int(buf[0] & 127)
|
||||||
|
newEventFun, ok := NewEventFuncs[evNum]
|
||||||
|
if !ok {
|
||||||
|
Logger.Printf("BUG: Could not find event construct function "+
|
||||||
|
"for event with number %d.", evNum)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.eventChan <- newEventFun(buf)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we have a sequence number and we're either
|
||||||
|
// processing an error or a reply, which are both responses to
|
||||||
|
// requests. So all we have to do is find the cookie corresponding
|
||||||
|
// to this error/reply, and send the appropriate data to it.
|
||||||
|
// In doing so, we make sure that any cookies that came before it
|
||||||
|
// are marked as successful if they are void and checked.
|
||||||
|
// If there's a cookie that requires a reply that is before this
|
||||||
|
// reply, then something is wrong.
|
||||||
|
for cookie := range c.cookieChan {
|
||||||
|
// This is the cookie we're looking for. Process and break.
|
||||||
|
if cookie.Sequence == seq {
|
||||||
|
if err != nil { // this is an error to a request
|
||||||
|
// synchronous processing
|
||||||
|
if cookie.errorChan != nil {
|
||||||
|
cookie.errorChan <- err
|
||||||
|
} else { // asynchronous processing
|
||||||
|
c.eventChan <- err
|
||||||
|
// if this is an unchecked reply, ping the cookie too
|
||||||
|
if cookie.pingChan != nil {
|
||||||
|
cookie.pingChan <- true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // this is a reply
|
||||||
|
if cookie.replyChan == nil {
|
||||||
|
Logger.Printf("Reply with sequence id %d does not "+
|
||||||
|
"have a cookie with a valid reply channel.", seq)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
cookie.replyChan <- replyBytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// Checked requests with replies
|
||||||
|
case cookie.replyChan != nil && cookie.errorChan != nil:
|
||||||
|
Logger.Printf("Found cookie with sequence id %d that is "+
|
||||||
|
"expecting a reply but will never get it. Currently "+
|
||||||
|
"on sequence number %d", cookie.Sequence, seq)
|
||||||
|
// Unchecked requests with replies
|
||||||
|
case cookie.replyChan != nil && cookie.pingChan != nil:
|
||||||
|
Logger.Printf("Found cookie with sequence id %d that is "+
|
||||||
|
"expecting a reply (and not an error) but will never "+
|
||||||
|
"get it. Currently on sequence number %d",
|
||||||
|
cookie.Sequence, seq)
|
||||||
|
// Checked requests without replies
|
||||||
|
case cookie.pingChan != nil && cookie.errorChan != nil:
|
||||||
|
cookie.pingChan <- true
|
||||||
|
// Unchecked requests without replies don't have any channels,
|
||||||
|
// so we can't do anything with them except let them pass by.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// processEventOrError takes an eventOrError, type switches on it,
|
||||||
|
// and returns it in Go idiomatic style.
|
||||||
|
func processEventOrError(everr eventOrError) (Event, Error) {
|
||||||
|
switch ee := everr.(type) {
|
||||||
|
case Event:
|
||||||
|
return ee, nil
|
||||||
|
case Error:
|
||||||
|
return nil, ee
|
||||||
|
default:
|
||||||
|
Logger.Printf("Invalid event/error type: %T", everr)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForEvent returns the next event from the server.
|
||||||
|
// It will block until an event is available.
|
||||||
|
// WaitForEvent returns either an Event or an Error. (Returning both
|
||||||
|
// is a bug.) Note than an Error here is an X error and not an XGB error. That
|
||||||
|
// is, X errors are sometimes completely expected (and you may want to ignore
|
||||||
|
// them in some cases).
|
||||||
|
//
|
||||||
|
// If both the event and error are nil, then the connection has been closed.
|
||||||
|
func (c *Conn) WaitForEvent() (Event, Error) {
|
||||||
|
return processEventOrError(<-c.eventChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PollForEvent returns the next event from the server if one is available in
|
||||||
|
// the internal queue without blocking. Note that unlike WaitForEvent, both
|
||||||
|
// Event and Error could be nil. Indeed, they are both nil when the event queue
|
||||||
|
// is empty.
|
||||||
|
func (c *Conn) PollForEvent() (Event, Error) {
|
||||||
|
select {
|
||||||
|
case everr := <-c.eventChan:
|
||||||
|
return processEventOrError(everr)
|
||||||
|
default:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
Version 2, December 2004
|
||||||
|
|
||||||
|
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
copies of this license document, and changing it is allowed as long
|
||||||
|
as the name is changed.
|
||||||
|
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
@ -0,0 +1,168 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context represents the protocol we're converting to Go, and a writer
|
||||||
|
// buffer to write the Go source to.
|
||||||
|
type Context struct {
|
||||||
|
protocol *Protocol
|
||||||
|
out *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContext() *Context {
|
||||||
|
return &Context{
|
||||||
|
out: bytes.NewBuffer([]byte{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Putln calls put and adds a new line to the end of 'format'.
|
||||||
|
func (c *Context) Putln(format string, v ...interface{}) {
|
||||||
|
c.Put(format+"\n", v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put is a short alias to write to 'out'.
|
||||||
|
func (c *Context) Put(format string, v ...interface{}) {
|
||||||
|
_, err := c.out.WriteString(fmt.Sprintf(format, v...))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("There was an error writing to context buffer: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Morph is the big daddy of them all. It takes in an XML byte slice,
|
||||||
|
// parse it, transforms the XML types into more usable types,
|
||||||
|
// and writes Go code to the 'out' buffer.
|
||||||
|
func (c *Context) Morph(xmlBytes []byte) {
|
||||||
|
parsedXml := &XML{}
|
||||||
|
err := xml.Unmarshal(xmlBytes, parsedXml)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse all imports
|
||||||
|
parsedXml.Imports.Eval()
|
||||||
|
|
||||||
|
// Translate XML types to nice types
|
||||||
|
c.protocol = parsedXml.Translate(nil)
|
||||||
|
|
||||||
|
// For backwards compatibility we patch the type of the send_event field of
|
||||||
|
// PutImage to be byte
|
||||||
|
if c.protocol.Name == "shm" {
|
||||||
|
for _, req := range c.protocol.Requests {
|
||||||
|
if req.xmlName != "PutImage" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, ifield := range req.Fields {
|
||||||
|
field, ok := ifield.(*SingleField)
|
||||||
|
if !ok || field.xmlName != "send_event" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
field.Type = &Base{ srcName: "byte", xmlName: "CARD8", size: newFixedSize(1, true) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with Go header.
|
||||||
|
c.Putln("// Package %s is the X client API for the %s extension.",
|
||||||
|
c.protocol.PkgName(), c.protocol.ExtXName)
|
||||||
|
c.Putln("package %s", c.protocol.PkgName())
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("// This file is automatically generated from %s.xml. "+
|
||||||
|
"Edit at your peril!", c.protocol.Name)
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Write imports. We always need to import at least xgb.
|
||||||
|
// We also need to import xproto if it's an extension.
|
||||||
|
c.Putln("import (")
|
||||||
|
c.Putln("\"github.com/BurntSushi/xgb\"")
|
||||||
|
c.Putln("")
|
||||||
|
if c.protocol.isExt() {
|
||||||
|
c.Putln("\"github.com/BurntSushi/xgb/xproto\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(Protocols(c.protocol.Imports))
|
||||||
|
for _, imp := range c.protocol.Imports {
|
||||||
|
// We always import xproto, so skip it if it's explicitly imported
|
||||||
|
if imp.Name == "xproto" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.Putln("\"github.com/BurntSushi/xgb/%s\"", imp.Name)
|
||||||
|
}
|
||||||
|
c.Putln(")")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// If this is an extension, create a function to initialize the extension
|
||||||
|
// before it can be used.
|
||||||
|
if c.protocol.isExt() {
|
||||||
|
xname := c.protocol.ExtXName
|
||||||
|
|
||||||
|
c.Putln("// Init must be called before using the %s extension.",
|
||||||
|
xname)
|
||||||
|
c.Putln("func Init(c *xgb.Conn) error {")
|
||||||
|
c.Putln("reply, err := xproto.QueryExtension(c, %d, \"%s\").Reply()",
|
||||||
|
len(xname), xname)
|
||||||
|
c.Putln("switch {")
|
||||||
|
c.Putln("case err != nil:")
|
||||||
|
c.Putln("return err")
|
||||||
|
c.Putln("case !reply.Present:")
|
||||||
|
c.Putln("return xgb.Errorf(\"No extension named %s could be found on "+
|
||||||
|
"on the server.\")", xname)
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("c.ExtLock.Lock()")
|
||||||
|
c.Putln("c.Extensions[\"%s\"] = reply.MajorOpcode", xname)
|
||||||
|
c.Putln("c.ExtLock.Unlock()")
|
||||||
|
c.Putln("for evNum, fun := range xgb.NewExtEventFuncs[\"%s\"] {",
|
||||||
|
xname)
|
||||||
|
c.Putln("xgb.NewEventFuncs[int(reply.FirstEvent) + evNum] = fun")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("for errNum, fun := range xgb.NewExtErrorFuncs[\"%s\"] {",
|
||||||
|
xname)
|
||||||
|
c.Putln("xgb.NewErrorFuncs[int(reply.FirstError) + errNum] = fun")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("return nil")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Make sure newExtEventFuncs["EXT_NAME"] map is initialized.
|
||||||
|
// Same deal for newExtErrorFuncs["EXT_NAME"]
|
||||||
|
c.Putln("func init() {")
|
||||||
|
c.Putln("xgb.NewExtEventFuncs[\"%s\"] = make(map[int]xgb.NewEventFun)",
|
||||||
|
xname)
|
||||||
|
c.Putln("xgb.NewExtErrorFuncs[\"%s\"] = make(map[int]xgb.NewErrorFun)",
|
||||||
|
xname)
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
} else {
|
||||||
|
// In the xproto package, we must provide a Setup function that uses
|
||||||
|
// SetupBytes in xgb.Conn to return a SetupInfo structure.
|
||||||
|
c.Putln("// Setup parses the setup bytes retrieved when")
|
||||||
|
c.Putln("// connecting into a SetupInfo struct.")
|
||||||
|
c.Putln("func Setup(c *xgb.Conn) *SetupInfo {")
|
||||||
|
c.Putln("setup := new(SetupInfo)")
|
||||||
|
c.Putln("SetupInfoRead(c.SetupBytes, setup)")
|
||||||
|
c.Putln("return setup")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("// DefaultScreen gets the default screen info from SetupInfo.")
|
||||||
|
c.Putln("func (s *SetupInfo) DefaultScreen(c *xgb.Conn) *ScreenInfo {")
|
||||||
|
c.Putln("return &s.Roots[c.DefaultScreen]")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now write Go source code
|
||||||
|
sort.Sort(Types(c.protocol.Types))
|
||||||
|
sort.Sort(Requests(c.protocol.Requests))
|
||||||
|
for _, typ := range c.protocol.Types {
|
||||||
|
typ.Define(c)
|
||||||
|
}
|
||||||
|
for _, req := range c.protocol.Requests {
|
||||||
|
req.Define(c)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
xgbgen constructs Go source files from xproto XML description files. xgbgen
|
||||||
|
accomplishes the same task as the Python code generator for XCB and xpyb.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
xgbgen [flags] some-protocol.xml
|
||||||
|
|
||||||
|
The flags are:
|
||||||
|
--proto-path path
|
||||||
|
The path to a directory containing xproto XML description files.
|
||||||
|
This is only necessary when 'some-protocol.xml' imports other
|
||||||
|
protocol files.
|
||||||
|
--gofmt=true
|
||||||
|
When false, the outputted Go code will not be gofmt'd. And it won't
|
||||||
|
be very pretty at all. This is typically useful if there are syntax
|
||||||
|
errors that need to be debugged in code generation. gofmt will hiccup;
|
||||||
|
this will allow you to see the raw code.
|
||||||
|
|
||||||
|
How it works
|
||||||
|
|
||||||
|
xgbgen works by parsing the input XML file using Go's encoding/xml package.
|
||||||
|
The majority of this work is done in xml.go and xml_fields.go, where the
|
||||||
|
appropriate types are declared.
|
||||||
|
|
||||||
|
Due to the nature of the XML in the protocol description files, the types
|
||||||
|
required to parse the XML are not well suited to reasoning about code
|
||||||
|
generation. Because of this, all data parsed in the XML types is translated
|
||||||
|
into more reasonable types. This translation is done in translation.go,
|
||||||
|
and is mainly grunt work. (The only interesting tidbits are the translation
|
||||||
|
of XML names to Go source names, and connecting fields with their
|
||||||
|
appropriate types.)
|
||||||
|
|
||||||
|
The organization of these types is greatly
|
||||||
|
inspired by the description of the XML found here:
|
||||||
|
http://cgit.freedesktop.org/xcb/proto/tree/doc/xml-xcb.txt
|
||||||
|
|
||||||
|
These types come with a lot of supporting methods to make their use in
|
||||||
|
code generation easier. They can be found in expression.go, field.go,
|
||||||
|
protocol.go, request_reply.go and type.go. Of particular interest are
|
||||||
|
expression evaluation and size calculation (in bytes).
|
||||||
|
|
||||||
|
These types also come with supporting methods that convert their
|
||||||
|
representation into Go source code. I've quartered such methods in
|
||||||
|
go.go, go_error.go, go_event.go, go_list.go, go_request_reply.go,
|
||||||
|
go_single_field.go, go_struct.go and go_union.go. The idea is to keep
|
||||||
|
as much of the Go specific code generation in one area as possible. Namely,
|
||||||
|
while not *all* Go related code is found in the 'go*.go' files, *most*
|
||||||
|
of it is. (If there's any interest in using xgbgen for other languages,
|
||||||
|
I'd be happy to try and make xgbgen a little more friendly in this regard.
|
||||||
|
I did, however, design xgbgen with this in mind, so it shouldn't involve
|
||||||
|
anything as serious as a re-design.)
|
||||||
|
|
||||||
|
Why
|
||||||
|
|
||||||
|
I wrote xgbgen because I found the existing code generator that was written in
|
||||||
|
Python to be unwieldy. In particular, static and strong typing greatly helped
|
||||||
|
me reason better about the code generation task.
|
||||||
|
|
||||||
|
What does not work
|
||||||
|
|
||||||
|
The core X protocol should be completely working. As far as I know, most
|
||||||
|
extensions should work too, although I've only tested (and not much) the
|
||||||
|
Xinerama and RandR extensions.
|
||||||
|
|
||||||
|
XKB does not work. I don't have any real plans of working on this unless there
|
||||||
|
is demand and I have some test cases to work with. (i.e., even if I could get
|
||||||
|
something generated for XKB, I don't have the inclination to understand it
|
||||||
|
enough to verify that it works.) XKB poses several extremely difficult
|
||||||
|
problems that XCB also has trouble with. More info on that can be found at
|
||||||
|
http://cgit.freedesktop.org/xcb/libxcb/tree/doc/xkb_issues and
|
||||||
|
http://cgit.freedesktop.org/xcb/libxcb/tree/doc/xkb_internals.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package main
|
|
@ -0,0 +1,436 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Expression represents all the different forms of expressions possible in
|
||||||
|
// side an XML protocol description file. It's also received a few custom
|
||||||
|
// addendums to make applying special functions (like padding) easier.
|
||||||
|
type Expression interface {
|
||||||
|
// Concrete determines whether this particular expression can be computed
|
||||||
|
// to some constant value inside xgbgen. (The alternative is that the
|
||||||
|
// expression can only be computed with values at run time of the
|
||||||
|
// generated code.)
|
||||||
|
Concrete() bool
|
||||||
|
|
||||||
|
// Eval evaluates a concrete expression. It is an error to call Eval
|
||||||
|
// on any expression that is not concrete (or contains any sub-expression
|
||||||
|
// that is not concrete).
|
||||||
|
Eval() int
|
||||||
|
|
||||||
|
// Reduce attempts to evaluate any concrete sub-expressions.
|
||||||
|
// i.e., (1 + 2 * (5 + 1 + someSizeOfStruct) reduces to
|
||||||
|
// (3 * (6 + someSizeOfStruct)).
|
||||||
|
// 'prefix' is used preprended to any field reference name.
|
||||||
|
Reduce(prefix string) string
|
||||||
|
|
||||||
|
// String is an alias for Reduce("")
|
||||||
|
String() string
|
||||||
|
|
||||||
|
// Initialize makes sure all names in this expression and any subexpressions
|
||||||
|
// have been translated to Go source names.
|
||||||
|
Initialize(p *Protocol)
|
||||||
|
|
||||||
|
// Makes all field references relative to path
|
||||||
|
Specialize(path string) Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function is a custom expression not found in the XML. It's simply used
|
||||||
|
// to apply a function named in 'Name' to the Expr expression.
|
||||||
|
type Function struct {
|
||||||
|
Name string
|
||||||
|
Expr Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Function) Concrete() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Function) Eval() int {
|
||||||
|
log.Fatalf("Cannot evaluate a 'Function'. It is not concrete.")
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Function) Reduce(prefix string) string {
|
||||||
|
return fmt.Sprintf("%s(%s)", e.Name, e.Expr.Reduce(prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Function) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Function) Initialize(p *Protocol) {
|
||||||
|
e.Expr.Initialize(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Function) Specialize(path string) Expression {
|
||||||
|
r := *e
|
||||||
|
r.Expr = r.Expr.Specialize(path)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp is an expression that performs some operation (defined in the XML
|
||||||
|
// file) with Expr1 and Expr2 as operands.
|
||||||
|
type BinaryOp struct {
|
||||||
|
Op string
|
||||||
|
Expr1 Expression
|
||||||
|
Expr2 Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBinaryOp constructs a new binary expression when both expr1 and expr2
|
||||||
|
// are not nil. If one or both are nil, then the non-nil expression is
|
||||||
|
// returned unchanged or nil is returned.
|
||||||
|
func newBinaryOp(op string, expr1, expr2 Expression) Expression {
|
||||||
|
switch {
|
||||||
|
case expr1 != nil && expr2 != nil:
|
||||||
|
return &BinaryOp{
|
||||||
|
Op: op,
|
||||||
|
Expr1: expr1,
|
||||||
|
Expr2: expr2,
|
||||||
|
}
|
||||||
|
case expr1 != nil && expr2 == nil:
|
||||||
|
return expr1
|
||||||
|
case expr1 == nil && expr2 != nil:
|
||||||
|
return expr2
|
||||||
|
case expr1 == nil && expr2 == nil:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BinaryOp) Concrete() bool {
|
||||||
|
return e.Expr1.Concrete() && e.Expr2.Concrete()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BinaryOp) Eval() int {
|
||||||
|
switch e.Op {
|
||||||
|
case "+":
|
||||||
|
return e.Expr1.Eval() + e.Expr2.Eval()
|
||||||
|
case "-":
|
||||||
|
return e.Expr1.Eval() - e.Expr2.Eval()
|
||||||
|
case "*":
|
||||||
|
return e.Expr1.Eval() * e.Expr2.Eval()
|
||||||
|
case "/":
|
||||||
|
return e.Expr1.Eval() / e.Expr2.Eval()
|
||||||
|
case "&":
|
||||||
|
return e.Expr1.Eval() & e.Expr2.Eval()
|
||||||
|
case "<<":
|
||||||
|
return int(uint(e.Expr1.Eval()) << uint(e.Expr2.Eval()))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatalf("Invalid binary operator '%s' for expression.", e.Op)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BinaryOp) Reduce(prefix string) string {
|
||||||
|
if e.Concrete() {
|
||||||
|
return fmt.Sprintf("%d", e.Eval())
|
||||||
|
}
|
||||||
|
|
||||||
|
// An incredibly dirty hack to make sure any time we perform an operation
|
||||||
|
// on a field, we're dealing with ints...
|
||||||
|
expr1, expr2 := e.Expr1, e.Expr2
|
||||||
|
switch expr1.(type) {
|
||||||
|
case *FieldRef:
|
||||||
|
expr1 = &Function{
|
||||||
|
Name: "int",
|
||||||
|
Expr: expr1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch expr2.(type) {
|
||||||
|
case *FieldRef:
|
||||||
|
expr2 = &Function{
|
||||||
|
Name: "int",
|
||||||
|
Expr: expr2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("(%s %s %s)",
|
||||||
|
expr1.Reduce(prefix), e.Op, expr2.Reduce(prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BinaryOp) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BinaryOp) Initialize(p *Protocol) {
|
||||||
|
e.Expr1.Initialize(p)
|
||||||
|
e.Expr2.Initialize(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BinaryOp) Specialize(path string) Expression {
|
||||||
|
r := *e
|
||||||
|
r.Expr1 = r.Expr1.Specialize(path)
|
||||||
|
r.Expr2 = r.Expr2.Specialize(path)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnaryOp is the same as BinaryOp, except it's a unary operator with only
|
||||||
|
// one sub-expression.
|
||||||
|
type UnaryOp struct {
|
||||||
|
Op string
|
||||||
|
Expr Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnaryOp) Concrete() bool {
|
||||||
|
return e.Expr.Concrete()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnaryOp) Eval() int {
|
||||||
|
switch e.Op {
|
||||||
|
case "~":
|
||||||
|
return ^e.Expr.Eval()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatalf("Invalid unary operator '%s' for expression.", e.Op)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnaryOp) Reduce(prefix string) string {
|
||||||
|
if e.Concrete() {
|
||||||
|
return fmt.Sprintf("%d", e.Eval())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("(%s (%s))", e.Op, e.Expr.Reduce(prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnaryOp) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnaryOp) Initialize(p *Protocol) {
|
||||||
|
e.Expr.Initialize(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnaryOp) Specialize(path string) Expression {
|
||||||
|
r := *e
|
||||||
|
r.Expr = r.Expr.Specialize(path)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Padding represents the application of the 'pad' function to some
|
||||||
|
// sub-expression.
|
||||||
|
type Padding struct {
|
||||||
|
Expr Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Padding) Concrete() bool {
|
||||||
|
return e.Expr.Concrete()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Padding) Eval() int {
|
||||||
|
return pad(e.Expr.Eval())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Padding) Reduce(prefix string) string {
|
||||||
|
if e.Concrete() {
|
||||||
|
return fmt.Sprintf("%d", e.Eval())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("xgb.Pad(%s)", e.Expr.Reduce(prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Padding) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Padding) Initialize(p *Protocol) {
|
||||||
|
e.Expr.Initialize(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Padding) Specialize(path string) Expression {
|
||||||
|
r := *e
|
||||||
|
r.Expr = r.Expr.Specialize(path)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopCount represents the application of the 'PopCount' function to
|
||||||
|
// some sub-expression.
|
||||||
|
type PopCount struct {
|
||||||
|
Expr Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PopCount) Concrete() bool {
|
||||||
|
return e.Expr.Concrete()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PopCount) Eval() int {
|
||||||
|
return int(popCount(uint(e.Expr.Eval())))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PopCount) Reduce(prefix string) string {
|
||||||
|
if e.Concrete() {
|
||||||
|
return fmt.Sprintf("%d", e.Eval())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("xgb.PopCount(%s)", e.Expr.Reduce(prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PopCount) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PopCount) Initialize(p *Protocol) {
|
||||||
|
e.Expr.Initialize(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PopCount) Specialize(path string) Expression {
|
||||||
|
r := *e
|
||||||
|
r.Expr = r.Expr.Specialize(path)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value represents some constant integer.
|
||||||
|
type Value struct {
|
||||||
|
v int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Value) Concrete() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Value) Eval() int {
|
||||||
|
return e.v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Value) Reduce(prefix string) string {
|
||||||
|
return fmt.Sprintf("%d", e.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Value) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Value) Initialize(p *Protocol) {}
|
||||||
|
|
||||||
|
func (e *Value) Specialize(path string) Expression {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bit represents some bit whose value is computed by '1 << bit'.
|
||||||
|
type Bit struct {
|
||||||
|
b int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Bit) Concrete() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Bit) Eval() int {
|
||||||
|
return int(1 << uint(e.b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Bit) Reduce(prefix string) string {
|
||||||
|
return fmt.Sprintf("%d", e.Eval())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Bit) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Bit) Initialize(p *Protocol) {}
|
||||||
|
|
||||||
|
func (e *Bit) Specialize(path string) Expression {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldRef represents a reference to some variable in the generated code
|
||||||
|
// with name Name.
|
||||||
|
type FieldRef struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FieldRef) Concrete() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FieldRef) Eval() int {
|
||||||
|
log.Fatalf("Cannot evaluate a 'FieldRef'. It is not concrete.")
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FieldRef) Reduce(prefix string) string {
|
||||||
|
val := e.Name
|
||||||
|
if len(prefix) > 0 {
|
||||||
|
val = fmt.Sprintf("%s%s", prefix, val)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FieldRef) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FieldRef) Initialize(p *Protocol) {
|
||||||
|
e.Name = SrcName(p, e.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FieldRef) Specialize(path string) Expression {
|
||||||
|
return &FieldRef{Name: path + "." + e.Name}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumRef represents a reference to some enumeration field.
|
||||||
|
// EnumKind is the "group" an EnumItem is the name of the specific enumeration
|
||||||
|
// value inside that group.
|
||||||
|
type EnumRef struct {
|
||||||
|
EnumKind Type
|
||||||
|
EnumItem string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EnumRef) Concrete() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EnumRef) Eval() int {
|
||||||
|
log.Fatalf("Cannot evaluate an 'EnumRef'. It is not concrete.")
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EnumRef) Reduce(prefix string) string {
|
||||||
|
return fmt.Sprintf("%s%s", e.EnumKind, e.EnumItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EnumRef) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EnumRef) Initialize(p *Protocol) {
|
||||||
|
e.EnumKind = e.EnumKind.(*Translation).RealType(p)
|
||||||
|
e.EnumItem = SrcName(p, e.EnumItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EnumRef) Specialize(path string) Expression {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// SumOf represents a summation of the variable in the generated code named by
|
||||||
|
// Name. It is not currently used. (It's XKB voodoo.)
|
||||||
|
type SumOf struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SumOf) Concrete() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SumOf) Eval() int {
|
||||||
|
log.Fatalf("Cannot evaluate a 'SumOf'. It is not concrete.")
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SumOf) Reduce(prefix string) string {
|
||||||
|
if len(prefix) > 0 {
|
||||||
|
return fmt.Sprintf("sum(%s%s)", prefix, e.Name)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("sum(%s)", e.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SumOf) String() string {
|
||||||
|
return e.Reduce("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SumOf) Initialize(p *Protocol) {
|
||||||
|
e.Name = SrcName(p, e.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SumOf) Specialize(path string) Expression {
|
||||||
|
return e
|
||||||
|
}
|
|
@ -0,0 +1,398 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Field corresponds to any field described in an XML protocol description
|
||||||
|
// file. This includes struct fields, union fields, request fields,
|
||||||
|
// reply fields and so on.
|
||||||
|
// To make code generation easier, fields that have types are also stored.
|
||||||
|
// Note that not all fields support all methods defined in this interface.
|
||||||
|
// For instance, a padding field does not have a source name.
|
||||||
|
type Field interface {
|
||||||
|
// Initialize sets up the source name of this field.
|
||||||
|
Initialize(p *Protocol)
|
||||||
|
|
||||||
|
// SrcName is the Go source name of this field.
|
||||||
|
SrcName() string
|
||||||
|
|
||||||
|
// XmlName is the name of this field from the XML file.
|
||||||
|
XmlName() string
|
||||||
|
|
||||||
|
// SrcType is the Go source type name of this field.
|
||||||
|
SrcType() string
|
||||||
|
|
||||||
|
// Size returns an expression that computes the size (in bytes)
|
||||||
|
// of this field.
|
||||||
|
Size() Size
|
||||||
|
|
||||||
|
// Define writes the Go code to declare this field (in a struct definition).
|
||||||
|
Define(c *Context)
|
||||||
|
|
||||||
|
// Read writes the Go code to convert a byte slice to a Go value
|
||||||
|
// of this field.
|
||||||
|
// 'prefix' is the prefix of the name of the Go value.
|
||||||
|
Read(c *Context, prefix string)
|
||||||
|
|
||||||
|
// Write writes the Go code to convert a Go value to a byte slice of
|
||||||
|
// this field.
|
||||||
|
// 'prefix' is the prefix of the name of the Go value.
|
||||||
|
Write(c *Context, prefix string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pad *PadField) Initialize(p *Protocol) {}
|
||||||
|
|
||||||
|
// PadField represents any type of padding. It is omitted from
|
||||||
|
// definitions, but is used in Read/Write to increment the buffer index.
|
||||||
|
// It is also used in size calculation.
|
||||||
|
type PadField struct {
|
||||||
|
Bytes uint
|
||||||
|
Align uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PadField) SrcName() string {
|
||||||
|
panic("illegal to take source name of a pad field")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PadField) XmlName() string {
|
||||||
|
panic("illegal to take XML name of a pad field")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *PadField) SrcType() string {
|
||||||
|
panic("it is illegal to call SrcType on a PadField field")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PadField) Size() Size {
|
||||||
|
if p.Align > 0 {
|
||||||
|
return newFixedSize(uint(p.Align), false)
|
||||||
|
} else {
|
||||||
|
return newFixedSize(p.Bytes, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequiredStartAlign struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RequiredStartAlign) Initialize(p *Protocol) {}
|
||||||
|
|
||||||
|
func (f *RequiredStartAlign) SrcName() string {
|
||||||
|
panic("illegal to take source name of a required_start_align field")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RequiredStartAlign) XmlName() string {
|
||||||
|
panic("illegal to take XML name of a required_start_align field")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RequiredStartAlign) SrcType() string {
|
||||||
|
panic("it is illegal to call SrcType on a required_start_align field")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RequiredStartAlign) Size() Size {
|
||||||
|
return newFixedSize(0, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RequiredStartAlign) Define(c *Context) {}
|
||||||
|
|
||||||
|
func (f *RequiredStartAlign) Read(c *Context, prefix string) {}
|
||||||
|
func (f *RequiredStartAlign) Write(c *Context, prefix string) {}
|
||||||
|
|
||||||
|
// SingleField represents most of the fields in an XML protocol description.
|
||||||
|
// It corresponds to any single value.
|
||||||
|
type SingleField struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Type Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SingleField) Initialize(p *Protocol) {
|
||||||
|
f.srcName = SrcName(p, f.XmlName())
|
||||||
|
f.Type = f.Type.(*Translation).RealType(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SingleField) SrcName() string {
|
||||||
|
if f.srcName == "Bytes" {
|
||||||
|
return "Bytes_"
|
||||||
|
}
|
||||||
|
return f.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SingleField) XmlName() string {
|
||||||
|
return f.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SingleField) SrcType() string {
|
||||||
|
return f.Type.SrcName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SingleField) Size() Size {
|
||||||
|
return f.Type.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListField represents a list of values.
|
||||||
|
type ListField struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Type Type
|
||||||
|
LengthExpr Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ListField) SrcName() string {
|
||||||
|
return f.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ListField) XmlName() string {
|
||||||
|
return f.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ListField) SrcType() string {
|
||||||
|
if strings.ToLower(f.Type.XmlName()) == "char" {
|
||||||
|
return fmt.Sprintf("string")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[]%s", f.Type.SrcName())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length computes the *number* of values in a list.
|
||||||
|
// If this ListField does not have any length expression, we throw our hands
|
||||||
|
// up and simply compute the 'len' of the field name of this list.
|
||||||
|
func (f *ListField) Length() Size {
|
||||||
|
if f.LengthExpr == nil {
|
||||||
|
return newExpressionSize(&Function{
|
||||||
|
Name: "len",
|
||||||
|
Expr: &FieldRef{
|
||||||
|
Name: f.SrcName(),
|
||||||
|
},
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
|
return newExpressionSize(f.LengthExpr, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size computes the *size* of a list (in bytes).
|
||||||
|
// It it typically a simple matter of multiplying the length of the list by
|
||||||
|
// the size of the type of the list.
|
||||||
|
// But if it's a list of struct where the struct has a list field, we use a
|
||||||
|
// special function written in go_struct.go to compute the size (since the
|
||||||
|
// size in this case can only be computed recursively).
|
||||||
|
func (f *ListField) Size() Size {
|
||||||
|
elsz := f.Type.Size()
|
||||||
|
simpleLen := &Padding{
|
||||||
|
Expr: newBinaryOp("*", f.Length().Expression, elsz.Expression),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch field := f.Type.(type) {
|
||||||
|
case *Struct:
|
||||||
|
if field.HasList() {
|
||||||
|
sizeFun := &Function{
|
||||||
|
Name: fmt.Sprintf("%sListSize", f.Type.SrcName()),
|
||||||
|
Expr: &FieldRef{Name: f.SrcName()},
|
||||||
|
}
|
||||||
|
return newExpressionSize(sizeFun, elsz.exact)
|
||||||
|
} else {
|
||||||
|
return newExpressionSize(simpleLen, elsz.exact)
|
||||||
|
}
|
||||||
|
case *Union:
|
||||||
|
return newExpressionSize(simpleLen, elsz.exact)
|
||||||
|
case *Base:
|
||||||
|
return newExpressionSize(simpleLen, elsz.exact)
|
||||||
|
case *Resource:
|
||||||
|
return newExpressionSize(simpleLen, elsz.exact)
|
||||||
|
case *TypeDef:
|
||||||
|
return newExpressionSize(simpleLen, elsz.exact)
|
||||||
|
default:
|
||||||
|
log.Panicf("Cannot compute list size with type '%T'.", f.Type)
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ListField) Initialize(p *Protocol) {
|
||||||
|
f.srcName = SrcName(p, f.XmlName())
|
||||||
|
f.Type = f.Type.(*Translation).RealType(p)
|
||||||
|
if f.LengthExpr != nil {
|
||||||
|
f.LengthExpr.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalField is exactly the same as a regular SingleField, except it isn't
|
||||||
|
// sent over the wire. (i.e., it's probably used to compute an ExprField).
|
||||||
|
type LocalField struct {
|
||||||
|
*SingleField
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExprField is a field that is not parameterized, but is computed from values
|
||||||
|
// of other fields.
|
||||||
|
type ExprField struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Type Type
|
||||||
|
Expr Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ExprField) SrcName() string {
|
||||||
|
return f.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ExprField) XmlName() string {
|
||||||
|
return f.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ExprField) SrcType() string {
|
||||||
|
return f.Type.SrcName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ExprField) Size() Size {
|
||||||
|
return f.Type.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ExprField) Initialize(p *Protocol) {
|
||||||
|
f.srcName = SrcName(p, f.XmlName())
|
||||||
|
f.Type = f.Type.(*Translation).RealType(p)
|
||||||
|
f.Expr.Initialize(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueField represents two fields in one: a mask and a list of 4-byte
|
||||||
|
// integers. The mask specifies which kinds of values are in the list.
|
||||||
|
// (i.e., See ConfigureWindow, CreateWindow, ChangeWindowAttributes, etc.)
|
||||||
|
type ValueField struct {
|
||||||
|
Parent interface{}
|
||||||
|
MaskType Type
|
||||||
|
MaskName string
|
||||||
|
ListName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ValueField) SrcName() string {
|
||||||
|
panic("it is illegal to call SrcName on a ValueField field")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ValueField) XmlName() string {
|
||||||
|
panic("it is illegal to call XmlName on a ValueField field")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ValueField) SrcType() string {
|
||||||
|
return f.MaskType.SrcName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size computes the size in bytes of the combination of the mask and list
|
||||||
|
// in this value field.
|
||||||
|
// The expression to compute this looks complicated, but it's really just
|
||||||
|
// the number of bits set in the mask multiplied 4 (and padded of course).
|
||||||
|
func (f *ValueField) Size() Size {
|
||||||
|
maskSize := f.MaskType.Size()
|
||||||
|
listSize := newExpressionSize(&Function{
|
||||||
|
Name: "xgb.Pad",
|
||||||
|
Expr: &BinaryOp{
|
||||||
|
Op: "*",
|
||||||
|
Expr1: &Value{v: 4},
|
||||||
|
Expr2: &PopCount{
|
||||||
|
Expr: &Function{
|
||||||
|
Name: "int",
|
||||||
|
Expr: &FieldRef{
|
||||||
|
Name: f.MaskName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, true)
|
||||||
|
return maskSize.Add(listSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ValueField) ListLength() Size {
|
||||||
|
return newExpressionSize(&PopCount{
|
||||||
|
Expr: &Function{
|
||||||
|
Name: "int",
|
||||||
|
Expr: &FieldRef{
|
||||||
|
Name: f.MaskName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ValueField) Initialize(p *Protocol) {
|
||||||
|
f.MaskType = f.MaskType.(*Translation).RealType(p)
|
||||||
|
f.MaskName = SrcName(p, f.MaskName)
|
||||||
|
f.ListName = SrcName(p, f.ListName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwitchField represents a 'switch' element in the XML protocol description
|
||||||
|
// file.
|
||||||
|
// Currently we translate this to a slice of uint32 and let the user sort
|
||||||
|
// through it.
|
||||||
|
type SwitchField struct {
|
||||||
|
xmlName string
|
||||||
|
Name string
|
||||||
|
MaskName string
|
||||||
|
Expr Expression
|
||||||
|
Bitcases []*Bitcase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SwitchField) SrcName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SwitchField) XmlName() string {
|
||||||
|
return f.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SwitchField) SrcType() string {
|
||||||
|
return "[]uint32"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SwitchField) Size() Size {
|
||||||
|
// TODO: size expression used here is not correct unless every element of
|
||||||
|
// the switch is 32 bit long. This assumption holds for xproto but may not
|
||||||
|
// hold for other protocols (xkb?)
|
||||||
|
|
||||||
|
listSize := newExpressionSize(&Function{
|
||||||
|
Name: "xgb.Pad",
|
||||||
|
Expr: &BinaryOp{
|
||||||
|
Op: "*",
|
||||||
|
Expr1: &Value{v: 4},
|
||||||
|
Expr2: &PopCount{
|
||||||
|
Expr: &Function{
|
||||||
|
Name: "int",
|
||||||
|
Expr: &FieldRef{
|
||||||
|
Name: f.MaskName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
return listSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SwitchField) ListLength() Size {
|
||||||
|
return newExpressionSize(&PopCount{
|
||||||
|
Expr: &Function{
|
||||||
|
Name: "int",
|
||||||
|
Expr: &FieldRef{
|
||||||
|
Name: f.MaskName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SwitchField) Initialize(p *Protocol) {
|
||||||
|
f.xmlName = f.Name
|
||||||
|
f.Name = SrcName(p, f.Name)
|
||||||
|
f.Expr.Initialize(p)
|
||||||
|
fieldref, ok := f.Expr.(*FieldRef)
|
||||||
|
if !ok {
|
||||||
|
panic("switch field's expression not a fieldref")
|
||||||
|
}
|
||||||
|
f.MaskName = SrcName(p, fieldref.Name)
|
||||||
|
for _, bitcase := range f.Bitcases {
|
||||||
|
bitcase.Expr.Initialize(p)
|
||||||
|
for _, field := range bitcase.Fields {
|
||||||
|
field.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitcase represents a single bitcase inside a switch expression.
|
||||||
|
// It is not currently used. (i.e., it's XKB voodoo.)
|
||||||
|
type Bitcase struct {
|
||||||
|
Fields []Field
|
||||||
|
Expr Expression
|
||||||
|
}
|
|
@ -0,0 +1,220 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseTypeMap is a map from X base types to Go types.
|
||||||
|
// X base types should correspond to the smallest set of X types
|
||||||
|
// that can be used to rewrite ALL X types in terms of Go types.
|
||||||
|
// That is, if you remove any of the following types, at least one
|
||||||
|
// XML protocol description will produce an invalid Go program.
|
||||||
|
// The types on the left *never* show themselves in the source.
|
||||||
|
var BaseTypeMap = map[string]string{
|
||||||
|
"CARD8": "byte",
|
||||||
|
"CARD16": "uint16",
|
||||||
|
"CARD32": "uint32",
|
||||||
|
"INT8": "int8",
|
||||||
|
"INT16": "int16",
|
||||||
|
"INT32": "int32",
|
||||||
|
"BYTE": "byte",
|
||||||
|
"BOOL": "bool",
|
||||||
|
"float": "float64",
|
||||||
|
"double": "float64",
|
||||||
|
"char": "byte",
|
||||||
|
"void": "byte",
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseTypeSizes should have precisely the same keys as in BaseTypeMap,
|
||||||
|
// and the values should correspond to the size of the type in bytes.
|
||||||
|
var BaseTypeSizes = map[string]uint{
|
||||||
|
"CARD8": 1,
|
||||||
|
"CARD16": 2,
|
||||||
|
"CARD32": 4,
|
||||||
|
"INT8": 1,
|
||||||
|
"INT16": 2,
|
||||||
|
"INT32": 4,
|
||||||
|
"BYTE": 1,
|
||||||
|
"BOOL": 1,
|
||||||
|
"float": 4,
|
||||||
|
"double": 8,
|
||||||
|
"char": 1,
|
||||||
|
"void": 1,
|
||||||
|
|
||||||
|
// Id is a special type used to determine the size of all Xid types.
|
||||||
|
// "Id" is not actually written in the source.
|
||||||
|
"Id": 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeMap is a map from types in the XML to type names that is used
|
||||||
|
// in the functions that follow. Basically, every occurrence of the key
|
||||||
|
// type is replaced with the value type.
|
||||||
|
var TypeMap = map[string]string{
|
||||||
|
"VISUALTYPE": "VisualInfo",
|
||||||
|
"DEPTH": "DepthInfo",
|
||||||
|
"SCREEN": "ScreenInfo",
|
||||||
|
"Setup": "SetupInfo",
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameMap is the same as TypeMap, but for names.
|
||||||
|
var NameMap = map[string]string{}
|
||||||
|
|
||||||
|
// Reading, writing and defining...
|
||||||
|
|
||||||
|
// Base types
|
||||||
|
func (b *Base) Define(c *Context) {
|
||||||
|
c.Putln("// Skipping definition for base type '%s'",
|
||||||
|
SrcName(c.protocol, b.XmlName()))
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enum types
|
||||||
|
func (enum *Enum) Define(c *Context) {
|
||||||
|
c.Putln("const (")
|
||||||
|
for _, item := range enum.Items {
|
||||||
|
c.Putln("%s%s = %d", enum.SrcName(), item.srcName, item.Expr.Eval())
|
||||||
|
}
|
||||||
|
c.Putln(")")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resource types
|
||||||
|
func (res *Resource) Define(c *Context) {
|
||||||
|
c.Putln("type %s uint32", res.SrcName())
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("func New%sId(c *xgb.Conn) (%s, error) {",
|
||||||
|
res.SrcName(), res.SrcName())
|
||||||
|
c.Putln("id, err := c.NewId()")
|
||||||
|
c.Putln("if err != nil {")
|
||||||
|
c.Putln("return 0, err")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("return %s(id), nil", res.SrcName())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeDef types
|
||||||
|
func (td *TypeDef) Define(c *Context) {
|
||||||
|
c.Putln("type %s %s", td.srcName, td.Old.SrcName())
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field definitions, reads and writes.
|
||||||
|
|
||||||
|
// Pad fields
|
||||||
|
func (f *PadField) Define(c *Context) {
|
||||||
|
if f.Align > 0 {
|
||||||
|
c.Putln("// alignment gap to multiple of %d", f.Align)
|
||||||
|
} else {
|
||||||
|
c.Putln("// padding: %d bytes", f.Bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *PadField) Read(c *Context, prefix string) {
|
||||||
|
if f.Align > 0 {
|
||||||
|
c.Putln("b = (b + %d) & ^%d // alignment gap", f.Align-1, f.Align-1)
|
||||||
|
} else {
|
||||||
|
c.Putln("b += %s // padding", f.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *PadField) Write(c *Context, prefix string) {
|
||||||
|
if f.Align > 0 {
|
||||||
|
c.Putln("b = (b + %d) & ^%d // alignment gap", f.Align-1, f.Align-1)
|
||||||
|
} else {
|
||||||
|
c.Putln("b += %s // padding", f.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local fields
|
||||||
|
func (f *LocalField) Define(c *Context) {
|
||||||
|
c.Putln("// local field: %s %s", f.SrcName(), f.Type.SrcName())
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *LocalField) Read(c *Context, prefix string) {
|
||||||
|
c.Putln("// reading local field: %s (%s) :: %s",
|
||||||
|
f.SrcName(), f.Size(), f.Type.SrcName())
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *LocalField) Write(c *Context, prefix string) {
|
||||||
|
c.Putln("// skip writing local field: %s (%s) :: %s",
|
||||||
|
f.SrcName(), f.Size(), f.Type.SrcName())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expr fields
|
||||||
|
func (f *ExprField) Define(c *Context) {
|
||||||
|
c.Putln("// expression field: %s %s (%s)",
|
||||||
|
f.SrcName(), f.Type.SrcName(), f.Expr)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ExprField) Read(c *Context, prefix string) {
|
||||||
|
c.Putln("// reading expression field: %s (%s) (%s) :: %s",
|
||||||
|
f.SrcName(), f.Size(), f.Expr, f.Type.SrcName())
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ExprField) Write(c *Context, prefix string) {
|
||||||
|
// Special case for bools, grrr.
|
||||||
|
if f.Type.SrcName() == "bool" {
|
||||||
|
c.Putln("buf[b] = byte(%s)", f.Expr.Reduce(prefix))
|
||||||
|
c.Putln("b += 1")
|
||||||
|
} else {
|
||||||
|
WriteSimpleSingleField(c, f.Expr.Reduce(prefix), f.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value field
|
||||||
|
func (f *ValueField) Define(c *Context) {
|
||||||
|
c.Putln("%s %s", f.MaskName, f.SrcType())
|
||||||
|
c.Putln("%s []uint32", f.ListName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ValueField) Read(c *Context, prefix string) {
|
||||||
|
ReadSimpleSingleField(c,
|
||||||
|
fmt.Sprintf("%s%s", prefix, f.MaskName), f.MaskType)
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("%s%s = make([]uint32, %s)",
|
||||||
|
prefix, f.ListName, f.ListLength().Reduce(prefix))
|
||||||
|
c.Putln("for i := 0; i < %s; i++ {", f.ListLength().Reduce(prefix))
|
||||||
|
c.Putln("%s%s[i] = xgb.Get32(buf[b:])", prefix, f.ListName)
|
||||||
|
c.Putln("b += 4")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("b = xgb.Pad(b)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ValueField) Write(c *Context, prefix string) {
|
||||||
|
WriteSimpleSingleField(c,
|
||||||
|
fmt.Sprintf("%s%s", prefix, f.MaskName), f.MaskType)
|
||||||
|
c.Putln("for i := 0; i < %s; i++ {", f.ListLength().Reduce(prefix))
|
||||||
|
c.Putln("xgb.Put32(buf[b:], %s%s[i])", prefix, f.ListName)
|
||||||
|
c.Putln("b += 4")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("b = xgb.Pad(b)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch field
|
||||||
|
func (f *SwitchField) Define(c *Context) {
|
||||||
|
c.Putln("%s []uint32", f.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SwitchField) Read(c *Context, prefix string) {
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("%s%s = make([]uint32, %s)",
|
||||||
|
prefix, f.Name, f.ListLength().Reduce(prefix))
|
||||||
|
c.Putln("for i := 0; i < %s; i++ {", f.ListLength().Reduce(prefix))
|
||||||
|
c.Putln("%s%s[i] = xgb.Get32(buf[b:])", prefix, f.Name)
|
||||||
|
c.Putln("b += 4")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("b = xgb.Pad(b)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SwitchField) Write(c *Context, prefix string) {
|
||||||
|
c.Putln("for i := 0; i < %s; i++ {", f.ListLength().Reduce(prefix))
|
||||||
|
c.Putln("xgb.Put32(buf[b:], %s%s[i])", prefix, f.Name)
|
||||||
|
c.Putln("b += 4")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("b = xgb.Pad(b)")
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error types
|
||||||
|
func (e *Error) Define(c *Context) {
|
||||||
|
c.Putln("// %s is the error number for a %s.", e.ErrConst(), e.ErrConst())
|
||||||
|
c.Putln("const %s = %d", e.ErrConst(), e.Number)
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("type %s struct {", e.ErrType())
|
||||||
|
c.Putln("Sequence uint16")
|
||||||
|
c.Putln("NiceName string")
|
||||||
|
for _, field := range e.Fields {
|
||||||
|
field.Define(c)
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Read defines a function that transforms a byte slice into this
|
||||||
|
// error struct.
|
||||||
|
e.Read(c)
|
||||||
|
|
||||||
|
// Makes sure this error type implements the xgb.Error interface.
|
||||||
|
e.ImplementsError(c)
|
||||||
|
|
||||||
|
// Let's the XGB event loop read this error.
|
||||||
|
c.Putln("func init() {")
|
||||||
|
if c.protocol.isExt() {
|
||||||
|
c.Putln("xgb.NewExtErrorFuncs[\"%s\"][%d] = %sNew",
|
||||||
|
c.protocol.ExtXName, e.Number, e.ErrType())
|
||||||
|
} else {
|
||||||
|
c.Putln("xgb.NewErrorFuncs[%d] = %sNew", e.Number, e.ErrType())
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Read(c *Context) {
|
||||||
|
c.Putln("// %sNew constructs a %s value that implements xgb.Error from "+
|
||||||
|
"a byte slice.", e.ErrType(), e.ErrType())
|
||||||
|
c.Putln("func %sNew(buf []byte) xgb.Error {", e.ErrType())
|
||||||
|
c.Putln("v := %s{}", e.ErrType())
|
||||||
|
c.Putln("v.NiceName = \"%s\"", e.SrcName())
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("b := 1 // skip error determinant")
|
||||||
|
c.Putln("b += 1 // don't read error number")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("v.Sequence = xgb.Get16(buf[b:])")
|
||||||
|
c.Putln("b += 2")
|
||||||
|
c.Putln("")
|
||||||
|
for _, field := range e.Fields {
|
||||||
|
field.Read(c, "v.")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
c.Putln("return v")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImplementsError writes functions to implement the XGB Error interface.
|
||||||
|
func (e *Error) ImplementsError(c *Context) {
|
||||||
|
c.Putln("// SequenceId returns the sequence id attached to the %s error.",
|
||||||
|
e.ErrConst())
|
||||||
|
c.Putln("// This is mostly used internally.")
|
||||||
|
c.Putln("func (err %s) SequenceId() uint16 {", e.ErrType())
|
||||||
|
c.Putln("return err.Sequence")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("// BadId returns the 'BadValue' number if one exists for the "+
|
||||||
|
"%s error. If no bad value exists, 0 is returned.", e.ErrConst())
|
||||||
|
c.Putln("func (err %s) BadId() uint32 {", e.ErrType())
|
||||||
|
if !c.protocol.isExt() {
|
||||||
|
c.Putln("return err.BadValue")
|
||||||
|
} else {
|
||||||
|
c.Putln("return 0")
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("// Error returns a rudimentary string representation of the %s "+
|
||||||
|
"error.", e.ErrConst())
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("func (err %s) Error() string {", e.ErrType())
|
||||||
|
ErrorFieldString(c, e.Fields, e.ErrConst())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorCopy types
|
||||||
|
func (e *ErrorCopy) Define(c *Context) {
|
||||||
|
c.Putln("// %s is the error number for a %s.", e.ErrConst(), e.ErrConst())
|
||||||
|
c.Putln("const %s = %d", e.ErrConst(), e.Number)
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("type %s %s", e.ErrType(), e.Old.(*Error).ErrType())
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Read defines a function that transforms a byte slice into this
|
||||||
|
// error struct.
|
||||||
|
e.Read(c)
|
||||||
|
|
||||||
|
// Makes sure this error type implements the xgb.Error interface.
|
||||||
|
e.ImplementsError(c)
|
||||||
|
|
||||||
|
// Let's the XGB know how to read this error.
|
||||||
|
c.Putln("func init() {")
|
||||||
|
if c.protocol.isExt() {
|
||||||
|
c.Putln("xgb.NewExtErrorFuncs[\"%s\"][%d] = %sNew",
|
||||||
|
c.protocol.ExtXName, e.Number, e.ErrType())
|
||||||
|
} else {
|
||||||
|
c.Putln("xgb.NewErrorFuncs[%d] = %sNew", e.Number, e.ErrType())
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorCopy) Read(c *Context) {
|
||||||
|
c.Putln("// %sNew constructs a %s value that implements xgb.Error from "+
|
||||||
|
"a byte slice.", e.ErrType(), e.ErrType())
|
||||||
|
c.Putln("func %sNew(buf []byte) xgb.Error {", e.ErrType())
|
||||||
|
c.Putln("v := %s(%sNew(buf).(%s))",
|
||||||
|
e.ErrType(), e.Old.(*Error).ErrType(), e.Old.(*Error).ErrType())
|
||||||
|
c.Putln("v.NiceName = \"%s\"", e.SrcName())
|
||||||
|
c.Putln("return v")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImplementsError writes functions to implement the XGB Error interface.
|
||||||
|
func (e *ErrorCopy) ImplementsError(c *Context) {
|
||||||
|
c.Putln("// SequenceId returns the sequence id attached to the %s error.",
|
||||||
|
e.ErrConst())
|
||||||
|
c.Putln("// This is mostly used internally.")
|
||||||
|
c.Putln("func (err %s) SequenceId() uint16 {", e.ErrType())
|
||||||
|
c.Putln("return err.Sequence")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("// BadId returns the 'BadValue' number if one exists for the "+
|
||||||
|
"%s error. If no bad value exists, 0 is returned.", e.ErrConst())
|
||||||
|
c.Putln("func (err %s) BadId() uint32 {", e.ErrType())
|
||||||
|
if !c.protocol.isExt() {
|
||||||
|
c.Putln("return err.BadValue")
|
||||||
|
} else {
|
||||||
|
c.Putln("return 0")
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("// Error returns a rudimentary string representation of the %s "+
|
||||||
|
"error.", e.ErrConst())
|
||||||
|
c.Putln("func (err %s) Error() string {", e.ErrType())
|
||||||
|
ErrorFieldString(c, e.Old.(*Error).Fields, e.ErrConst())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorFieldString works for both Error and ErrorCopy. It assembles all of the
|
||||||
|
// fields in an error and formats them into a single string.
|
||||||
|
func ErrorFieldString(c *Context, fields []Field, errName string) {
|
||||||
|
c.Putln("fieldVals := make([]string, 0, %d)", len(fields))
|
||||||
|
c.Putln("fieldVals = append(fieldVals, \"NiceName: \" + err.NiceName)")
|
||||||
|
c.Putln("fieldVals = append(fieldVals, "+
|
||||||
|
"xgb.Sprintf(\"Sequence: %s\", err.Sequence))", "%d")
|
||||||
|
for _, field := range fields {
|
||||||
|
switch field.(type) {
|
||||||
|
case *PadField:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
if field.SrcType() == "string" {
|
||||||
|
c.Putln("fieldVals = append(fieldVals, \"%s: \" + err.%s)",
|
||||||
|
field.SrcName(), field.SrcName())
|
||||||
|
} else {
|
||||||
|
format := fmt.Sprintf("xgb.Sprintf(\"%s: %s\", err.%s)",
|
||||||
|
field.SrcName(), "%d", field.SrcName())
|
||||||
|
c.Putln("fieldVals = append(fieldVals, %s)", format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Putln("return \"%s {\" + xgb.StringsJoin(fieldVals, \", \") + \"}\"",
|
||||||
|
errName)
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event types
|
||||||
|
func (e *Event) Define(c *Context) {
|
||||||
|
c.Putln("// %s is the event number for a %s.", e.SrcName(), e.EvType())
|
||||||
|
c.Putln("const %s = %d", e.SrcName(), e.Number)
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("type %s struct {", e.EvType())
|
||||||
|
if !e.NoSequence {
|
||||||
|
c.Putln("Sequence uint16")
|
||||||
|
}
|
||||||
|
for _, field := range e.Fields {
|
||||||
|
field.Define(c)
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Read defines a function that transforms a byte slice into this
|
||||||
|
// event struct.
|
||||||
|
e.Read(c)
|
||||||
|
|
||||||
|
// Write defines a function that transforms this event struct into
|
||||||
|
// a byte slice.
|
||||||
|
e.Write(c)
|
||||||
|
|
||||||
|
// Makes sure that this event type is an Event interface.
|
||||||
|
c.Putln("// SequenceId returns the sequence id attached to the %s event.",
|
||||||
|
e.SrcName())
|
||||||
|
c.Putln("// Events without a sequence number (KeymapNotify) return 0.")
|
||||||
|
c.Putln("// This is mostly used internally.")
|
||||||
|
c.Putln("func (v %s) SequenceId() uint16 {", e.EvType())
|
||||||
|
if e.NoSequence {
|
||||||
|
c.Putln("return uint16(0)")
|
||||||
|
} else {
|
||||||
|
c.Putln("return v.Sequence")
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("// String is a rudimentary string representation of %s.",
|
||||||
|
e.EvType())
|
||||||
|
c.Putln("func (v %s) String() string {", e.EvType())
|
||||||
|
EventFieldString(c, e.Fields, e.SrcName())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Let's the XGB event loop read this event.
|
||||||
|
c.Putln("func init() {")
|
||||||
|
if c.protocol.isExt() {
|
||||||
|
c.Putln("xgb.NewExtEventFuncs[\"%s\"][%d] = %sNew",
|
||||||
|
c.protocol.ExtXName, e.Number, e.EvType())
|
||||||
|
} else {
|
||||||
|
c.Putln("xgb.NewEventFuncs[%d] = %sNew", e.Number, e.EvType())
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Event) Read(c *Context) {
|
||||||
|
c.Putln("// %sNew constructs a %s value that implements xgb.Event from "+
|
||||||
|
"a byte slice.", e.EvType(), e.EvType())
|
||||||
|
c.Putln("func %sNew(buf []byte) xgb.Event {", e.EvType())
|
||||||
|
c.Putln("v := %s{}", e.EvType())
|
||||||
|
c.Putln("b := 1 // don't read event number")
|
||||||
|
c.Putln("")
|
||||||
|
for i, field := range e.Fields {
|
||||||
|
if i == 1 && !e.NoSequence {
|
||||||
|
c.Putln("v.Sequence = xgb.Get16(buf[b:])")
|
||||||
|
c.Putln("b += 2")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
field.Read(c, "v.")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
c.Putln("return v")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Event) Write(c *Context) {
|
||||||
|
c.Putln("// Bytes writes a %s value to a byte slice.", e.EvType())
|
||||||
|
c.Putln("func (v %s) Bytes() []byte {", e.EvType())
|
||||||
|
c.Putln("buf := make([]byte, %s)", e.Size())
|
||||||
|
c.Putln("b := 0")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("// write event number")
|
||||||
|
c.Putln("buf[b] = %d", e.Number)
|
||||||
|
c.Putln("b += 1")
|
||||||
|
c.Putln("")
|
||||||
|
for i, field := range e.Fields {
|
||||||
|
if i == 1 && !e.NoSequence {
|
||||||
|
c.Putln("b += 2 // skip sequence number")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
field.Write(c, "v.")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
c.Putln("return buf")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventCopy types
|
||||||
|
func (e *EventCopy) Define(c *Context) {
|
||||||
|
c.Putln("// %s is the event number for a %s.", e.SrcName(), e.EvType())
|
||||||
|
c.Putln("const %s = %d", e.SrcName(), e.Number)
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("type %s %s", e.EvType(), e.Old.(*Event).EvType())
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Read defines a function that transforms a byte slice into this
|
||||||
|
// event struct.
|
||||||
|
e.Read(c)
|
||||||
|
|
||||||
|
// Write defines a function that transoforms this event struct into
|
||||||
|
// a byte slice.
|
||||||
|
e.Write(c)
|
||||||
|
|
||||||
|
// Makes sure that this event type is an Event interface.
|
||||||
|
c.Putln("// SequenceId returns the sequence id attached to the %s event.",
|
||||||
|
e.SrcName())
|
||||||
|
c.Putln("// Events without a sequence number (KeymapNotify) return 0.")
|
||||||
|
c.Putln("// This is mostly used internally.")
|
||||||
|
c.Putln("func (v %s) SequenceId() uint16 {", e.EvType())
|
||||||
|
if e.Old.(*Event).NoSequence {
|
||||||
|
c.Putln("return uint16(0)")
|
||||||
|
} else {
|
||||||
|
c.Putln("return v.Sequence")
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("func (v %s) String() string {", e.EvType())
|
||||||
|
EventFieldString(c, e.Old.(*Event).Fields, e.SrcName())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Let's the XGB event loop read this event.
|
||||||
|
c.Putln("func init() {")
|
||||||
|
if c.protocol.isExt() {
|
||||||
|
c.Putln("xgb.NewExtEventFuncs[\"%s\"][%d] = %sNew",
|
||||||
|
c.protocol.ExtXName, e.Number, e.EvType())
|
||||||
|
} else {
|
||||||
|
c.Putln("xgb.NewEventFuncs[%d] = %sNew", e.Number, e.EvType())
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventCopy) Read(c *Context) {
|
||||||
|
c.Putln("// %sNew constructs a %s value that implements xgb.Event from "+
|
||||||
|
"a byte slice.", e.EvType(), e.EvType())
|
||||||
|
c.Putln("func %sNew(buf []byte) xgb.Event {", e.EvType())
|
||||||
|
c.Putln("return %s(%sNew(buf).(%s))",
|
||||||
|
e.EvType(), e.Old.(*Event).EvType(), e.Old.(*Event).EvType())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventCopy) Write(c *Context) {
|
||||||
|
c.Putln("// Bytes writes a %s value to a byte slice.", e.EvType())
|
||||||
|
c.Putln("func (v %s) Bytes() []byte {", e.EvType())
|
||||||
|
c.Putln("return %s(v).Bytes()", e.Old.(*Event).EvType())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventFieldString works for both Event and EventCopy. It assembles all of the
|
||||||
|
// fields in an event and formats them into a single string.
|
||||||
|
func EventFieldString(c *Context, fields []Field, evName string) {
|
||||||
|
c.Putln("fieldVals := make([]string, 0, %d)", len(fields))
|
||||||
|
if evName != "KeymapNotify" {
|
||||||
|
c.Putln("fieldVals = append(fieldVals, "+
|
||||||
|
"xgb.Sprintf(\"Sequence: %s\", v.Sequence))", "%d")
|
||||||
|
}
|
||||||
|
for _, field := range fields {
|
||||||
|
switch f := field.(type) {
|
||||||
|
case *PadField:
|
||||||
|
continue
|
||||||
|
case *SingleField:
|
||||||
|
switch f.Type.(type) {
|
||||||
|
case *Base:
|
||||||
|
case *Resource:
|
||||||
|
case *TypeDef:
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch field.SrcType() {
|
||||||
|
case "string":
|
||||||
|
format := fmt.Sprintf("xgb.Sprintf(\"%s: %s\", v.%s)",
|
||||||
|
field.SrcName(), "%s", field.SrcName())
|
||||||
|
c.Putln("fieldVals = append(fieldVals, %s)", format)
|
||||||
|
case "bool":
|
||||||
|
format := fmt.Sprintf("xgb.Sprintf(\"%s: %s\", v.%s)",
|
||||||
|
field.SrcName(), "%t", field.SrcName())
|
||||||
|
c.Putln("fieldVals = append(fieldVals, %s)", format)
|
||||||
|
default:
|
||||||
|
format := fmt.Sprintf("xgb.Sprintf(\"%s: %s\", v.%s)",
|
||||||
|
field.SrcName(), "%d", field.SrcName())
|
||||||
|
c.Putln("fieldVals = append(fieldVals, %s)", format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Putln("return \"%s {\" + xgb.StringsJoin(fieldVals, \", \") + \"}\"",
|
||||||
|
evName)
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List fields
|
||||||
|
func (f *ListField) Define(c *Context) {
|
||||||
|
c.Putln("%s %s // size: %s",
|
||||||
|
f.SrcName(), f.SrcType(), f.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ListField) Read(c *Context, prefix string) {
|
||||||
|
switch t := f.Type.(type) {
|
||||||
|
case *Resource:
|
||||||
|
length := f.LengthExpr.Reduce(prefix)
|
||||||
|
c.Putln("%s%s = make([]%s, %s)",
|
||||||
|
prefix, f.SrcName(), t.SrcName(), length)
|
||||||
|
c.Putln("for i := 0; i < int(%s); i++ {", length)
|
||||||
|
ReadSimpleSingleField(c, fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
|
||||||
|
c.Putln("}")
|
||||||
|
case *Base:
|
||||||
|
length := f.LengthExpr.Reduce(prefix)
|
||||||
|
if strings.ToLower(t.XmlName()) == "char" {
|
||||||
|
c.Putln("{")
|
||||||
|
c.Putln("byteString := make([]%s, %s)", t.SrcName(), length)
|
||||||
|
c.Putln("copy(byteString[:%s], buf[b:])", length)
|
||||||
|
c.Putln("%s%s = string(byteString)", prefix, f.SrcName())
|
||||||
|
// This is apparently a special case. The "Str" type itself
|
||||||
|
// doesn't specify any padding. I suppose it's up to the
|
||||||
|
// request/reply spec that uses it to get the padding right?
|
||||||
|
c.Putln("b += int(%s)", length)
|
||||||
|
c.Putln("}")
|
||||||
|
} else if t.SrcName() == "byte" {
|
||||||
|
c.Putln("%s%s = make([]%s, %s)",
|
||||||
|
prefix, f.SrcName(), t.SrcName(), length)
|
||||||
|
c.Putln("copy(%s%s[:%s], buf[b:])", prefix, f.SrcName(), length)
|
||||||
|
c.Putln("b += int(%s)", length)
|
||||||
|
} else {
|
||||||
|
c.Putln("%s%s = make([]%s, %s)",
|
||||||
|
prefix, f.SrcName(), t.SrcName(), length)
|
||||||
|
c.Putln("for i := 0; i < int(%s); i++ {", length)
|
||||||
|
ReadSimpleSingleField(c,
|
||||||
|
fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
|
||||||
|
c.Putln("}")
|
||||||
|
}
|
||||||
|
case *TypeDef:
|
||||||
|
length := f.LengthExpr.Reduce(prefix)
|
||||||
|
c.Putln("%s%s = make([]%s, %s)",
|
||||||
|
prefix, f.SrcName(), t.SrcName(), length)
|
||||||
|
c.Putln("for i := 0; i < int(%s); i++ {", length)
|
||||||
|
ReadSimpleSingleField(c, fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
|
||||||
|
c.Putln("}")
|
||||||
|
case *Union:
|
||||||
|
c.Putln("%s%s = make([]%s, %s)",
|
||||||
|
prefix, f.SrcName(), t.SrcName(), f.LengthExpr.Reduce(prefix))
|
||||||
|
c.Putln("b += %sReadList(buf[b:], %s%s)",
|
||||||
|
t.SrcName(), prefix, f.SrcName())
|
||||||
|
case *Struct:
|
||||||
|
c.Putln("%s%s = make([]%s, %s)",
|
||||||
|
prefix, f.SrcName(), t.SrcName(), f.LengthExpr.Reduce(prefix))
|
||||||
|
c.Putln("b += %sReadList(buf[b:], %s%s)",
|
||||||
|
t.SrcName(), prefix, f.SrcName())
|
||||||
|
default:
|
||||||
|
log.Panicf("Cannot read list field '%s' with %T type.",
|
||||||
|
f.XmlName(), f.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ListField) Write(c *Context, prefix string) {
|
||||||
|
switch t := f.Type.(type) {
|
||||||
|
case *Resource:
|
||||||
|
length := f.Length().Reduce(prefix)
|
||||||
|
c.Putln("for i := 0; i < int(%s); i++ {", length)
|
||||||
|
WriteSimpleSingleField(c,
|
||||||
|
fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
|
||||||
|
c.Putln("}")
|
||||||
|
case *Base:
|
||||||
|
length := f.Length().Reduce(prefix)
|
||||||
|
if t.SrcName() == "byte" {
|
||||||
|
c.Putln("copy(buf[b:], %s%s[:%s])", prefix, f.SrcName(), length)
|
||||||
|
c.Putln("b += int(%s)", length)
|
||||||
|
} else {
|
||||||
|
c.Putln("for i := 0; i < int(%s); i++ {", length)
|
||||||
|
WriteSimpleSingleField(c,
|
||||||
|
fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
|
||||||
|
c.Putln("}")
|
||||||
|
}
|
||||||
|
case *TypeDef:
|
||||||
|
length := f.Length().Reduce(prefix)
|
||||||
|
c.Putln("for i := 0; i < int(%s); i++ {", length)
|
||||||
|
WriteSimpleSingleField(c,
|
||||||
|
fmt.Sprintf("%s%s[i]", prefix, f.SrcName()), t)
|
||||||
|
c.Putln("}")
|
||||||
|
case *Union:
|
||||||
|
c.Putln("b += %sListBytes(buf[b:], %s%s)",
|
||||||
|
t.SrcName(), prefix, f.SrcName())
|
||||||
|
case *Struct:
|
||||||
|
c.Putln("b += %sListBytes(buf[b:], %s%s)",
|
||||||
|
t.SrcName(), prefix, f.SrcName())
|
||||||
|
default:
|
||||||
|
log.Panicf("Cannot write list field '%s' with %T type.",
|
||||||
|
f.XmlName(), f.Type)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *Request) Define(c *Context) {
|
||||||
|
c.Putln("// %s is a cookie used only for %s requests.",
|
||||||
|
r.CookieName(), r.SrcName())
|
||||||
|
c.Putln("type %s struct {", r.CookieName())
|
||||||
|
c.Putln("*xgb.Cookie")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
if r.Reply != nil {
|
||||||
|
c.Putln("// %s sends a checked request.", r.SrcName())
|
||||||
|
c.Putln("// If an error occurs, it will be returned with the reply "+
|
||||||
|
"by calling %s.Reply()", r.CookieName())
|
||||||
|
c.Putln("func %s(c *xgb.Conn, %s) %s {",
|
||||||
|
r.SrcName(), r.ParamNameTypes(), r.CookieName())
|
||||||
|
r.CheckExt(c)
|
||||||
|
c.Putln("cookie := c.NewCookie(true, true)")
|
||||||
|
c.Putln("c.NewRequest(%s(c, %s), cookie)", r.ReqName(), r.ParamNames())
|
||||||
|
c.Putln("return %s{cookie}", r.CookieName())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
c.Putln("// %sUnchecked sends an unchecked request.", r.SrcName())
|
||||||
|
c.Putln("// If an error occurs, it can only be retrieved using " +
|
||||||
|
"xgb.WaitForEvent or xgb.PollForEvent.")
|
||||||
|
c.Putln("func %sUnchecked(c *xgb.Conn, %s) %s {",
|
||||||
|
r.SrcName(), r.ParamNameTypes(), r.CookieName())
|
||||||
|
r.CheckExt(c)
|
||||||
|
c.Putln("cookie := c.NewCookie(false, true)")
|
||||||
|
c.Putln("c.NewRequest(%s(c, %s), cookie)", r.ReqName(), r.ParamNames())
|
||||||
|
c.Putln("return %s{cookie}", r.CookieName())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
r.ReadReply(c)
|
||||||
|
} else {
|
||||||
|
c.Putln("// %s sends an unchecked request.", r.SrcName())
|
||||||
|
c.Putln("// If an error occurs, it can only be retrieved using " +
|
||||||
|
"xgb.WaitForEvent or xgb.PollForEvent.")
|
||||||
|
c.Putln("func %s(c *xgb.Conn, %s) %s {",
|
||||||
|
r.SrcName(), r.ParamNameTypes(), r.CookieName())
|
||||||
|
r.CheckExt(c)
|
||||||
|
c.Putln("cookie := c.NewCookie(false, false)")
|
||||||
|
c.Putln("c.NewRequest(%s(c, %s), cookie)", r.ReqName(), r.ParamNames())
|
||||||
|
c.Putln("return %s{cookie}", r.CookieName())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
c.Putln("// %sChecked sends a checked request.", r.SrcName())
|
||||||
|
c.Putln("// If an error occurs, it can be retrieved using "+
|
||||||
|
"%s.Check()", r.CookieName())
|
||||||
|
c.Putln("func %sChecked(c *xgb.Conn, %s) %s {",
|
||||||
|
r.SrcName(), r.ParamNameTypes(), r.CookieName())
|
||||||
|
r.CheckExt(c)
|
||||||
|
c.Putln("cookie := c.NewCookie(true, false)")
|
||||||
|
c.Putln("c.NewRequest(%s(c, %s), cookie)", r.ReqName(), r.ParamNames())
|
||||||
|
c.Putln("return %s{cookie}", r.CookieName())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
c.Putln("// Check returns an error if one occurred for checked " +
|
||||||
|
"requests that are not expecting a reply.")
|
||||||
|
c.Putln("// This cannot be called for requests expecting a reply, " +
|
||||||
|
"nor for unchecked requests.")
|
||||||
|
c.Putln("func (cook %s) Check() error {", r.CookieName())
|
||||||
|
c.Putln("return cook.Cookie.Check()")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
r.WriteRequest(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) CheckExt(c *Context) {
|
||||||
|
if !c.protocol.isExt() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Putln("c.ExtLock.RLock()")
|
||||||
|
c.Putln("defer c.ExtLock.RUnlock()")
|
||||||
|
c.Putln("if _, ok := c.Extensions[\"%s\"]; !ok {", c.protocol.ExtXName)
|
||||||
|
c.Putln("panic(\"Cannot issue request '%s' using the uninitialized "+
|
||||||
|
"extension '%s'. %s.Init(connObj) must be called first.\")",
|
||||||
|
r.SrcName(), c.protocol.ExtXName, c.protocol.PkgName())
|
||||||
|
c.Putln("}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) ReadReply(c *Context) {
|
||||||
|
c.Putln("// %s represents the data returned from a %s request.",
|
||||||
|
r.ReplyTypeName(), r.SrcName())
|
||||||
|
c.Putln("type %s struct {", r.ReplyTypeName())
|
||||||
|
c.Putln("Sequence uint16 // sequence number of the request for this reply")
|
||||||
|
c.Putln("Length uint32 // number of bytes in this reply")
|
||||||
|
for _, field := range r.Reply.Fields {
|
||||||
|
field.Define(c)
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
c.Putln("// Reply blocks and returns the reply data for a %s request.",
|
||||||
|
r.SrcName())
|
||||||
|
c.Putln("func (cook %s) Reply() (*%s, error) {",
|
||||||
|
r.CookieName(), r.ReplyTypeName())
|
||||||
|
c.Putln("buf, err := cook.Cookie.Reply()")
|
||||||
|
c.Putln("if err != nil {")
|
||||||
|
c.Putln("return nil, err")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("if buf == nil {")
|
||||||
|
c.Putln("return nil, nil")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("return %s(buf), nil", r.ReplyName())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
c.Putln("// %s reads a byte slice into a %s value.",
|
||||||
|
r.ReplyName(), r.ReplyTypeName())
|
||||||
|
c.Putln("func %s(buf []byte) *%s {",
|
||||||
|
r.ReplyName(), r.ReplyTypeName())
|
||||||
|
c.Putln("v := new(%s)", r.ReplyTypeName())
|
||||||
|
c.Putln("b := 1 // skip reply determinant")
|
||||||
|
c.Putln("")
|
||||||
|
for i, field := range r.Reply.Fields {
|
||||||
|
field.Read(c, "v.")
|
||||||
|
c.Putln("")
|
||||||
|
if i == 0 {
|
||||||
|
c.Putln("v.Sequence = xgb.Get16(buf[b:])")
|
||||||
|
c.Putln("b += 2")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("v.Length = xgb.Get32(buf[b:]) // 4-byte units")
|
||||||
|
c.Putln("b += 4")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Putln("return v")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) WriteRequest(c *Context) {
|
||||||
|
sz := r.Size(c)
|
||||||
|
writeSize1 := func() {
|
||||||
|
if sz.exact {
|
||||||
|
c.Putln("xgb.Put16(buf[b:], uint16(size / 4)) " +
|
||||||
|
"// write request size in 4-byte units")
|
||||||
|
} else {
|
||||||
|
c.Putln("blen := b")
|
||||||
|
}
|
||||||
|
c.Putln("b += 2")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
writeSize2 := func() {
|
||||||
|
if sz.exact {
|
||||||
|
c.Putln("return buf")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Putln("b = xgb.Pad(b)")
|
||||||
|
c.Putln("xgb.Put16(buf[blen:], uint16(b / 4)) " +
|
||||||
|
"// write request size in 4-byte units")
|
||||||
|
c.Putln("return buf[:b]")
|
||||||
|
}
|
||||||
|
c.Putln("// Write request to wire for %s", r.SrcName())
|
||||||
|
c.Putln("// %s writes a %s request to a byte slice.",
|
||||||
|
r.ReqName(), r.SrcName())
|
||||||
|
c.Putln("func %s(c *xgb.Conn, %s) []byte {",
|
||||||
|
r.ReqName(), r.ParamNameTypes())
|
||||||
|
c.Putln("size := %s", sz)
|
||||||
|
c.Putln("b := 0")
|
||||||
|
c.Putln("buf := make([]byte, size)")
|
||||||
|
c.Putln("")
|
||||||
|
if c.protocol.isExt() {
|
||||||
|
c.Putln("c.ExtLock.RLock()")
|
||||||
|
c.Putln("buf[b] = c.Extensions[\"%s\"]", c.protocol.ExtXName)
|
||||||
|
c.Putln("c.ExtLock.RUnlock()")
|
||||||
|
c.Putln("b += 1")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
c.Putln("buf[b] = %d // request opcode", r.Opcode)
|
||||||
|
c.Putln("b += 1")
|
||||||
|
c.Putln("")
|
||||||
|
if len(r.Fields) == 0 {
|
||||||
|
if !c.protocol.isExt() {
|
||||||
|
c.Putln("b += 1 // padding")
|
||||||
|
}
|
||||||
|
writeSize1()
|
||||||
|
} else if c.protocol.isExt() {
|
||||||
|
writeSize1()
|
||||||
|
}
|
||||||
|
for i, field := range r.Fields {
|
||||||
|
field.Write(c, "")
|
||||||
|
c.Putln("")
|
||||||
|
if i == 0 && !c.protocol.isExt() {
|
||||||
|
writeSize1()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeSize2()
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) ParamNames() string {
|
||||||
|
names := make([]string, 0, len(r.Fields))
|
||||||
|
for _, field := range r.Fields {
|
||||||
|
switch f := field.(type) {
|
||||||
|
case *ValueField:
|
||||||
|
names = append(names, f.MaskName)
|
||||||
|
names = append(names, f.ListName)
|
||||||
|
case *PadField:
|
||||||
|
continue
|
||||||
|
case *ExprField:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
names = append(names, fmt.Sprintf("%s", field.SrcName()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(names, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) ParamNameTypes() string {
|
||||||
|
nameTypes := make([]string, 0, len(r.Fields))
|
||||||
|
for _, field := range r.Fields {
|
||||||
|
switch f := field.(type) {
|
||||||
|
case *ValueField:
|
||||||
|
nameTypes = append(nameTypes,
|
||||||
|
fmt.Sprintf("%s %s", f.MaskName, f.MaskType.SrcName()))
|
||||||
|
nameTypes = append(nameTypes,
|
||||||
|
fmt.Sprintf("%s []uint32", f.ListName))
|
||||||
|
case *PadField:
|
||||||
|
continue
|
||||||
|
case *ExprField:
|
||||||
|
continue
|
||||||
|
case *RequiredStartAlign:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
nameTypes = append(nameTypes,
|
||||||
|
fmt.Sprintf("%s %s", field.SrcName(), field.SrcType()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(nameTypes, ", ")
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *SingleField) Define(c *Context) {
|
||||||
|
c.Putln("%s %s", f.SrcName(), f.Type.SrcName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadSimpleSingleField(c *Context, name string, typ Type) {
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case *Resource:
|
||||||
|
c.Putln("%s = %s(xgb.Get32(buf[b:]))", name, t.SrcName())
|
||||||
|
case *TypeDef:
|
||||||
|
switch t.Size().Eval() {
|
||||||
|
case 1:
|
||||||
|
c.Putln("%s = %s(buf[b])", name, t.SrcName())
|
||||||
|
case 2:
|
||||||
|
c.Putln("%s = %s(xgb.Get16(buf[b:]))", name, t.SrcName())
|
||||||
|
case 4:
|
||||||
|
c.Putln("%s = %s(xgb.Get32(buf[b:]))", name, t.SrcName())
|
||||||
|
case 8:
|
||||||
|
c.Putln("%s = %s(xgb.Get64(buf[b:]))", name, t.SrcName())
|
||||||
|
}
|
||||||
|
case *Base:
|
||||||
|
// If this is a bool, stop short and do something special.
|
||||||
|
if t.SrcName() == "bool" {
|
||||||
|
c.Putln("if buf[b] == 1 {")
|
||||||
|
c.Putln("%s = true", name)
|
||||||
|
c.Putln("} else {")
|
||||||
|
c.Putln("%s = false", name)
|
||||||
|
c.Putln("}")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var val string
|
||||||
|
switch t.Size().Eval() {
|
||||||
|
case 1:
|
||||||
|
val = fmt.Sprintf("buf[b]")
|
||||||
|
case 2:
|
||||||
|
val = fmt.Sprintf("xgb.Get16(buf[b:])")
|
||||||
|
case 4:
|
||||||
|
val = fmt.Sprintf("xgb.Get32(buf[b:])")
|
||||||
|
case 8:
|
||||||
|
val = fmt.Sprintf("xgb.Get64(buf[b:])")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to convert base types if they aren't uintXX or byte
|
||||||
|
ty := t.SrcName()
|
||||||
|
if ty != "byte" && ty != "uint16" && ty != "uint32" && ty != "uint64" {
|
||||||
|
val = fmt.Sprintf("%s(%s)", ty, val)
|
||||||
|
}
|
||||||
|
c.Putln("%s = %s", name, val)
|
||||||
|
default:
|
||||||
|
log.Panicf("Cannot read field '%s' as a simple field with %T type.",
|
||||||
|
name, typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Putln("b += %s", typ.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SingleField) Read(c *Context, prefix string) {
|
||||||
|
switch t := f.Type.(type) {
|
||||||
|
case *Resource:
|
||||||
|
ReadSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
|
||||||
|
case *TypeDef:
|
||||||
|
ReadSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
|
||||||
|
case *Base:
|
||||||
|
ReadSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
|
||||||
|
case *Struct:
|
||||||
|
c.Putln("%s%s = %s{}", prefix, f.SrcName(), t.SrcName())
|
||||||
|
c.Putln("b += %sRead(buf[b:], &%s%s)", t.SrcName(), prefix, f.SrcName())
|
||||||
|
case *Union:
|
||||||
|
c.Putln("%s%s = %s{}", prefix, f.SrcName(), t.SrcName())
|
||||||
|
c.Putln("b += %sRead(buf[b:], &%s%s)", t.SrcName(), prefix, f.SrcName())
|
||||||
|
default:
|
||||||
|
log.Panicf("Cannot read field '%s' with %T type.", f.XmlName(), f.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteSimpleSingleField(c *Context, name string, typ Type) {
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case *Resource:
|
||||||
|
c.Putln("xgb.Put32(buf[b:], uint32(%s))", name)
|
||||||
|
case *TypeDef:
|
||||||
|
switch t.Size().Eval() {
|
||||||
|
case 1:
|
||||||
|
c.Putln("buf[b] = byte(%s)", name)
|
||||||
|
case 2:
|
||||||
|
c.Putln("xgb.Put16(buf[b:], uint16(%s))", name)
|
||||||
|
case 4:
|
||||||
|
c.Putln("xgb.Put32(buf[b:], uint32(%s))", name)
|
||||||
|
case 8:
|
||||||
|
c.Putln("xgb.Put64(buf[b:], uint64(%s))", name)
|
||||||
|
}
|
||||||
|
case *Base:
|
||||||
|
// If this is a bool, stop short and do something special.
|
||||||
|
if t.SrcName() == "bool" {
|
||||||
|
c.Putln("if %s {", name)
|
||||||
|
c.Putln("buf[b] = 1")
|
||||||
|
c.Putln("} else {")
|
||||||
|
c.Putln("buf[b] = 0")
|
||||||
|
c.Putln("}")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t.Size().Eval() {
|
||||||
|
case 1:
|
||||||
|
if t.SrcName() != "byte" {
|
||||||
|
c.Putln("buf[b] = byte(%s)", name)
|
||||||
|
} else {
|
||||||
|
c.Putln("buf[b] = %s", name)
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if t.SrcName() != "uint16" {
|
||||||
|
c.Putln("xgb.Put16(buf[b:], uint16(%s))", name)
|
||||||
|
} else {
|
||||||
|
c.Putln("xgb.Put16(buf[b:], %s)", name)
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
if t.SrcName() != "uint32" {
|
||||||
|
c.Putln("xgb.Put32(buf[b:], uint32(%s))", name)
|
||||||
|
} else {
|
||||||
|
c.Putln("xgb.Put32(buf[b:], %s)", name)
|
||||||
|
}
|
||||||
|
case 8:
|
||||||
|
if t.SrcName() != "uint64" {
|
||||||
|
c.Putln("xgb.Put64(buf[b:], uint64(%s))", name)
|
||||||
|
} else {
|
||||||
|
c.Putln("xgb.Put64(buf[b:], %s)", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Fatalf("Cannot read field '%s' as a simple field with %T type.",
|
||||||
|
name, typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Putln("b += %s", typ.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SingleField) Write(c *Context, prefix string) {
|
||||||
|
switch t := f.Type.(type) {
|
||||||
|
case *Resource:
|
||||||
|
WriteSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
|
||||||
|
case *TypeDef:
|
||||||
|
WriteSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
|
||||||
|
case *Base:
|
||||||
|
WriteSimpleSingleField(c, fmt.Sprintf("%s%s", prefix, f.SrcName()), t)
|
||||||
|
case *Union:
|
||||||
|
c.Putln("{")
|
||||||
|
c.Putln("unionBytes := %s%s.Bytes()", prefix, f.SrcName())
|
||||||
|
c.Putln("copy(buf[b:], unionBytes)")
|
||||||
|
c.Putln("b += len(unionBytes)")
|
||||||
|
c.Putln("}")
|
||||||
|
case *Struct:
|
||||||
|
c.Putln("{")
|
||||||
|
c.Putln("structBytes := %s%s.Bytes()", prefix, f.SrcName())
|
||||||
|
c.Putln("copy(buf[b:], structBytes)")
|
||||||
|
c.Putln("b += len(structBytes)")
|
||||||
|
c.Putln("}")
|
||||||
|
default:
|
||||||
|
log.Fatalf("Cannot read field '%s' with %T type.", f.XmlName(), f.Type)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
func (s *Struct) Define(c *Context) {
|
||||||
|
c.Putln("type %s struct {", s.SrcName())
|
||||||
|
for _, field := range s.Fields {
|
||||||
|
field.Define(c)
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Write function that reads bytes and produces this struct.
|
||||||
|
s.Read(c)
|
||||||
|
|
||||||
|
// Write function that reads bytes and produces a list of this struct.
|
||||||
|
s.ReadList(c)
|
||||||
|
|
||||||
|
// Write function that writes bytes given this struct.
|
||||||
|
s.Write(c)
|
||||||
|
|
||||||
|
// Write function that writes a list of this struct.
|
||||||
|
s.WriteList(c)
|
||||||
|
|
||||||
|
// Write function that computes the size of a list of these structs,
|
||||||
|
// IF there is a list field in this struct.
|
||||||
|
if s.HasList() {
|
||||||
|
s.WriteListSize(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read for a struct creates a function 'ReadStructName' that takes a source
|
||||||
|
// byte slice (i.e., the buffer) and a destination struct, and returns
|
||||||
|
// the number of bytes read off the buffer.
|
||||||
|
// 'ReadStructName' should only be used to read raw reply data from the wire.
|
||||||
|
func (s *Struct) Read(c *Context) {
|
||||||
|
c.Putln("// %sRead reads a byte slice into a %s value.",
|
||||||
|
s.SrcName(), s.SrcName())
|
||||||
|
c.Putln("func %sRead(buf []byte, v *%s) int {", s.SrcName(), s.SrcName())
|
||||||
|
|
||||||
|
c.Putln("b := 0")
|
||||||
|
c.Putln("")
|
||||||
|
for _, field := range s.Fields {
|
||||||
|
field.Read(c, "v.")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
c.Putln("return b")
|
||||||
|
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadList for a struct creates a function 'ReadStructNameList' that takes
|
||||||
|
// a source (i.e., the buffer) byte slice, and a destination slice and returns
|
||||||
|
// the number of bytes read from the byte slice.
|
||||||
|
func (s *Struct) ReadList(c *Context) {
|
||||||
|
c.Putln("// %sReadList reads a byte slice into a list of %s values.",
|
||||||
|
s.SrcName(), s.SrcName())
|
||||||
|
c.Putln("func %sReadList(buf []byte, dest []%s) int {",
|
||||||
|
s.SrcName(), s.SrcName())
|
||||||
|
c.Putln("b := 0")
|
||||||
|
c.Putln("for i := 0; i < len(dest); i++ {")
|
||||||
|
c.Putln("dest[i] = %s{}", s.SrcName())
|
||||||
|
c.Putln("b += %sRead(buf[b:], &dest[i])", s.SrcName())
|
||||||
|
c.Putln("}")
|
||||||
|
|
||||||
|
c.Putln("return xgb.Pad(b)")
|
||||||
|
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) Write(c *Context) {
|
||||||
|
c.Putln("// Bytes writes a %s value to a byte slice.", s.SrcName())
|
||||||
|
c.Putln("func (v %s) Bytes() []byte {", s.SrcName())
|
||||||
|
c.Putln("buf := make([]byte, %s)", s.Size().Reduce("v."))
|
||||||
|
c.Putln("b := 0")
|
||||||
|
c.Putln("")
|
||||||
|
for _, field := range s.Fields {
|
||||||
|
field.Write(c, "v.")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
c.Putln("return buf[:b]")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) WriteList(c *Context) {
|
||||||
|
c.Putln("// %sListBytes writes a list of %s values to a byte slice.",
|
||||||
|
s.SrcName(), s.SrcName())
|
||||||
|
c.Putln("func %sListBytes(buf []byte, list []%s) int {",
|
||||||
|
s.SrcName(), s.SrcName())
|
||||||
|
c.Putln("b := 0")
|
||||||
|
c.Putln("var structBytes []byte")
|
||||||
|
c.Putln("for _, item := range list {")
|
||||||
|
c.Putln("structBytes = item.Bytes()")
|
||||||
|
c.Putln("copy(buf[b:], structBytes)")
|
||||||
|
c.Putln("b += len(structBytes)")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("return xgb.Pad(b)")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) WriteListSize(c *Context) {
|
||||||
|
c.Putln("// %sListSize computes the size (bytes) of a list of %s values.",
|
||||||
|
s.SrcName(), s.SrcName())
|
||||||
|
c.Putln("func %sListSize(list []%s) int {", s.SrcName(), s.SrcName())
|
||||||
|
c.Putln("size := 0")
|
||||||
|
if s.Size().Expression.Concrete() {
|
||||||
|
c.Putln("for _ = range list {")
|
||||||
|
} else {
|
||||||
|
c.Putln("for _, item := range list {")
|
||||||
|
}
|
||||||
|
c.Putln("size += %s", s.Size().Reduce("item."))
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("return size")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Union types
|
||||||
|
func (u *Union) Define(c *Context) {
|
||||||
|
c.Putln("// %s is a represention of the %s union type.",
|
||||||
|
u.SrcName(), u.SrcName())
|
||||||
|
c.Putln("// Note that to *create* a Union, you should *never* create")
|
||||||
|
c.Putln("// this struct directly (unless you know what you're doing).")
|
||||||
|
c.Putln("// Instead use one of the following constructors for '%s':",
|
||||||
|
u.SrcName())
|
||||||
|
for _, field := range u.Fields {
|
||||||
|
c.Putln("// %s%sNew(%s %s) %s", u.SrcName(), field.SrcName(),
|
||||||
|
field.SrcName(), field.SrcType(), u.SrcName())
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Putln("type %s struct {", u.SrcName())
|
||||||
|
for _, field := range u.Fields {
|
||||||
|
field.Define(c)
|
||||||
|
}
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
// Write functions for each field that create instances of this
|
||||||
|
// union using the corresponding field.
|
||||||
|
u.New(c)
|
||||||
|
|
||||||
|
// Write function that reads bytes and produces this union.
|
||||||
|
u.Read(c)
|
||||||
|
|
||||||
|
// Write function that reads bytes and produces a list of this union.
|
||||||
|
u.ReadList(c)
|
||||||
|
|
||||||
|
// Write function that writes bytes given this union.
|
||||||
|
u.Write(c)
|
||||||
|
|
||||||
|
// Write function that writes a list of this union.
|
||||||
|
u.WriteList(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Union) New(c *Context) {
|
||||||
|
for _, field := range u.Fields {
|
||||||
|
c.Putln("// %s%sNew constructs a new %s union type with the %s field.",
|
||||||
|
u.SrcName(), field.SrcName(), u.SrcName(), field.SrcName())
|
||||||
|
c.Putln("func %s%sNew(%s %s) %s {",
|
||||||
|
u.SrcName(), field.SrcName(), field.SrcName(),
|
||||||
|
field.SrcType(), u.SrcName())
|
||||||
|
c.Putln("var b int")
|
||||||
|
c.Putln("buf := make([]byte, %s)", u.Size())
|
||||||
|
c.Putln("")
|
||||||
|
field.Write(c, "")
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("// Create the Union type")
|
||||||
|
c.Putln("v := %s{}", u.SrcName())
|
||||||
|
c.Putln("")
|
||||||
|
c.Putln("// Now copy buf into all fields")
|
||||||
|
c.Putln("")
|
||||||
|
for _, field2 := range u.Fields {
|
||||||
|
c.Putln("b = 0 // always read the same bytes")
|
||||||
|
field2.Read(c, "v.")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
c.Putln("return v")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Union) Read(c *Context) {
|
||||||
|
c.Putln("// %sRead reads a byte slice into a %s value.",
|
||||||
|
u.SrcName(), u.SrcName())
|
||||||
|
c.Putln("func %sRead(buf []byte, v *%s) int {", u.SrcName(), u.SrcName())
|
||||||
|
c.Putln("var b int")
|
||||||
|
c.Putln("")
|
||||||
|
for _, field := range u.Fields {
|
||||||
|
c.Putln("b = 0 // re-read the same bytes")
|
||||||
|
field.Read(c, "v.")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
c.Putln("return %s", u.Size())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Union) ReadList(c *Context) {
|
||||||
|
c.Putln("// %sReadList reads a byte slice into a list of %s values.",
|
||||||
|
u.SrcName(), u.SrcName())
|
||||||
|
c.Putln("func %sReadList(buf []byte, dest []%s) int {",
|
||||||
|
u.SrcName(), u.SrcName())
|
||||||
|
c.Putln("b := 0")
|
||||||
|
c.Putln("for i := 0; i < len(dest); i++ {")
|
||||||
|
c.Putln("dest[i] = %s{}", u.SrcName())
|
||||||
|
c.Putln("b += %sRead(buf[b:], &dest[i])", u.SrcName())
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("return xgb.Pad(b)")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a bit tricky since writing from a Union implies that only
|
||||||
|
// the data inside ONE of the elements is actually written.
|
||||||
|
// However, we only currently support unions where every field has the
|
||||||
|
// *same* *fixed* size. Thus, we make sure to always read bytes into
|
||||||
|
// every field which allows us to simply pick the first field and write it.
|
||||||
|
func (u *Union) Write(c *Context) {
|
||||||
|
c.Putln("// Bytes writes a %s value to a byte slice.", u.SrcName())
|
||||||
|
c.Putln("// Each field in a union must contain the same data.")
|
||||||
|
c.Putln("// So simply pick the first field and write that to the wire.")
|
||||||
|
c.Putln("func (v %s) Bytes() []byte {", u.SrcName())
|
||||||
|
c.Putln("buf := make([]byte, %s)", u.Size().Reduce("v."))
|
||||||
|
c.Putln("b := 0")
|
||||||
|
c.Putln("")
|
||||||
|
u.Fields[0].Write(c, "v.")
|
||||||
|
c.Putln("return buf")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Union) WriteList(c *Context) {
|
||||||
|
c.Putln("// %sListBytes writes a list of %s values to a byte slice.",
|
||||||
|
u.SrcName(), u.SrcName())
|
||||||
|
c.Putln("func %sListBytes(buf []byte, list []%s) int {",
|
||||||
|
u.SrcName(), u.SrcName())
|
||||||
|
c.Putln("b := 0")
|
||||||
|
c.Putln("var unionBytes []byte")
|
||||||
|
c.Putln("for _, item := range list {")
|
||||||
|
c.Putln("unionBytes = item.Bytes()")
|
||||||
|
c.Putln("copy(buf[b:], unionBytes)")
|
||||||
|
c.Putln("b += xgb.Pad(len(unionBytes))")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("return b")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Union) WriteListSize(c *Context) {
|
||||||
|
c.Putln("// Union list size %s", u.SrcName())
|
||||||
|
c.Putln("// %sListSize computes the size (bytes) of a list of %s values.",
|
||||||
|
u.SrcName())
|
||||||
|
c.Putln("func %sListSize(list []%s) int {", u.SrcName(), u.SrcName())
|
||||||
|
c.Putln("size := 0")
|
||||||
|
c.Putln("for _, item := range list {")
|
||||||
|
c.Putln("size += %s", u.Size().Reduce("item."))
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("return size")
|
||||||
|
c.Putln("}")
|
||||||
|
c.Putln("")
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
protoPath = flag.String("proto-path",
|
||||||
|
"/usr/share/xcb", "path to directory of X protocol XML files")
|
||||||
|
gofmt = flag.Bool("gofmt", true,
|
||||||
|
"When disabled, gofmt will not be run before outputting Go code")
|
||||||
|
)
|
||||||
|
|
||||||
|
func usage() {
|
||||||
|
basename := os.Args[0]
|
||||||
|
if lastSlash := strings.LastIndex(basename, "/"); lastSlash > -1 {
|
||||||
|
basename = basename[lastSlash+1:]
|
||||||
|
}
|
||||||
|
log.Printf("Usage: %s [flags] xml-file", basename)
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = usage
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if flag.NArg() != 1 {
|
||||||
|
log.Printf("A single XML protocol file can be processed at once.")
|
||||||
|
flag.Usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the single XML file into []byte
|
||||||
|
xmlBytes, err := ioutil.ReadFile(flag.Arg(0))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the buffer, parse it, and filter it through gofmt.
|
||||||
|
c := newContext()
|
||||||
|
c.Morph(xmlBytes)
|
||||||
|
|
||||||
|
if !*gofmt {
|
||||||
|
c.out.WriteTo(os.Stdout)
|
||||||
|
} else {
|
||||||
|
cmdGofmt := exec.Command("gofmt")
|
||||||
|
cmdGofmt.Stdin = c.out
|
||||||
|
cmdGofmt.Stdout = os.Stdout
|
||||||
|
cmdGofmt.Stderr = os.Stderr
|
||||||
|
err = cmdGofmt.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AllCaps is a regex to test if a string identifier is made of
|
||||||
|
// all upper case letters.
|
||||||
|
var allCaps = regexp.MustCompile("^[A-Z0-9]+$")
|
||||||
|
|
||||||
|
// popCount counts number of bits 'set' in mask.
|
||||||
|
func popCount(mask uint) uint {
|
||||||
|
m := uint32(mask)
|
||||||
|
n := uint(0)
|
||||||
|
for i := uint32(0); i < 32; i++ {
|
||||||
|
if m&(1<<i) != 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// pad makes sure 'n' aligns on 4 bytes.
|
||||||
|
func pad(n int) int {
|
||||||
|
return (n + 3) & ^3
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitAndTitle takes a string, splits it by underscores, capitalizes the
|
||||||
|
// first letter of each chunk, and smushes'em back together.
|
||||||
|
func splitAndTitle(s string) string {
|
||||||
|
// If the string is all caps, lower it and capitalize first letter.
|
||||||
|
if allCaps.MatchString(s) {
|
||||||
|
return strings.Title(strings.ToLower(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the string has no underscores, capitalize it and leave it be.
|
||||||
|
if i := strings.Index(s, "_"); i == -1 {
|
||||||
|
return strings.Title(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now split the name at underscores, capitalize the first
|
||||||
|
// letter of each chunk, and smush'em back together.
|
||||||
|
chunks := strings.Split(s, "_")
|
||||||
|
for i, chunk := range chunks {
|
||||||
|
chunks[i] = strings.Title(strings.ToLower(chunk))
|
||||||
|
}
|
||||||
|
return strings.Join(chunks, "")
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Protocol is a type that encapsulates all information about one
|
||||||
|
// particular XML file. It also contains links to other protocol types
|
||||||
|
// if this protocol imports other other extensions. The import relationship
|
||||||
|
// is recursive.
|
||||||
|
type Protocol struct {
|
||||||
|
Parent *Protocol
|
||||||
|
Name string
|
||||||
|
ExtXName string
|
||||||
|
ExtName string
|
||||||
|
MajorVersion string
|
||||||
|
MinorVersion string
|
||||||
|
|
||||||
|
Imports []*Protocol
|
||||||
|
Types []Type
|
||||||
|
Requests []*Request
|
||||||
|
}
|
||||||
|
|
||||||
|
type Protocols []*Protocol
|
||||||
|
|
||||||
|
func (ps Protocols) Len() int { return len(ps) }
|
||||||
|
func (ps Protocols) Swap(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
|
||||||
|
func (ps Protocols) Less(i, j int) bool { return ps[i].ExtName < ps[j].ExtName }
|
||||||
|
|
||||||
|
// Initialize traverses all structures, looks for 'Translation' type,
|
||||||
|
// and looks up the real type in the namespace. It also sets the source
|
||||||
|
// name for all relevant fields/structures.
|
||||||
|
// This is necessary because we don't traverse the XML in order initially.
|
||||||
|
func (p *Protocol) Initialize() {
|
||||||
|
for _, typ := range p.Types {
|
||||||
|
typ.Initialize(p)
|
||||||
|
}
|
||||||
|
for _, req := range p.Requests {
|
||||||
|
req.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PkgName returns the name of this package.
|
||||||
|
// i.e., 'xproto' for the core X protocol, 'randr' for the RandR extension, etc.
|
||||||
|
func (p *Protocol) PkgName() string {
|
||||||
|
return strings.Replace(p.Name, "_", "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProtocolGet searches the current context for the protocol with the given
|
||||||
|
// name. (i.e., the current protocol and its imports.)
|
||||||
|
// It is an error if one is not found.
|
||||||
|
func (p *Protocol) ProtocolFind(name string) *Protocol {
|
||||||
|
if p.Name == name {
|
||||||
|
return p // that was easy
|
||||||
|
}
|
||||||
|
for _, imp := range p.Imports {
|
||||||
|
if imp.Name == name {
|
||||||
|
return imp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Panicf("Could not find protocol with name '%s'.", name)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isExt returns true if this protocol is an extension.
|
||||||
|
// i.e., it's name isn't "xproto".
|
||||||
|
func (p *Protocol) isExt() bool {
|
||||||
|
return strings.ToLower(p.Name) != "xproto"
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Request represents all XML 'request' nodes.
|
||||||
|
// If the request doesn't have a reply, Reply is nil.
|
||||||
|
type Request struct {
|
||||||
|
srcName string // The Go name of this request.
|
||||||
|
xmlName string // The XML name of this request.
|
||||||
|
Opcode int
|
||||||
|
Combine bool // Not currently used.
|
||||||
|
Fields []Field // All fields in the request.
|
||||||
|
Reply *Reply // A reply, if one exists for this request.
|
||||||
|
}
|
||||||
|
|
||||||
|
type Requests []*Request
|
||||||
|
|
||||||
|
func (rs Requests) Len() int { return len(rs) }
|
||||||
|
func (rs Requests) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] }
|
||||||
|
func (rs Requests) Less(i, j int) bool { return rs[i].xmlName < rs[j].xmlName }
|
||||||
|
|
||||||
|
// Initialize creates the proper Go source name for this request.
|
||||||
|
// It also initializes the reply if one exists, and all fields in this request.
|
||||||
|
func (r *Request) Initialize(p *Protocol) {
|
||||||
|
r.srcName = SrcName(p, r.xmlName)
|
||||||
|
if p.isExt() {
|
||||||
|
r.srcName = r.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Reply != nil {
|
||||||
|
r.Reply.Initialize(p)
|
||||||
|
}
|
||||||
|
for _, field := range r.Fields {
|
||||||
|
field.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) SrcName() string {
|
||||||
|
return r.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) XmlName() string {
|
||||||
|
return r.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplyName gets the Go source name of the function that generates a
|
||||||
|
// reply type from a slice of bytes.
|
||||||
|
// The generated function is not currently exported.
|
||||||
|
func (r *Request) ReplyName() string {
|
||||||
|
if r.Reply == nil {
|
||||||
|
log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
|
||||||
|
r.SrcName())
|
||||||
|
}
|
||||||
|
name := r.SrcName()
|
||||||
|
lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
|
||||||
|
return fmt.Sprintf("%sReply", lower)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplyTypeName gets the Go source name of the type holding all reply data
|
||||||
|
// for this request.
|
||||||
|
func (r *Request) ReplyTypeName() string {
|
||||||
|
if r.Reply == nil {
|
||||||
|
log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
|
||||||
|
r.SrcName())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%sReply", r.SrcName())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReqName gets the Go source name of the function that generates a byte
|
||||||
|
// slice from a list of parameters.
|
||||||
|
// The generated function is not currently exported.
|
||||||
|
func (r *Request) ReqName() string {
|
||||||
|
name := r.SrcName()
|
||||||
|
lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
|
||||||
|
return fmt.Sprintf("%sRequest", lower)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CookieName gets the Go source name of the type that holds cookies for
|
||||||
|
// this request.
|
||||||
|
func (r *Request) CookieName() string {
|
||||||
|
return fmt.Sprintf("%sCookie", r.SrcName())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size for Request needs a context.
|
||||||
|
// Namely, if this is an extension, we need to account for *four* bytes
|
||||||
|
// of a header (extension opcode, request opcode, and the sequence number).
|
||||||
|
// If it's a core protocol request, then we only account for *three*
|
||||||
|
// bytes of the header (remove the extension opcode).
|
||||||
|
func (r *Request) Size(c *Context) Size {
|
||||||
|
size := newFixedSize(0, true)
|
||||||
|
|
||||||
|
// If this is a core protocol request, we squeeze in an extra byte of
|
||||||
|
// data (from the fields below) between the opcode and the size of the
|
||||||
|
// request. In an extension request, this byte is always occupied
|
||||||
|
// by the opcode of the request (while the first byte is always occupied
|
||||||
|
// by the opcode of the extension).
|
||||||
|
if !c.protocol.isExt() {
|
||||||
|
size = size.Add(newFixedSize(3, true))
|
||||||
|
} else {
|
||||||
|
size = size.Add(newFixedSize(4, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, field := range r.Fields {
|
||||||
|
switch field := field.(type) {
|
||||||
|
case *LocalField: // local fields don't go over the wire
|
||||||
|
continue
|
||||||
|
case *SingleField:
|
||||||
|
fsz := field.Size()
|
||||||
|
if _, isstruct := field.Type.(*Struct); isstruct {
|
||||||
|
fsz.Expression = fsz.Expression.Specialize(field.SrcName())
|
||||||
|
}
|
||||||
|
size = size.Add(fsz)
|
||||||
|
default:
|
||||||
|
size = size.Add(field.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newExpressionSize(&Padding{
|
||||||
|
Expr: size.Expression,
|
||||||
|
}, size.exact)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply encapsulates the fields associated with a 'reply' element.
|
||||||
|
type Reply struct {
|
||||||
|
Fields []Field
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size gets the number of bytes in this request's reply.
|
||||||
|
// A reply always has at least 7 bytes:
|
||||||
|
// 1 byte: A reply discriminant (first byte set to 1)
|
||||||
|
// 2 bytes: A sequence number
|
||||||
|
// 4 bytes: Number of additional bytes in 4-byte units past initial 32 bytes.
|
||||||
|
func (r *Reply) Size() Size {
|
||||||
|
size := newFixedSize(0, true)
|
||||||
|
|
||||||
|
// Account for reply discriminant, sequence number and reply length
|
||||||
|
size = size.Add(newFixedSize(7, true))
|
||||||
|
|
||||||
|
for _, field := range r.Fields {
|
||||||
|
size = size.Add(field.Size())
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reply) Initialize(p *Protocol) {
|
||||||
|
for _, field := range r.Fields {
|
||||||
|
field.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Size corresponds to an expression that represents the number of bytes
|
||||||
|
// in some *thing*. Generally, sizes are used to allocate buffers and to
|
||||||
|
// inform X how big requests are.
|
||||||
|
// Size is basically a thin layer over an Expression that yields easy methods
|
||||||
|
// for adding and multiplying sizes.
|
||||||
|
type Size struct {
|
||||||
|
Expression
|
||||||
|
exact bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFixedSize creates a new Size with some fixed and known value.
|
||||||
|
func newFixedSize(fixed uint, exact bool) Size {
|
||||||
|
return Size{&Value{v: int(fixed)}, exact}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newExpressionSize creates a new Size with some expression.
|
||||||
|
func newExpressionSize(variable Expression, exact bool) Size {
|
||||||
|
return Size{variable, exact}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds s1 and s2 and returns a new Size.
|
||||||
|
func (s1 Size) Add(s2 Size) Size {
|
||||||
|
return Size{newBinaryOp("+", s1, s2), s1.exact && s2.exact}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply mupltiplies s1 and s2 and returns a new Size.
|
||||||
|
func (s1 Size) Multiply(s2 Size) Size {
|
||||||
|
return Size{newBinaryOp("*", s1, s2), s1.exact && s2.exact}
|
||||||
|
}
|
|
@ -0,0 +1,427 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
translation.go provides a 'Translate' method on every XML type that converts
|
||||||
|
the XML type into our "better" representation.
|
||||||
|
|
||||||
|
i.e., the representation of Fields and Expressions is just too general.
|
||||||
|
We end up losing a lot of the advantages of static typing if we keep
|
||||||
|
the types that encoding/xml forces us into.
|
||||||
|
|
||||||
|
Please see 'representation.go' for the type definitions that we're
|
||||||
|
translating to.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (xml *XML) Translate(parent *Protocol) *Protocol {
|
||||||
|
protocol := &Protocol{
|
||||||
|
Parent: parent,
|
||||||
|
Name: xml.Header,
|
||||||
|
ExtXName: xml.ExtensionXName,
|
||||||
|
ExtName: xml.ExtensionName,
|
||||||
|
MajorVersion: xml.MajorVersion,
|
||||||
|
MinorVersion: xml.MinorVersion,
|
||||||
|
|
||||||
|
Imports: make([]*Protocol, 0),
|
||||||
|
Types: make([]Type, 0),
|
||||||
|
Requests: make([]*Request, len(xml.Requests)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, imp := range xml.Imports {
|
||||||
|
if imp.xml != nil {
|
||||||
|
protocol.Imports = append(protocol.Imports,
|
||||||
|
imp.xml.Translate(protocol))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for xmlName, srcName := range BaseTypeMap {
|
||||||
|
newBaseType := &Base{
|
||||||
|
srcName: srcName,
|
||||||
|
xmlName: xmlName,
|
||||||
|
size: newFixedSize(BaseTypeSizes[xmlName], true),
|
||||||
|
}
|
||||||
|
protocol.Types = append(protocol.Types, newBaseType)
|
||||||
|
}
|
||||||
|
for _, enum := range xml.Enums {
|
||||||
|
protocol.Types = append(protocol.Types, enum.Translate())
|
||||||
|
}
|
||||||
|
for _, xid := range xml.Xids {
|
||||||
|
protocol.Types = append(protocol.Types, xid.Translate())
|
||||||
|
}
|
||||||
|
for _, xidunion := range xml.XidUnions {
|
||||||
|
protocol.Types = append(protocol.Types, xidunion.Translate())
|
||||||
|
}
|
||||||
|
for _, typedef := range xml.TypeDefs {
|
||||||
|
protocol.Types = append(protocol.Types, typedef.Translate())
|
||||||
|
}
|
||||||
|
for _, s := range xml.Structs {
|
||||||
|
protocol.Types = append(protocol.Types, s.Translate())
|
||||||
|
}
|
||||||
|
for _, union := range xml.Unions {
|
||||||
|
protocol.Types = append(protocol.Types, union.Translate())
|
||||||
|
}
|
||||||
|
for _, ev := range xml.Events {
|
||||||
|
protocol.Types = append(protocol.Types, ev.Translate())
|
||||||
|
}
|
||||||
|
for _, evcopy := range xml.EventCopies {
|
||||||
|
protocol.Types = append(protocol.Types, evcopy.Translate())
|
||||||
|
}
|
||||||
|
for _, err := range xml.Errors {
|
||||||
|
protocol.Types = append(protocol.Types, err.Translate())
|
||||||
|
}
|
||||||
|
for _, errcopy := range xml.ErrorCopies {
|
||||||
|
protocol.Types = append(protocol.Types, errcopy.Translate())
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, request := range xml.Requests {
|
||||||
|
protocol.Requests[i] = request.Translate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now load all of the type and source name information.
|
||||||
|
protocol.Initialize()
|
||||||
|
|
||||||
|
// Make sure all enums have concrete values.
|
||||||
|
for _, typ := range protocol.Types {
|
||||||
|
enum, ok := typ.(*Enum)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nextValue := 0
|
||||||
|
for _, item := range enum.Items {
|
||||||
|
if item.Expr == nil {
|
||||||
|
item.Expr = &Value{v: nextValue}
|
||||||
|
nextValue++
|
||||||
|
} else {
|
||||||
|
nextValue = item.Expr.Eval() + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLEnum) Translate() *Enum {
|
||||||
|
enum := &Enum{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Items: make([]*EnumItem, len(x.Items)),
|
||||||
|
}
|
||||||
|
for i, item := range x.Items {
|
||||||
|
enum.Items[i] = &EnumItem{
|
||||||
|
xmlName: item.Name,
|
||||||
|
Expr: item.Expr.Translate(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLXid) Translate() *Resource {
|
||||||
|
return &Resource{
|
||||||
|
xmlName: x.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLTypeDef) Translate() *TypeDef {
|
||||||
|
return &TypeDef{
|
||||||
|
xmlName: x.New,
|
||||||
|
Old: newTranslation(x.Old),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLEvent) Translate() *Event {
|
||||||
|
ev := &Event{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Number: x.Number,
|
||||||
|
NoSequence: x.NoSequence,
|
||||||
|
Fields: make([]Field, 0, len(x.Fields)),
|
||||||
|
}
|
||||||
|
for _, field := range x.Fields {
|
||||||
|
if field.XMLName.Local == "doc" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ev.Fields = append(ev.Fields, field.Translate(ev))
|
||||||
|
}
|
||||||
|
return ev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLEventCopy) Translate() *EventCopy {
|
||||||
|
return &EventCopy{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Number: x.Number,
|
||||||
|
Old: newTranslation(x.Ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLError) Translate() *Error {
|
||||||
|
err := &Error{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Number: x.Number,
|
||||||
|
Fields: make([]Field, len(x.Fields)),
|
||||||
|
}
|
||||||
|
for i, field := range x.Fields {
|
||||||
|
err.Fields[i] = field.Translate(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLErrorCopy) Translate() *ErrorCopy {
|
||||||
|
return &ErrorCopy{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Number: x.Number,
|
||||||
|
Old: newTranslation(x.Ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLStruct) Translate() *Struct {
|
||||||
|
s := &Struct{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Fields: make([]Field, len(x.Fields)),
|
||||||
|
}
|
||||||
|
for i, field := range x.Fields {
|
||||||
|
s.Fields[i] = field.Translate(s)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLUnion) Translate() *Union {
|
||||||
|
u := &Union{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Fields: make([]Field, len(x.Fields)),
|
||||||
|
}
|
||||||
|
for i, field := range x.Fields {
|
||||||
|
u.Fields[i] = field.Translate(u)
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLRequest) Translate() *Request {
|
||||||
|
r := &Request{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Opcode: x.Opcode,
|
||||||
|
Combine: x.Combine,
|
||||||
|
Fields: make([]Field, 0, len(x.Fields)),
|
||||||
|
Reply: x.Reply.Translate(),
|
||||||
|
}
|
||||||
|
for _, field := range x.Fields {
|
||||||
|
if field.XMLName.Local == "doc" || field.XMLName.Local == "fd" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.Fields = append(r.Fields, field.Translate(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address bug (or legacy code) in QueryTextExtents.
|
||||||
|
// The XML protocol description references 'string_len' in the
|
||||||
|
// computation of the 'odd_length' field. However, 'string_len' is not
|
||||||
|
// defined. Therefore, let's forcefully add it as a 'local field'.
|
||||||
|
// (i.e., a parameter in the caller but does not get sent over the wire.)
|
||||||
|
if x.Name == "QueryTextExtents" {
|
||||||
|
stringLenLocal := &LocalField{&SingleField{
|
||||||
|
xmlName: "string_len",
|
||||||
|
Type: newTranslation("CARD16"),
|
||||||
|
}}
|
||||||
|
r.Fields = append(r.Fields, stringLenLocal)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLReply) Translate() *Reply {
|
||||||
|
if x == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &Reply{
|
||||||
|
Fields: make([]Field, 0, len(x.Fields)),
|
||||||
|
}
|
||||||
|
for _, field := range x.Fields {
|
||||||
|
if field.XMLName.Local == "doc" || field.XMLName.Local == "fd" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.Fields = append(r.Fields, field.Translate(r))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLExpression) Translate() Expression {
|
||||||
|
if x == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x.XMLName.Local {
|
||||||
|
case "op":
|
||||||
|
if len(x.Exprs) != 2 {
|
||||||
|
log.Panicf("'op' found %d expressions; expected 2.", len(x.Exprs))
|
||||||
|
}
|
||||||
|
return &BinaryOp{
|
||||||
|
Op: x.Op,
|
||||||
|
Expr1: x.Exprs[0].Translate(),
|
||||||
|
Expr2: x.Exprs[1].Translate(),
|
||||||
|
}
|
||||||
|
case "unop":
|
||||||
|
if len(x.Exprs) != 1 {
|
||||||
|
log.Panicf("'unop' found %d expressions; expected 1.", len(x.Exprs))
|
||||||
|
}
|
||||||
|
return &UnaryOp{
|
||||||
|
Op: x.Op,
|
||||||
|
Expr: x.Exprs[0].Translate(),
|
||||||
|
}
|
||||||
|
case "popcount":
|
||||||
|
if len(x.Exprs) != 1 {
|
||||||
|
log.Panicf("'popcount' found %d expressions; expected 1.",
|
||||||
|
len(x.Exprs))
|
||||||
|
}
|
||||||
|
return &PopCount{
|
||||||
|
Expr: x.Exprs[0].Translate(),
|
||||||
|
}
|
||||||
|
case "value":
|
||||||
|
val, err := strconv.Atoi(strings.TrimSpace(x.Data))
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("Could not convert '%s' in 'value' expression to int.",
|
||||||
|
x.Data)
|
||||||
|
}
|
||||||
|
return &Value{
|
||||||
|
v: val,
|
||||||
|
}
|
||||||
|
case "bit":
|
||||||
|
bit, err := strconv.Atoi(strings.TrimSpace(x.Data))
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("Could not convert '%s' in 'bit' expression to int.",
|
||||||
|
x.Data)
|
||||||
|
}
|
||||||
|
if bit < 0 || bit > 31 {
|
||||||
|
log.Panicf("A 'bit' literal must be in the range [0, 31], but "+
|
||||||
|
" is %d", bit)
|
||||||
|
}
|
||||||
|
return &Bit{
|
||||||
|
b: bit,
|
||||||
|
}
|
||||||
|
case "fieldref":
|
||||||
|
return &FieldRef{
|
||||||
|
Name: x.Data,
|
||||||
|
}
|
||||||
|
case "enumref":
|
||||||
|
return &EnumRef{
|
||||||
|
EnumKind: newTranslation(x.Ref),
|
||||||
|
EnumItem: x.Data,
|
||||||
|
}
|
||||||
|
case "sumof":
|
||||||
|
return &SumOf{
|
||||||
|
Name: x.Ref,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Panicf("Unrecognized tag '%s' in expression context. Expected one of "+
|
||||||
|
"op, fieldref, value, bit, enumref, unop, sumof or popcount.",
|
||||||
|
x.XMLName.Local)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLField) Translate(parent interface{}) Field {
|
||||||
|
switch x.XMLName.Local {
|
||||||
|
case "pad":
|
||||||
|
return &PadField{
|
||||||
|
Bytes: x.Bytes,
|
||||||
|
Align: x.Align,
|
||||||
|
}
|
||||||
|
case "field":
|
||||||
|
s := &SingleField{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Type: newTranslation(x.Type),
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
case "list":
|
||||||
|
return &ListField{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Type: newTranslation(x.Type),
|
||||||
|
LengthExpr: x.Expr.Translate(),
|
||||||
|
}
|
||||||
|
case "localfield":
|
||||||
|
return &LocalField{&SingleField{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Type: newTranslation(x.Type),
|
||||||
|
}}
|
||||||
|
case "exprfield":
|
||||||
|
return &ExprField{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Type: newTranslation(x.Type),
|
||||||
|
Expr: x.Expr.Translate(),
|
||||||
|
}
|
||||||
|
case "valueparam":
|
||||||
|
return &ValueField{
|
||||||
|
Parent: parent,
|
||||||
|
MaskType: newTranslation(x.ValueMaskType),
|
||||||
|
MaskName: x.ValueMaskName,
|
||||||
|
ListName: x.ValueListName,
|
||||||
|
}
|
||||||
|
case "switch":
|
||||||
|
swtch := &SwitchField{
|
||||||
|
Name: x.Name,
|
||||||
|
Expr: x.Expr.Translate(),
|
||||||
|
Bitcases: make([]*Bitcase, len(x.Bitcases)),
|
||||||
|
}
|
||||||
|
for i, bitcase := range x.Bitcases {
|
||||||
|
swtch.Bitcases[i] = bitcase.Translate()
|
||||||
|
}
|
||||||
|
return swtch
|
||||||
|
case "required_start_align":
|
||||||
|
return &RequiredStartAlign{}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Panicf("Unrecognized field element: %s", x.XMLName.Local)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLBitcase) Translate() *Bitcase {
|
||||||
|
b := &Bitcase{
|
||||||
|
Expr: x.Expr().Translate(),
|
||||||
|
Fields: make([]Field, len(x.Fields)),
|
||||||
|
}
|
||||||
|
for i, field := range x.Fields {
|
||||||
|
b.Fields[i] = field.Translate(b)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// SrcName is used to translate any identifier into a Go name.
|
||||||
|
// Mostly used for fields, but used in a couple other places too (enum items).
|
||||||
|
func SrcName(p *Protocol, name string) string {
|
||||||
|
// If it's in the name map, use that translation.
|
||||||
|
if newn, ok := NameMap[name]; ok {
|
||||||
|
return newn
|
||||||
|
}
|
||||||
|
return splitAndTitle(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TypeSrcName(p *Protocol, typ Type) string {
|
||||||
|
t := typ.XmlName()
|
||||||
|
|
||||||
|
// If this is a base type, then write the raw Go type.
|
||||||
|
if baseType, ok := typ.(*Base); ok {
|
||||||
|
return baseType.SrcName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's in the type map, use that translation.
|
||||||
|
if newt, ok := TypeMap[t]; ok {
|
||||||
|
return newt
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's a namespace to this type, just use it and be done.
|
||||||
|
if colon := strings.Index(t, ":"); colon > -1 {
|
||||||
|
namespace := t[:colon]
|
||||||
|
rest := t[colon+1:]
|
||||||
|
return p.ProtocolFind(namespace).PkgName() + "." + splitAndTitle(rest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since there's no namespace, we're left with the raw type name.
|
||||||
|
// If the type is part of the source we're generating (i.e., there is
|
||||||
|
// no parent protocol), then just return that type name.
|
||||||
|
// Otherwise, we must qualify it with a package name.
|
||||||
|
if p.Parent == nil {
|
||||||
|
return splitAndTitle(t)
|
||||||
|
}
|
||||||
|
return p.PkgName() + "." + splitAndTitle(t)
|
||||||
|
}
|
|
@ -0,0 +1,390 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Type interface {
|
||||||
|
Initialize(p *Protocol)
|
||||||
|
SrcName() string
|
||||||
|
XmlName() string
|
||||||
|
Size() Size
|
||||||
|
|
||||||
|
Define(c *Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Types []Type
|
||||||
|
|
||||||
|
func (ts Types) Len() int { return len(ts) }
|
||||||
|
func (ts Types) Swap(i, j int) { ts[i], ts[j] = ts[j], ts[i] }
|
||||||
|
func (ts Types) Less(i, j int) bool {
|
||||||
|
x1, x2 := ts[i].XmlName(), ts[j].XmlName()
|
||||||
|
s1, s2 := ts[i].SrcName(), ts[j].SrcName()
|
||||||
|
return (s1 == s2 && x1 < x2) || s1 < s2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translation is used *only* when transitioning from XML types to
|
||||||
|
// our better representation. They are placeholders for the real types (below)
|
||||||
|
// that will replace them.
|
||||||
|
type Translation struct {
|
||||||
|
xmlName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTranslation(name string) *Translation {
|
||||||
|
return &Translation{xmlName: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealType takes 'XmlName' and finds its real concrete type in our Protocol.
|
||||||
|
// It is an error if we can't find such a type.
|
||||||
|
func (t *Translation) RealType(p *Protocol) Type {
|
||||||
|
// Check to see if there is a namespace. If so, strip it and use it to
|
||||||
|
// make sure we only look for a type in that protocol.
|
||||||
|
namespace, typeName := "", t.XmlName()
|
||||||
|
if ni := strings.Index(t.XmlName(), ":"); ni > -1 {
|
||||||
|
namespace, typeName = strings.ToLower(typeName[:ni]), typeName[ni+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(namespace) == 0 || namespace == strings.ToLower(p.Name) {
|
||||||
|
for _, typ := range p.Types {
|
||||||
|
if typeName == typ.XmlName() {
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, imp := range p.Imports {
|
||||||
|
if len(namespace) == 0 || namespace == strings.ToLower(imp.Name) {
|
||||||
|
for _, typ := range imp.Types {
|
||||||
|
if typeName == typ.XmlName() {
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("Could not find real type for translation type: " + t.XmlName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Translation) SrcName() string {
|
||||||
|
panic("it is illegal to call SrcName on a translation type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Translation) XmlName() string {
|
||||||
|
return t.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Translation) Size() Size {
|
||||||
|
panic("it is illegal to call Size on a translation type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Translation) Define(c *Context) {
|
||||||
|
panic("it is illegal to call Define on a translation type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Translation) Initialize(p *Protocol) {
|
||||||
|
panic("it is illegal to call Initialize on a translation type")
|
||||||
|
}
|
||||||
|
|
||||||
|
type Base struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
size Size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) SrcName() string {
|
||||||
|
return b.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) XmlName() string {
|
||||||
|
return b.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) Size() Size {
|
||||||
|
return b.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) Initialize(p *Protocol) {
|
||||||
|
b.srcName = TypeSrcName(p, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Enum struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Items []*EnumItem
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnumItem struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Expr Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum *Enum) SrcName() string {
|
||||||
|
return enum.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum *Enum) XmlName() string {
|
||||||
|
return enum.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum *Enum) Size() Size {
|
||||||
|
panic("Cannot take size of enum")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum *Enum) Initialize(p *Protocol) {
|
||||||
|
enum.srcName = TypeSrcName(p, enum)
|
||||||
|
for _, item := range enum.Items {
|
||||||
|
item.srcName = SrcName(p, item.xmlName)
|
||||||
|
if item.Expr != nil {
|
||||||
|
item.Expr.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resource struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resource) SrcName() string {
|
||||||
|
return r.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resource) XmlName() string {
|
||||||
|
return r.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resource) Size() Size {
|
||||||
|
return newFixedSize(BaseTypeSizes["Id"], true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resource) Initialize(p *Protocol) {
|
||||||
|
r.srcName = TypeSrcName(p, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeDef struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Old Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypeDef) SrcName() string {
|
||||||
|
return t.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypeDef) XmlName() string {
|
||||||
|
return t.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypeDef) Size() Size {
|
||||||
|
return t.Old.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypeDef) Initialize(p *Protocol) {
|
||||||
|
t.Old = t.Old.(*Translation).RealType(p)
|
||||||
|
t.srcName = TypeSrcName(p, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Number int
|
||||||
|
NoSequence bool
|
||||||
|
Fields []Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Event) SrcName() string {
|
||||||
|
return e.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Event) XmlName() string {
|
||||||
|
return e.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Event) Size() Size {
|
||||||
|
return newExpressionSize(&Value{v: 32}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Event) Initialize(p *Protocol) {
|
||||||
|
e.srcName = TypeSrcName(p, e)
|
||||||
|
for _, field := range e.Fields {
|
||||||
|
field.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Event) EvType() string {
|
||||||
|
return fmt.Sprintf("%sEvent", e.srcName)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventCopy struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Old Type
|
||||||
|
Number int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventCopy) SrcName() string {
|
||||||
|
return e.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventCopy) XmlName() string {
|
||||||
|
return e.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventCopy) Size() Size {
|
||||||
|
return newExpressionSize(&Value{v: 32}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventCopy) Initialize(p *Protocol) {
|
||||||
|
e.srcName = TypeSrcName(p, e)
|
||||||
|
e.Old = e.Old.(*Translation).RealType(p)
|
||||||
|
if _, ok := e.Old.(*Event); !ok {
|
||||||
|
panic("an EventCopy's old type *must* be *Event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventCopy) EvType() string {
|
||||||
|
return fmt.Sprintf("%sEvent", e.srcName)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Number int
|
||||||
|
Fields []Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) SrcName() string {
|
||||||
|
return e.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) XmlName() string {
|
||||||
|
return e.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Size() Size {
|
||||||
|
return newExpressionSize(&Value{v: 32}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Initialize(p *Protocol) {
|
||||||
|
e.srcName = TypeSrcName(p, e)
|
||||||
|
for _, field := range e.Fields {
|
||||||
|
field.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) ErrConst() string {
|
||||||
|
return fmt.Sprintf("Bad%s", e.srcName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) ErrType() string {
|
||||||
|
return fmt.Sprintf("%sError", e.srcName)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorCopy struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Old Type
|
||||||
|
Number int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorCopy) SrcName() string {
|
||||||
|
return e.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorCopy) XmlName() string {
|
||||||
|
return e.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorCopy) Size() Size {
|
||||||
|
return newExpressionSize(&Value{v: 32}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorCopy) Initialize(p *Protocol) {
|
||||||
|
e.srcName = TypeSrcName(p, e)
|
||||||
|
e.Old = e.Old.(*Translation).RealType(p)
|
||||||
|
if _, ok := e.Old.(*Error); !ok {
|
||||||
|
panic("an ErrorCopy's old type *must* be *Event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorCopy) ErrConst() string {
|
||||||
|
return fmt.Sprintf("Bad%s", e.srcName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorCopy) ErrType() string {
|
||||||
|
return fmt.Sprintf("%sError", e.srcName)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Struct struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Fields []Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) SrcName() string {
|
||||||
|
return s.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) XmlName() string {
|
||||||
|
return s.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) Size() Size {
|
||||||
|
size := newFixedSize(0, true)
|
||||||
|
for _, field := range s.Fields {
|
||||||
|
size = size.Add(field.Size())
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) Initialize(p *Protocol) {
|
||||||
|
s.srcName = TypeSrcName(p, s)
|
||||||
|
for _, field := range s.Fields {
|
||||||
|
field.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasList returns whether there is a field in this struct that is a list.
|
||||||
|
// When true, a more involved calculation is necessary to compute this struct's
|
||||||
|
// size.
|
||||||
|
func (s *Struct) HasList() bool {
|
||||||
|
for _, field := range s.Fields {
|
||||||
|
if _, ok := field.(*ListField); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type Union struct {
|
||||||
|
srcName string
|
||||||
|
xmlName string
|
||||||
|
Fields []Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Union) SrcName() string {
|
||||||
|
return u.srcName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Union) XmlName() string {
|
||||||
|
return u.xmlName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size for Union is broken. At least, it's broken for XKB.
|
||||||
|
// It *looks* like the protocol inherently relies on some amount of
|
||||||
|
// memory unsafety, since some members of unions in XKB are *variable* in
|
||||||
|
// length! The only thing I can come up with, maybe, is when a union has
|
||||||
|
// variable size, simply return the raw bytes. Then it's up to the user to
|
||||||
|
// pass those raw bytes into the appropriate New* constructor. GROSS!
|
||||||
|
// As of now, just pluck out the first field and return that size. This
|
||||||
|
// should work for union elements in randr.xml and xproto.xml.
|
||||||
|
func (u *Union) Size() Size {
|
||||||
|
return u.Fields[0].Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Union) Initialize(p *Protocol) {
|
||||||
|
u.srcName = fmt.Sprintf("%sUnion", TypeSrcName(p, u))
|
||||||
|
for _, field := range u.Fields {
|
||||||
|
field.Initialize(p)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XML struct {
|
||||||
|
// Root 'xcb' element properties.
|
||||||
|
XMLName xml.Name `xml:"xcb"`
|
||||||
|
Header string `xml:"header,attr"`
|
||||||
|
ExtensionXName string `xml:"extension-xname,attr"`
|
||||||
|
ExtensionName string `xml:"extension-name,attr"`
|
||||||
|
MajorVersion string `xml:"major-version,attr"`
|
||||||
|
MinorVersion string `xml:"minor-version,attr"`
|
||||||
|
|
||||||
|
// Types for all top-level elements.
|
||||||
|
// First are the simple ones.
|
||||||
|
Imports XMLImports `xml:"import"`
|
||||||
|
Enums []*XMLEnum `xml:"enum"`
|
||||||
|
Xids []*XMLXid `xml:"xidtype"`
|
||||||
|
XidUnions []*XMLXid `xml:"xidunion"`
|
||||||
|
TypeDefs []*XMLTypeDef `xml:"typedef"`
|
||||||
|
EventCopies []*XMLEventCopy `xml:"eventcopy"`
|
||||||
|
ErrorCopies []*XMLErrorCopy `xml:"errorcopy"`
|
||||||
|
|
||||||
|
// Here are the complex ones, i.e., anything with "structure contents"
|
||||||
|
Structs []*XMLStruct `xml:"struct"`
|
||||||
|
Unions []*XMLUnion `xml:"union"`
|
||||||
|
Requests []*XMLRequest `xml:"request"`
|
||||||
|
Events []*XMLEvent `xml:"event"`
|
||||||
|
Errors []*XMLError `xml:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLImports []*XMLImport
|
||||||
|
|
||||||
|
func (imports XMLImports) Eval() {
|
||||||
|
for _, imp := range imports {
|
||||||
|
xmlBytes, err := ioutil.ReadFile(*protoPath + "/" + imp.Name + ".xml")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not read X protocol description for import "+
|
||||||
|
"'%s' because: %s", imp.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
imp.xml = &XML{}
|
||||||
|
err = xml.Unmarshal(xmlBytes, imp.xml)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Could not parse X protocol description for import "+
|
||||||
|
"'%s' because: %s", imp.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// recursive imports...
|
||||||
|
imp.xml.Imports.Eval()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLImport struct {
|
||||||
|
Name string `xml:",chardata"`
|
||||||
|
xml *XML `xml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLEnum struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Items []*XMLEnumItem `xml:"item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLEnumItem struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Expr *XMLExpression `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLXid struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLTypeDef struct {
|
||||||
|
Old string `xml:"oldname,attr"`
|
||||||
|
New string `xml:"newname,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLEventCopy struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Number int `xml:"number,attr"`
|
||||||
|
Ref string `xml:"ref,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLErrorCopy struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Number int `xml:"number,attr"`
|
||||||
|
Ref string `xml:"ref,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLStruct struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Fields []*XMLField `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLUnion struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Fields []*XMLField `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLRequest struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Opcode int `xml:"opcode,attr"`
|
||||||
|
Combine bool `xml:"combine-adjacent,attr"`
|
||||||
|
Fields []*XMLField `xml:",any"`
|
||||||
|
Reply *XMLReply `xml:"reply"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLReply struct {
|
||||||
|
Fields []*XMLField `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLEvent struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Number int `xml:"number,attr"`
|
||||||
|
NoSequence bool `xml:"no-sequence-number,attr"`
|
||||||
|
Fields []*XMLField `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLError struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Number int `xml:"number,attr"`
|
||||||
|
Fields []*XMLField `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLExpression struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
|
||||||
|
Exprs []*XMLExpression `xml:",any"`
|
||||||
|
|
||||||
|
Data string `xml:",chardata"`
|
||||||
|
Op string `xml:"op,attr"`
|
||||||
|
Ref string `xml:"ref,attr"`
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XMLField struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
|
||||||
|
// For 'pad' element
|
||||||
|
Bytes uint `xml:"bytes,attr"`
|
||||||
|
Align uint16 `xml:"align,attr"`
|
||||||
|
|
||||||
|
// For 'field', 'list', 'localfield', 'exprfield' and 'switch' elements.
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
|
||||||
|
// For 'field', 'list', 'localfield', and 'exprfield' elements.
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
|
||||||
|
// For 'list', 'exprfield' and 'switch' elements.
|
||||||
|
Expr *XMLExpression `xml:",any"`
|
||||||
|
|
||||||
|
// For 'valueparm' element.
|
||||||
|
ValueMaskType string `xml:"value-mask-type,attr"`
|
||||||
|
ValueMaskName string `xml:"value-mask-name,attr"`
|
||||||
|
ValueListName string `xml:"value-list-name,attr"`
|
||||||
|
|
||||||
|
// For 'switch' element.
|
||||||
|
Bitcases []*XMLBitcase `xml:"bitcase"`
|
||||||
|
|
||||||
|
// I don't know which elements these are for. The documentation is vague.
|
||||||
|
// They also seem to be completely optional.
|
||||||
|
OptEnum string `xml:"enum,attr"`
|
||||||
|
OptMask string `xml:"mask,attr"`
|
||||||
|
OptAltEnum string `xml:"altenum,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitcase represents a single expression followed by any number of fields.
|
||||||
|
// Namely, if the switch's expression (all bitcases are inside a switch),
|
||||||
|
// and'd with the bitcase's expression is equal to the bitcase expression,
|
||||||
|
// then the fields should be included in its parent structure.
|
||||||
|
// Note that since a bitcase is unique in that expressions and fields are
|
||||||
|
// siblings, we must exhaustively search for one of them. Essentially,
|
||||||
|
// it's the closest thing to a Union I can get to in Go without interfaces.
|
||||||
|
// Would an '<expression>' tag have been too much to ask? :-(
|
||||||
|
type XMLBitcase struct {
|
||||||
|
Fields []*XMLField `xml:",any"`
|
||||||
|
|
||||||
|
// All the different expressions.
|
||||||
|
// When it comes time to choose one, use the 'Expr' method.
|
||||||
|
ExprOp *XMLExpression `xml:"op"`
|
||||||
|
ExprUnOp *XMLExpression `xml:"unop"`
|
||||||
|
ExprField *XMLExpression `xml:"fieldref"`
|
||||||
|
ExprValue *XMLExpression `xml:"value"`
|
||||||
|
ExprBit *XMLExpression `xml:"bit"`
|
||||||
|
ExprEnum *XMLExpression `xml:"enumref"`
|
||||||
|
ExprSum *XMLExpression `xml:"sumof"`
|
||||||
|
ExprPop *XMLExpression `xml:"popcount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expr chooses the only non-nil Expr* field from Bitcase.
|
||||||
|
// Panic if there is more than one non-nil expression.
|
||||||
|
func (b *XMLBitcase) Expr() *XMLExpression {
|
||||||
|
choices := []*XMLExpression{
|
||||||
|
b.ExprOp, b.ExprUnOp, b.ExprField, b.ExprValue,
|
||||||
|
b.ExprBit, b.ExprEnum, b.ExprSum, b.ExprPop,
|
||||||
|
}
|
||||||
|
|
||||||
|
var choice *XMLExpression = nil
|
||||||
|
numNonNil := 0
|
||||||
|
for _, c := range choices {
|
||||||
|
if c != nil {
|
||||||
|
numNonNil++
|
||||||
|
choice = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if choice == nil {
|
||||||
|
log.Panicf("No top level expression found in a bitcase.")
|
||||||
|
}
|
||||||
|
if numNonNil > 1 {
|
||||||
|
log.Panicf("More than one top-level expression was found in a bitcase.")
|
||||||
|
}
|
||||||
|
return choice
|
||||||
|
}
|
|
@ -0,0 +1,718 @@
|
||||||
|
// Package xinerama is the X client API for the XINERAMA extension.
|
||||||
|
package xinerama
|
||||||
|
|
||||||
|
// This file is automatically generated from xinerama.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the XINERAMA extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 8, "XINERAMA").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named XINERAMA could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["XINERAMA"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["XINERAMA"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["XINERAMA"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["XINERAMA"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["XINERAMA"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScreenInfo struct {
|
||||||
|
XOrg int16
|
||||||
|
YOrg int16
|
||||||
|
Width uint16
|
||||||
|
Height uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScreenInfoRead reads a byte slice into a ScreenInfo value.
|
||||||
|
func ScreenInfoRead(buf []byte, v *ScreenInfo) int {
|
||||||
|
b := 0
|
||||||
|
|
||||||
|
v.XOrg = int16(xgb.Get16(buf[b:]))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.YOrg = int16(xgb.Get16(buf[b:]))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Width = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Height = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScreenInfoReadList reads a byte slice into a list of ScreenInfo values.
|
||||||
|
func ScreenInfoReadList(buf []byte, dest []ScreenInfo) int {
|
||||||
|
b := 0
|
||||||
|
for i := 0; i < len(dest); i++ {
|
||||||
|
dest[i] = ScreenInfo{}
|
||||||
|
b += ScreenInfoRead(buf[b:], &dest[i])
|
||||||
|
}
|
||||||
|
return xgb.Pad(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes writes a ScreenInfo value to a byte slice.
|
||||||
|
func (v ScreenInfo) Bytes() []byte {
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
b := 0
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(v.XOrg))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(v.YOrg))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], v.Width)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], v.Height)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf[:b]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScreenInfoListBytes writes a list of ScreenInfo values to a byte slice.
|
||||||
|
func ScreenInfoListBytes(buf []byte, list []ScreenInfo) int {
|
||||||
|
b := 0
|
||||||
|
var structBytes []byte
|
||||||
|
for _, item := range list {
|
||||||
|
structBytes = item.Bytes()
|
||||||
|
copy(buf[b:], structBytes)
|
||||||
|
b += len(structBytes)
|
||||||
|
}
|
||||||
|
return xgb.Pad(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// GetScreenCountCookie is a cookie used only for GetScreenCount requests.
|
||||||
|
type GetScreenCountCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScreenCount sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetScreenCountCookie.Reply()
|
||||||
|
func GetScreenCount(c *xgb.Conn, Window xproto.Window) GetScreenCountCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetScreenCount' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getScreenCountRequest(c, Window), cookie)
|
||||||
|
return GetScreenCountCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScreenCountUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetScreenCountUnchecked(c *xgb.Conn, Window xproto.Window) GetScreenCountCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetScreenCount' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getScreenCountRequest(c, Window), cookie)
|
||||||
|
return GetScreenCountCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScreenCountReply represents the data returned from a GetScreenCount request.
|
||||||
|
type GetScreenCountReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
ScreenCount byte
|
||||||
|
Window xproto.Window
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetScreenCount request.
|
||||||
|
func (cook GetScreenCountCookie) Reply() (*GetScreenCountReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getScreenCountReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getScreenCountReply reads a byte slice into a GetScreenCountReply value.
|
||||||
|
func getScreenCountReply(buf []byte) *GetScreenCountReply {
|
||||||
|
v := new(GetScreenCountReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
v.ScreenCount = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetScreenCount
|
||||||
|
// getScreenCountRequest writes a GetScreenCount request to a byte slice.
|
||||||
|
func getScreenCountRequest(c *xgb.Conn, Window xproto.Window) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XINERAMA"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 2 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScreenSizeCookie is a cookie used only for GetScreenSize requests.
|
||||||
|
type GetScreenSizeCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScreenSize sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetScreenSizeCookie.Reply()
|
||||||
|
func GetScreenSize(c *xgb.Conn, Window xproto.Window, Screen uint32) GetScreenSizeCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetScreenSize' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getScreenSizeRequest(c, Window, Screen), cookie)
|
||||||
|
return GetScreenSizeCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScreenSizeUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetScreenSizeUnchecked(c *xgb.Conn, Window xproto.Window, Screen uint32) GetScreenSizeCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetScreenSize' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getScreenSizeRequest(c, Window, Screen), cookie)
|
||||||
|
return GetScreenSizeCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScreenSizeReply represents the data returned from a GetScreenSize request.
|
||||||
|
type GetScreenSizeReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
Width uint32
|
||||||
|
Height uint32
|
||||||
|
Window xproto.Window
|
||||||
|
Screen uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetScreenSize request.
|
||||||
|
func (cook GetScreenSizeCookie) Reply() (*GetScreenSizeReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getScreenSizeReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getScreenSizeReply reads a byte slice into a GetScreenSizeReply value.
|
||||||
|
func getScreenSizeReply(buf []byte) *GetScreenSizeReply {
|
||||||
|
v := new(GetScreenSizeReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Width = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Height = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Screen = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetScreenSize
|
||||||
|
// getScreenSizeRequest writes a GetScreenSize request to a byte slice.
|
||||||
|
func getScreenSizeRequest(c *xgb.Conn, Window xproto.Window, Screen uint32) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XINERAMA"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 3 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Screen)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStateCookie is a cookie used only for GetState requests.
|
||||||
|
type GetStateCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetStateCookie.Reply()
|
||||||
|
func GetState(c *xgb.Conn, Window xproto.Window) GetStateCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetState' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getStateRequest(c, Window), cookie)
|
||||||
|
return GetStateCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStateUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetStateUnchecked(c *xgb.Conn, Window xproto.Window) GetStateCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetState' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getStateRequest(c, Window), cookie)
|
||||||
|
return GetStateCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStateReply represents the data returned from a GetState request.
|
||||||
|
type GetStateReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
State byte
|
||||||
|
Window xproto.Window
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetState request.
|
||||||
|
func (cook GetStateCookie) Reply() (*GetStateReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getStateReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStateReply reads a byte slice into a GetStateReply value.
|
||||||
|
func getStateReply(buf []byte) *GetStateReply {
|
||||||
|
v := new(GetStateReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
v.State = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetState
|
||||||
|
// getStateRequest writes a GetState request to a byte slice.
|
||||||
|
func getStateRequest(c *xgb.Conn, Window xproto.Window) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XINERAMA"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 1 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsActiveCookie is a cookie used only for IsActive requests.
|
||||||
|
type IsActiveCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsActive sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling IsActiveCookie.Reply()
|
||||||
|
func IsActive(c *xgb.Conn) IsActiveCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'IsActive' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(isActiveRequest(c), cookie)
|
||||||
|
return IsActiveCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsActiveUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func IsActiveUnchecked(c *xgb.Conn) IsActiveCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'IsActive' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(isActiveRequest(c), cookie)
|
||||||
|
return IsActiveCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsActiveReply represents the data returned from a IsActive request.
|
||||||
|
type IsActiveReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
State uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a IsActive request.
|
||||||
|
func (cook IsActiveCookie) Reply() (*IsActiveReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return isActiveReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isActiveReply reads a byte slice into a IsActiveReply value.
|
||||||
|
func isActiveReply(buf []byte) *IsActiveReply {
|
||||||
|
v := new(IsActiveReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.State = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for IsActive
|
||||||
|
// isActiveRequest writes a IsActive request to a byte slice.
|
||||||
|
func isActiveRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XINERAMA"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 4 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryScreensCookie is a cookie used only for QueryScreens requests.
|
||||||
|
type QueryScreensCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryScreens sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling QueryScreensCookie.Reply()
|
||||||
|
func QueryScreens(c *xgb.Conn) QueryScreensCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryScreens' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(queryScreensRequest(c), cookie)
|
||||||
|
return QueryScreensCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryScreensUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func QueryScreensUnchecked(c *xgb.Conn) QueryScreensCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryScreens' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(queryScreensRequest(c), cookie)
|
||||||
|
return QueryScreensCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryScreensReply represents the data returned from a QueryScreens request.
|
||||||
|
type QueryScreensReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
Number uint32
|
||||||
|
// padding: 20 bytes
|
||||||
|
ScreenInfo []ScreenInfo // size: xgb.Pad((int(Number) * 8))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a QueryScreens request.
|
||||||
|
func (cook QueryScreensCookie) Reply() (*QueryScreensReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return queryScreensReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryScreensReply reads a byte slice into a QueryScreensReply value.
|
||||||
|
func queryScreensReply(buf []byte) *QueryScreensReply {
|
||||||
|
v := new(QueryScreensReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Number = xgb.Get32(buf[b:])
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 20 // padding
|
||||||
|
|
||||||
|
v.ScreenInfo = make([]ScreenInfo, v.Number)
|
||||||
|
b += ScreenInfoReadList(buf[b:], v.ScreenInfo)
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for QueryScreens
|
||||||
|
// queryScreensRequest writes a QueryScreens request to a byte slice.
|
||||||
|
func queryScreensRequest(c *xgb.Conn) []byte {
|
||||||
|
size := 4
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XINERAMA"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 5 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionCookie is a cookie used only for QueryVersion requests.
|
||||||
|
type QueryVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
|
||||||
|
func QueryVersion(c *xgb.Conn, Major byte, Minor byte) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, Major, Minor), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func QueryVersionUnchecked(c *xgb.Conn, Major byte, Minor byte) QueryVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||||
|
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(queryVersionRequest(c, Major, Minor), cookie)
|
||||||
|
return QueryVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryVersionReply represents the data returned from a QueryVersion request.
|
||||||
|
type QueryVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
// padding: 1 bytes
|
||||||
|
Major uint16
|
||||||
|
Minor uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a QueryVersion request.
|
||||||
|
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return queryVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionReply reads a byte slice into a QueryVersionReply value.
|
||||||
|
func queryVersionReply(buf []byte) *QueryVersionReply {
|
||||||
|
v := new(QueryVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.Major = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Minor = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for QueryVersion
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, Major byte, Minor byte) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XINERAMA"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
buf[b] = Major
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = Minor
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,384 @@
|
||||||
|
package xproto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tests for XGB.
|
||||||
|
|
||||||
|
These tests only test the core X protocol at the moment. It isn't even
|
||||||
|
close to complete coverage (and probably never will be), but it does test
|
||||||
|
a number of different corners: requests with no replies, requests without
|
||||||
|
replies, checked (i.e., synchronous) errors, unchecked (i.e., asynchronous)
|
||||||
|
errors, and sequence number wrapping.
|
||||||
|
|
||||||
|
There are also a couple of benchmarks that show the difference between
|
||||||
|
correctly issuing lots of requests and gathering replies and
|
||||||
|
incorrectly doing the same. (This particular difference is one of the
|
||||||
|
claimed advantages of the XCB, and therefore XGB, family.)
|
||||||
|
|
||||||
|
In sum, these tests are more focused on testing the core xgb package itself,
|
||||||
|
rather than whether xproto has properly implemented the core X client
|
||||||
|
protocol.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The X connection used throughout testing.
|
||||||
|
var X *xgb.Conn
|
||||||
|
|
||||||
|
// init initializes the X connection, seeds the RNG and starts waiting
|
||||||
|
// for events.
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
X, err = xgb.NewConn()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
go grabEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
// Tests
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// TestSynchronousError purposefully causes a BadWindow error in a
|
||||||
|
// MapWindow request, and checks it synchronously.
|
||||||
|
func TestSynchronousError(t *testing.T) {
|
||||||
|
err := MapWindowChecked(X, 0).Check() // resource 0 is always invalid
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("MapWindow: A MapWindow request that should return an " +
|
||||||
|
"error has returned a nil error.")
|
||||||
|
}
|
||||||
|
verifyMapWindowError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAsynchronousError does the same thing as TestSynchronousError, but
|
||||||
|
// grabs the error asynchronously instead.
|
||||||
|
func TestAsynchronousError(t *testing.T) {
|
||||||
|
MapWindow(X, 0) // resource id 0 is always invalid
|
||||||
|
|
||||||
|
evOrErr := waitForEvent(t, 5)
|
||||||
|
if evOrErr.ev != nil {
|
||||||
|
t.Fatalf("After issuing an erroneous MapWindow request, we have "+
|
||||||
|
"received an event rather than an error: %s", evOrErr.ev)
|
||||||
|
}
|
||||||
|
verifyMapWindowError(t, evOrErr.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCookieBuffer issues (2^16) + n requets *without* replies to guarantee
|
||||||
|
// that the sequence number wraps and that the cookie buffer will have to
|
||||||
|
// flush itself (since there are no replies coming in to flush it).
|
||||||
|
// And just like TestSequenceWrap, we issue another request with a reply
|
||||||
|
// at the end to make sure XGB is still working properly.
|
||||||
|
func TestCookieBuffer(t *testing.T) {
|
||||||
|
n := (1 << 16) + 10
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
NoOperation(X)
|
||||||
|
}
|
||||||
|
TestProperty(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSequenceWrap issues (2^16) + n requests w/ replies to guarantee that the
|
||||||
|
// sequence number (which is a 16 bit integer) will wrap. It then issues one
|
||||||
|
// final request to ensure things still work properly.
|
||||||
|
func TestSequenceWrap(t *testing.T) {
|
||||||
|
n := (1 << 16) + 10
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
_, err := InternAtom(X, false, 5, "RANDO").Reply()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("InternAtom: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TestProperty(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProperty tests whether a random value can be set and read.
|
||||||
|
func TestProperty(t *testing.T) {
|
||||||
|
propName := randString(20) // whatevs
|
||||||
|
writeVal := randString(20)
|
||||||
|
readVal, err := changeAndGetProp(propName, writeVal)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if readVal != writeVal {
|
||||||
|
t.Errorf("The value written, '%s', is not the same as the "+
|
||||||
|
"value read '%s'.", writeVal, readVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestWindowEvents creates a window, maps it, listens for configure notify
|
||||||
|
// events, issues a configure request, and checks for the appropriate
|
||||||
|
// configure notify event.
|
||||||
|
// This probably violates the notion of "test one thing and test it well,"
|
||||||
|
// but testing X stuff is unique since it involves so much state.
|
||||||
|
// Each request is checked to make sure there are no errors returned. If there
|
||||||
|
// is an error, the test is failed.
|
||||||
|
// You may see a window appear quickly and then disappear. Do not be alarmed :P
|
||||||
|
// It's possible that this test will yield a false negative because we cannot
|
||||||
|
// control our environment. That is, the window manager could override the
|
||||||
|
// placement set. However, we set override redirect on the window, so the
|
||||||
|
// window manager *shouldn't* touch our window if it is well-behaved.
|
||||||
|
func TestWindowEvents(t *testing.T) {
|
||||||
|
// The geometry to set the window.
|
||||||
|
gx, gy, gw, gh := 200, 400, 1000, 300
|
||||||
|
|
||||||
|
wid, err := NewWindowId(X)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewId: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
screen := Setup(X).DefaultScreen(X) // alias
|
||||||
|
err = CreateWindowChecked(X, screen.RootDepth, wid, screen.Root,
|
||||||
|
0, 0, 500, 500, 0,
|
||||||
|
WindowClassInputOutput, screen.RootVisual,
|
||||||
|
CwBackPixel|CwOverrideRedirect, []uint32{0xffffffff, 1}).Check()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateWindow: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = MapWindowChecked(X, wid).Check()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("MapWindow: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't listen in the CreateWindow request so that we don't get
|
||||||
|
// a MapNotify event.
|
||||||
|
err = ChangeWindowAttributesChecked(X, wid,
|
||||||
|
CwEventMask, []uint32{EventMaskStructureNotify}).Check()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ChangeWindowAttributes: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ConfigureWindowChecked(X, wid,
|
||||||
|
ConfigWindowX|ConfigWindowY|
|
||||||
|
ConfigWindowWidth|ConfigWindowHeight,
|
||||||
|
[]uint32{uint32(gx), uint32(gy), uint32(gw), uint32(gh)}).Check()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ConfigureWindow: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
evOrErr := waitForEvent(t, 5)
|
||||||
|
switch event := evOrErr.ev.(type) {
|
||||||
|
case ConfigureNotifyEvent:
|
||||||
|
if event.X != int16(gx) {
|
||||||
|
t.Fatalf("x was set to %d but ConfigureNotify reports %d",
|
||||||
|
gx, event.X)
|
||||||
|
}
|
||||||
|
if event.Y != int16(gy) {
|
||||||
|
t.Fatalf("y was set to %d but ConfigureNotify reports %d",
|
||||||
|
gy, event.Y)
|
||||||
|
}
|
||||||
|
if event.Width != uint16(gw) {
|
||||||
|
t.Fatalf("width was set to %d but ConfigureNotify reports %d",
|
||||||
|
gw, event.Width)
|
||||||
|
}
|
||||||
|
if event.Height != uint16(gh) {
|
||||||
|
t.Fatalf("height was set to %d but ConfigureNotify reports %d",
|
||||||
|
gh, event.Height)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Fatalf("Expected a ConfigureNotifyEvent but got %T instead.", event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okay, clean up!
|
||||||
|
err = ChangeWindowAttributesChecked(X, wid,
|
||||||
|
CwEventMask, []uint32{0}).Check()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ChangeWindowAttributes: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = DestroyWindowChecked(X, wid).Check()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("DestroyWindow: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calls GetFontPath function, Issue #12
|
||||||
|
func TestGetFontPath(t *testing.T) {
|
||||||
|
fontPathReply, err := GetFontPath(X).Reply()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetFontPath: %v", err)
|
||||||
|
}
|
||||||
|
_ = fontPathReply
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListFonts(t *testing.T) {
|
||||||
|
listFontsReply, err := ListFonts(X, 10, 1, "*").Reply()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListFonts: %v", err)
|
||||||
|
}
|
||||||
|
_ = listFontsReply
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
// Benchmarks
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// BenchmarkInternAtomsGood shows how many requests with replies
|
||||||
|
// *should* be sent and gathered from the server. Namely, send as many
|
||||||
|
// requests as you can at once, then go back and gather up all the replies.
|
||||||
|
// More importantly, this approach can exploit parallelism when
|
||||||
|
// GOMAXPROCS > 1.
|
||||||
|
// Run with `go test -run 'nomatch' -bench '.*' -cpu 1,2,6` if you have
|
||||||
|
// multiple cores to see the improvement that parallelism brings.
|
||||||
|
func BenchmarkInternAtomsGood(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
names := seqNames(b.N)
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
cookies := make([]InternAtomCookie, b.N)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
cookies[i] = InternAtom(X, false, uint16(len(names[i])), names[i])
|
||||||
|
}
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
cookie.Reply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkInternAtomsBad shows how *not* to issue a lot of requests with
|
||||||
|
// replies. Namely, each subsequent request isn't issued *until* the last
|
||||||
|
// reply is made. This implies a round trip to the X server for every
|
||||||
|
// iteration.
|
||||||
|
func BenchmarkInternAtomsPoor(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
names := seqNames(b.N)
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
InternAtom(X, false, uint16(len(names[i])), names[i]).Reply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
// Helper functions
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// changeAndGetProp sets property 'prop' with value 'val'.
|
||||||
|
// It then gets the value of that property and returns it.
|
||||||
|
// (It's used to check that the 'val' going in is the same 'val' going out.)
|
||||||
|
// It tests both requests with and without replies (GetProperty and
|
||||||
|
// ChangeProperty respectively.)
|
||||||
|
func changeAndGetProp(prop, val string) (string, error) {
|
||||||
|
setup := Setup(X)
|
||||||
|
root := setup.DefaultScreen(X).Root
|
||||||
|
|
||||||
|
propAtom, err := InternAtom(X, false, uint16(len(prop)), prop).Reply()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("InternAtom: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
typName := "UTF8_STRING"
|
||||||
|
typAtom, err := InternAtom(X, false, uint16(len(typName)), typName).Reply()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("InternAtom: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ChangePropertyChecked(X, PropModeReplace, root, propAtom.Atom,
|
||||||
|
typAtom.Atom, 8, uint32(len(val)), []byte(val)).Check()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("ChangeProperty: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reply, err := GetProperty(X, false, root, propAtom.Atom,
|
||||||
|
GetPropertyTypeAny, 0, (1<<32)-1).Reply()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("GetProperty: %s", err)
|
||||||
|
}
|
||||||
|
if reply.Format != 8 {
|
||||||
|
return "", fmt.Errorf("Property reply format is %d but it should be 8.",
|
||||||
|
reply.Format)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(reply.Value), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyMapWindowError takes an error that is returned with an invalid
|
||||||
|
// MapWindow request with a window Id of 0 and makes sure the error is the
|
||||||
|
// right type and contains the correct values.
|
||||||
|
func verifyMapWindowError(t *testing.T, err error) {
|
||||||
|
switch e := err.(type) {
|
||||||
|
case WindowError:
|
||||||
|
if e.BadValue != 0 {
|
||||||
|
t.Fatalf("WindowError should report a bad value of 0 but "+
|
||||||
|
"it reports %d instead.", e.BadValue)
|
||||||
|
}
|
||||||
|
if e.MajorOpcode != 8 {
|
||||||
|
t.Fatalf("WindowError should report a major opcode of 8 "+
|
||||||
|
"(which is a MapWindow request), but it reports %d instead.",
|
||||||
|
e.MajorOpcode)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Fatalf("Expected a WindowError but got %T instead.", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// randString generates a random string of length n.
|
||||||
|
func randString(n int) string {
|
||||||
|
byts := make([]byte, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
rando := rand.Intn(53)
|
||||||
|
switch {
|
||||||
|
case rando <= 25:
|
||||||
|
byts[i] = byte(65 + rando)
|
||||||
|
case rando <= 51:
|
||||||
|
byts[i] = byte(97 + rando - 26)
|
||||||
|
default:
|
||||||
|
byts[i] = ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(byts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// seqNames creates a slice of NAME0, NAME1, ..., NAMEN.
|
||||||
|
func seqNames(n int) []string {
|
||||||
|
names := make([]string, n)
|
||||||
|
for i := range names {
|
||||||
|
names[i] = fmt.Sprintf("NAME%d", i)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// evErr represents a value that is either an event or an error.
|
||||||
|
type evErr struct {
|
||||||
|
ev xgb.Event
|
||||||
|
err xgb.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// channel used to pass evErrs.
|
||||||
|
var evOrErrChan = make(chan evErr, 0)
|
||||||
|
|
||||||
|
// grabEvents is a goroutine that reads events off the wire.
|
||||||
|
// We used this instead of WaitForEvent directly in our tests so that
|
||||||
|
// we can timeout and fail a test.
|
||||||
|
func grabEvents() {
|
||||||
|
for {
|
||||||
|
ev, err := X.WaitForEvent()
|
||||||
|
evOrErrChan <- evErr{ev, err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForEvent asks the evOrErrChan channel for an event.
|
||||||
|
// If it doesn't get an event in 'n' seconds, the current test is failed.
|
||||||
|
func waitForEvent(t *testing.T, n int) evErr {
|
||||||
|
var evOrErr evErr
|
||||||
|
|
||||||
|
select {
|
||||||
|
case evOrErr = <-evOrErrChan:
|
||||||
|
case <-time.After(time.Second * 5):
|
||||||
|
t.Fatalf("After waiting 5 seconds for an event or an error, " +
|
||||||
|
"we have timed out.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return evOrErr
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,416 @@
|
||||||
|
// Package xtest is the X client API for the XTEST extension.
|
||||||
|
package xtest
|
||||||
|
|
||||||
|
// This file is automatically generated from xtest.xml. Edit at your peril!
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/BurntSushi/xgb"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/xgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init must be called before using the XTEST extension.
|
||||||
|
func Init(c *xgb.Conn) error {
|
||||||
|
reply, err := xproto.QueryExtension(c, 5, "XTEST").Reply()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case !reply.Present:
|
||||||
|
return xgb.Errorf("No extension named XTEST could be found on on the server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtLock.Lock()
|
||||||
|
c.Extensions["XTEST"] = reply.MajorOpcode
|
||||||
|
c.ExtLock.Unlock()
|
||||||
|
for evNum, fun := range xgb.NewExtEventFuncs["XTEST"] {
|
||||||
|
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||||
|
}
|
||||||
|
for errNum, fun := range xgb.NewExtErrorFuncs["XTEST"] {
|
||||||
|
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xgb.NewExtEventFuncs["XTEST"] = make(map[int]xgb.NewEventFun)
|
||||||
|
xgb.NewExtErrorFuncs["XTEST"] = make(map[int]xgb.NewErrorFun)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
CursorNone = 0
|
||||||
|
CursorCurrent = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Bool'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Byte'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Char'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Void'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Double'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Float'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int32'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Int8'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card16'
|
||||||
|
|
||||||
|
// Skipping definition for base type 'Card32'
|
||||||
|
|
||||||
|
// CompareCursorCookie is a cookie used only for CompareCursor requests.
|
||||||
|
type CompareCursorCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareCursor sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling CompareCursorCookie.Reply()
|
||||||
|
func CompareCursor(c *xgb.Conn, Window xproto.Window, Cursor xproto.Cursor) CompareCursorCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XTEST"]; !ok {
|
||||||
|
panic("Cannot issue request 'CompareCursor' using the uninitialized extension 'XTEST'. xtest.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(compareCursorRequest(c, Window, Cursor), cookie)
|
||||||
|
return CompareCursorCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareCursorUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func CompareCursorUnchecked(c *xgb.Conn, Window xproto.Window, Cursor xproto.Cursor) CompareCursorCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XTEST"]; !ok {
|
||||||
|
panic("Cannot issue request 'CompareCursor' using the uninitialized extension 'XTEST'. xtest.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(compareCursorRequest(c, Window, Cursor), cookie)
|
||||||
|
return CompareCursorCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareCursorReply represents the data returned from a CompareCursor request.
|
||||||
|
type CompareCursorReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
Same bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a CompareCursor request.
|
||||||
|
func (cook CompareCursorCookie) Reply() (*CompareCursorReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return compareCursorReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compareCursorReply reads a byte slice into a CompareCursorReply value.
|
||||||
|
func compareCursorReply(buf []byte) *CompareCursorReply {
|
||||||
|
v := new(CompareCursorReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
if buf[b] == 1 {
|
||||||
|
v.Same = true
|
||||||
|
} else {
|
||||||
|
v.Same = false
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for CompareCursor
|
||||||
|
// compareCursorRequest writes a CompareCursor request to a byte slice.
|
||||||
|
func compareCursorRequest(c *xgb.Conn, Window xproto.Window, Cursor xproto.Cursor) []byte {
|
||||||
|
size := 12
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XTEST"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 1 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Window))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Cursor))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// FakeInputCookie is a cookie used only for FakeInput requests.
|
||||||
|
type FakeInputCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// FakeInput sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func FakeInput(c *xgb.Conn, Type byte, Detail byte, Time uint32, Root xproto.Window, RootX int16, RootY int16, Deviceid byte) FakeInputCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XTEST"]; !ok {
|
||||||
|
panic("Cannot issue request 'FakeInput' using the uninitialized extension 'XTEST'. xtest.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(fakeInputRequest(c, Type, Detail, Time, Root, RootX, RootY, Deviceid), cookie)
|
||||||
|
return FakeInputCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FakeInputChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using FakeInputCookie.Check()
|
||||||
|
func FakeInputChecked(c *xgb.Conn, Type byte, Detail byte, Time uint32, Root xproto.Window, RootX int16, RootY int16, Deviceid byte) FakeInputCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XTEST"]; !ok {
|
||||||
|
panic("Cannot issue request 'FakeInput' using the uninitialized extension 'XTEST'. xtest.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(fakeInputRequest(c, Type, Detail, Time, Root, RootX, RootY, Deviceid), cookie)
|
||||||
|
return FakeInputCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook FakeInputCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for FakeInput
|
||||||
|
// fakeInputRequest writes a FakeInput request to a byte slice.
|
||||||
|
func fakeInputRequest(c *xgb.Conn, Type byte, Detail byte, Time uint32, Root xproto.Window, RootX int16, RootY int16, Deviceid byte) []byte {
|
||||||
|
size := 36
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XTEST"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 2 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
buf[b] = Type
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = Detail
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 2 // padding
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], Time)
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
xgb.Put32(buf[b:], uint32(Root))
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
b += 8 // padding
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(RootX))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(RootY))
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
b += 7 // padding
|
||||||
|
|
||||||
|
buf[b] = Deviceid
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionCookie is a cookie used only for GetVersion requests.
|
||||||
|
type GetVersionCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion sends a checked request.
|
||||||
|
// If an error occurs, it will be returned with the reply by calling GetVersionCookie.Reply()
|
||||||
|
func GetVersion(c *xgb.Conn, MajorVersion byte, MinorVersion uint16) GetVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XTEST"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetVersion' using the uninitialized extension 'XTEST'. xtest.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, true)
|
||||||
|
c.NewRequest(getVersionRequest(c, MajorVersion, MinorVersion), cookie)
|
||||||
|
return GetVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionUnchecked sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GetVersionUnchecked(c *xgb.Conn, MajorVersion byte, MinorVersion uint16) GetVersionCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XTEST"]; !ok {
|
||||||
|
panic("Cannot issue request 'GetVersion' using the uninitialized extension 'XTEST'. xtest.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, true)
|
||||||
|
c.NewRequest(getVersionRequest(c, MajorVersion, MinorVersion), cookie)
|
||||||
|
return GetVersionCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionReply represents the data returned from a GetVersion request.
|
||||||
|
type GetVersionReply struct {
|
||||||
|
Sequence uint16 // sequence number of the request for this reply
|
||||||
|
Length uint32 // number of bytes in this reply
|
||||||
|
MajorVersion byte
|
||||||
|
MinorVersion uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply blocks and returns the reply data for a GetVersion request.
|
||||||
|
func (cook GetVersionCookie) Reply() (*GetVersionReply, error) {
|
||||||
|
buf, err := cook.Cookie.Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return getVersionReply(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVersionReply reads a byte slice into a GetVersionReply value.
|
||||||
|
func getVersionReply(buf []byte) *GetVersionReply {
|
||||||
|
v := new(GetVersionReply)
|
||||||
|
b := 1 // skip reply determinant
|
||||||
|
|
||||||
|
v.MajorVersion = buf[b]
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
v.Sequence = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||||
|
b += 4
|
||||||
|
|
||||||
|
v.MinorVersion = xgb.Get16(buf[b:])
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GetVersion
|
||||||
|
// getVersionRequest writes a GetVersion request to a byte slice.
|
||||||
|
func getVersionRequest(c *xgb.Conn, MajorVersion byte, MinorVersion uint16) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XTEST"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 0 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
buf[b] = MajorVersion
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 1 // padding
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], MinorVersion)
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrabControlCookie is a cookie used only for GrabControl requests.
|
||||||
|
type GrabControlCookie struct {
|
||||||
|
*xgb.Cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrabControl sends an unchecked request.
|
||||||
|
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||||
|
func GrabControl(c *xgb.Conn, Impervious bool) GrabControlCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XTEST"]; !ok {
|
||||||
|
panic("Cannot issue request 'GrabControl' using the uninitialized extension 'XTEST'. xtest.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(false, false)
|
||||||
|
c.NewRequest(grabControlRequest(c, Impervious), cookie)
|
||||||
|
return GrabControlCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrabControlChecked sends a checked request.
|
||||||
|
// If an error occurs, it can be retrieved using GrabControlCookie.Check()
|
||||||
|
func GrabControlChecked(c *xgb.Conn, Impervious bool) GrabControlCookie {
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
defer c.ExtLock.RUnlock()
|
||||||
|
if _, ok := c.Extensions["XTEST"]; !ok {
|
||||||
|
panic("Cannot issue request 'GrabControl' using the uninitialized extension 'XTEST'. xtest.Init(connObj) must be called first.")
|
||||||
|
}
|
||||||
|
cookie := c.NewCookie(true, false)
|
||||||
|
c.NewRequest(grabControlRequest(c, Impervious), cookie)
|
||||||
|
return GrabControlCookie{cookie}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns an error if one occurred for checked requests that are not expecting a reply.
|
||||||
|
// This cannot be called for requests expecting a reply, nor for unchecked requests.
|
||||||
|
func (cook GrabControlCookie) Check() error {
|
||||||
|
return cook.Cookie.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write request to wire for GrabControl
|
||||||
|
// grabControlRequest writes a GrabControl request to a byte slice.
|
||||||
|
func grabControlRequest(c *xgb.Conn, Impervious bool) []byte {
|
||||||
|
size := 8
|
||||||
|
b := 0
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
c.ExtLock.RLock()
|
||||||
|
buf[b] = c.Extensions["XTEST"]
|
||||||
|
c.ExtLock.RUnlock()
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
buf[b] = 3 // request opcode
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||||
|
b += 2
|
||||||
|
|
||||||
|
if Impervious {
|
||||||
|
buf[b] = 1
|
||||||
|
} else {
|
||||||
|
buf[b] = 0
|
||||||
|
}
|
||||||
|
b += 1
|
||||||
|
|
||||||
|
b += 3 // padding
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue