Compare commits
104 Commits
632b3ae494
...
7d51aaa9a4
Author | SHA1 | Date | |
---|---|---|---|
7d51aaa9a4 | |||
f198f9f6ac | |||
106e9b82b8 | |||
139c50b748 | |||
e6e4e94436 | |||
7051829581 | |||
3e9ed4eac6 | |||
0056720d05 | |||
28a5112532 | |||
1a53c005e9 | |||
07bf9881cc | |||
7aa2601e66 | |||
48fb710f35 | |||
3173202cc1 | |||
|
3906399e7c | ||
|
1c01d79ba1 | ||
|
a102c4056f | ||
|
1f83ea75a2 | ||
|
1614b58c42 | ||
|
8d343cfd3a | ||
|
becaf43dcb | ||
|
baff8c1906 | ||
|
5451e59f88 | ||
|
dd00568d44 | ||
|
a1d1151017 | ||
|
ef8155bf17 | ||
|
a548d9d0f7 | ||
|
1f8bd79abe | ||
|
64c6e6170d | ||
|
76f9adb599 | ||
|
33509dbeb0 | ||
|
ad9c35a02f | ||
|
2dc9914b5e | ||
|
f0385db3a7 | ||
|
5a07ac7108 | ||
|
efe87cb908 | ||
|
3b4adabee1 | ||
|
2104b8fcdf | ||
|
38b293e74d | ||
|
b06a8ca976 | ||
|
0685fb57e1 | ||
|
4b20ffaf4f | ||
|
5d96993ee1 | ||
|
3658686aee | ||
|
e635de5e1d | ||
|
269a7b9cc6 | ||
|
13eff4bec3 | ||
|
08275ebda8 | ||
|
a9eae45cb3 | ||
|
22ceab8074 | ||
|
e9dc18b4f9 | ||
|
744c9688cc | ||
|
8ee0ea9899 | ||
|
e960f4d34e | ||
|
cd22f99b20 | ||
|
4ea94ca0fe | ||
|
58bb2572c5 | ||
|
acb84171e5 | ||
|
424f293671 | ||
|
45a4ee92eb | ||
|
6bdfd1d1b1 | ||
|
7abc9c6455 | ||
|
24fef4062a | ||
|
f77feff864 | ||
|
aa95801b2d | ||
|
29942bf078 | ||
|
fb3128ed2a | ||
|
3e6b354493 | ||
|
c00652934e | ||
|
a3363755cd | ||
|
0c50dc6241 | ||
|
e239bb3c68 | ||
|
00c6217ca9 | ||
|
5d64f69030 | ||
|
62b293c937 | ||
|
e256da00b1 | ||
|
13d598e5e7 | ||
|
daad54a5e1 | ||
|
eed777ebfd | ||
|
dc48249e1a | ||
|
3bf376bd66 | ||
|
fd30f1512a | ||
|
6d545e723a | ||
|
135cee5761 | ||
|
70ebcf5178 | ||
|
ea30f1a0a7 | ||
|
014a0598bf | ||
|
18b2d420b0 | ||
|
99bc76de54 | ||
|
369ad0d33e | ||
|
b6715f376f | ||
|
4a7b05be36 | ||
|
c222d406b0 | ||
|
a5d4ad6c9d | ||
|
5cdae5950c | ||
|
39507f86ab | ||
|
f48b6fafc6 | ||
|
83a71d4648 | ||
|
73154769b3 | ||
|
2a2d8653b3 | ||
|
05d8ec6a16 | ||
|
3115c13e88 | ||
|
6bf0191fb0 | ||
|
52a21b415a |
21
README
21
README
@ -173,6 +173,22 @@ The result of testing hid with telnet, OpenSSL s_client, OpenBSD nc, GNU nc and
|
|||||||
Ncat is that neither of them can properly shutdown the connection. We need
|
Ncat is that neither of them can properly shutdown the connection. We need
|
||||||
a good implementation with TLS support.
|
a good implementation with TLS support.
|
||||||
|
|
||||||
|
hpcu -- PRIMARY-CLIPBOARD unifier
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
An improved replacement for autocutsel in selection synchronization "mode":
|
||||||
|
|
||||||
|
- using only one OS process;
|
||||||
|
- not polling selections twice a second unnecessarily;
|
||||||
|
- calling SetSelectionOwner on change even when it already owns the selection,
|
||||||
|
so that XFIXES SelectionNotify events are delivered;
|
||||||
|
- not using cut buffers for anything.
|
||||||
|
|
||||||
|
Only UTF8_STRING-convertible selections are synchronized.
|
||||||
|
|
||||||
|
ht -- terminal emulator
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Similar scope to st(1). Clever display of internal padding for better looks.
|
||||||
|
|
||||||
hib and hic -- IRC bouncer and client
|
hib and hic -- IRC bouncer and client
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
An IRC client is a good starting application for building a GUI toolkit, as the
|
An IRC client is a good starting application for building a GUI toolkit, as the
|
||||||
@ -241,6 +257,7 @@ most basic features includes a VFS for archives. The editing widget in read-
|
|||||||
-only mode could be used for F3. The shell is going to work very simply,
|
-only mode could be used for F3. The shell is going to work very simply,
|
||||||
creating a PTY device and running things under TERM=dumb while decoding SGR,
|
creating a PTY device and running things under TERM=dumb while decoding SGR,
|
||||||
or one could decide to run a new terminal emulator with a different shortcut.
|
or one could decide to run a new terminal emulator with a different shortcut.
|
||||||
|
ht could probably also be integrated.
|
||||||
|
|
||||||
Eventually the number of panels should be arbitrary with proper shortcuts for
|
Eventually the number of panels should be arbitrary with proper shortcuts for
|
||||||
working with them. We might also integrate a special view for picture previews,
|
working with them. We might also integrate a special view for picture previews,
|
||||||
@ -255,10 +272,6 @@ Indexing and search may be based on a common database, no need to get all fancy:
|
|||||||
http://rachbelaid.com/postgres-full-text-search-is-good-enough/
|
http://rachbelaid.com/postgres-full-text-search-is-good-enough/
|
||||||
https://www.sqlite.org/fts3.html#full_text_index_queries (FTS4 seems better)
|
https://www.sqlite.org/fts3.html#full_text_index_queries (FTS4 seems better)
|
||||||
|
|
||||||
ht -- terminal emulator
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Similar scope to st(1). Clever display of internal padding for better looks.
|
|
||||||
|
|
||||||
The rest
|
The rest
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
Currently there are no significant, specific plans about the other applications.
|
Currently there are no significant, specific plans about the other applications.
|
||||||
|
351
hpcu/main.go
Normal file
351
hpcu/main.go
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
// hpcu unifies the PRIMARY and CLIPBOARD X11 selections for text contents.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"janouch.name/haven/nexgb"
|
||||||
|
"janouch.name/haven/nexgb/xfixes"
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type selectionState struct {
|
||||||
|
name string // name of the selection
|
||||||
|
inProgress xproto.Timestamp // timestamp of retrieved selection
|
||||||
|
buffer []byte // UTF-8 text buffer
|
||||||
|
incr bool // INCR running
|
||||||
|
incrFailed bool // INCR failure indicator
|
||||||
|
owning xproto.Timestamp // since when we own the selection
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
X *nexgb.Conn
|
||||||
|
setup *xproto.SetupInfo
|
||||||
|
screen *xproto.ScreenInfo
|
||||||
|
|
||||||
|
atomCLIPBOARD xproto.Atom // X11 atom for CLIPBOARD
|
||||||
|
atomUTF8String xproto.Atom // X11 atom for UTF8_STRING
|
||||||
|
atomINCR xproto.Atom // X11 atom for INCR
|
||||||
|
atomTARGETS xproto.Atom // X11 atom for TARGETS
|
||||||
|
atomTIMESTAMP xproto.Atom // X11 atom for TIMESTAMP
|
||||||
|
|
||||||
|
wid xproto.Window // auxiliary window
|
||||||
|
selections map[xproto.Atom]*selectionState
|
||||||
|
contents string // current shared selection contents
|
||||||
|
)
|
||||||
|
|
||||||
|
// resolveAtoms resolves a few required atoms that are not in the core protocol.
|
||||||
|
func resolveAtoms() error {
|
||||||
|
for _, i := range []struct {
|
||||||
|
placement *xproto.Atom
|
||||||
|
name string
|
||||||
|
}{
|
||||||
|
{&atomCLIPBOARD, "CLIPBOARD"},
|
||||||
|
{&atomUTF8String, "UTF8_STRING"},
|
||||||
|
{&atomINCR, "INCR"},
|
||||||
|
{&atomTARGETS, "TARGETS"},
|
||||||
|
{&atomTIMESTAMP, "TIMESTAMP"},
|
||||||
|
} {
|
||||||
|
if reply, err := xproto.InternAtom(X,
|
||||||
|
false, uint16(len(i.name)), i.name).Reply(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
*i.placement = reply.Atom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupAuxiliaryWindow creates a window that receives notifications about
|
||||||
|
// changed selection contents, and serves
|
||||||
|
func setupAuxiliaryWindow() error {
|
||||||
|
var err error
|
||||||
|
if wid, err = xproto.NewWindowId(X); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, 0, 0, 1, 1,
|
||||||
|
0, xproto.WindowClassInputOutput, screen.RootVisual, xproto.CwEventMask,
|
||||||
|
[]uint32{xproto.EventMaskPropertyChange})
|
||||||
|
|
||||||
|
for _, selection := range []xproto.Atom{xproto.AtomPrimary, atomCLIPBOARD} {
|
||||||
|
_ = xfixes.SelectSelectionInput(X, wid, selection,
|
||||||
|
xfixes.SelectionEventMaskSetSelectionOwner|
|
||||||
|
xfixes.SelectionEventMaskSelectionWindowDestroy|
|
||||||
|
xfixes.SelectionEventMaskSelectionClientClose)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getProperty reads a window property in a memory-efficient manner.
|
||||||
|
func getProperty(window xproto.Window, property xproto.Atom) (
|
||||||
|
*xproto.GetPropertyReply, error) {
|
||||||
|
// xorg-xserver doesn't seem to limit the length of replies or even
|
||||||
|
// the length of properties in the first place. It only has a huge
|
||||||
|
// (0xffffffff - sizeof(xChangePropertyReq))/4 limit for ChangeProperty
|
||||||
|
// requests, even though I can't XChangeProperty more than 0xffffe0
|
||||||
|
// bytes at a time.
|
||||||
|
//
|
||||||
|
// Since the XGB API doesn't let us provide our own buffer for
|
||||||
|
// value data, let us avoid multiplying the amount of consumed memory in
|
||||||
|
// pathological cases where properties are several gigabytes in size by
|
||||||
|
// chunking the requests. This has a cost of losing atomicity, although
|
||||||
|
// it shouldn't pose a problem except for timeout-caused INCR races.
|
||||||
|
var result xproto.GetPropertyReply
|
||||||
|
for result.Length == 0 || result.BytesAfter > 0 {
|
||||||
|
reply, err := xproto.GetProperty(X, false, /* delete */
|
||||||
|
window, property, xproto.GetPropertyTypeAny,
|
||||||
|
uint32(len(result.Value))/4,
|
||||||
|
uint32(setup.MaximumRequestLength)).Reply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if result.Length != 0 &&
|
||||||
|
(reply.Format != result.Format || reply.Type != result.Type) {
|
||||||
|
return nil, errors.New("property type changed during read")
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.Value = append(result.Value, reply.Value...)
|
||||||
|
reply.ValueLen += result.ValueLen
|
||||||
|
result = *reply
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendText tries to append UTF-8 text to the selection state buffer.
|
||||||
|
func appendText(state *selectionState, prop *xproto.GetPropertyReply) bool {
|
||||||
|
if prop.Type == atomUTF8String && prop.Format == 8 {
|
||||||
|
state.buffer = append(state.buffer, prop.Value...)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestOwnership(origin *selectionState, time xproto.Timestamp) {
|
||||||
|
contents = string(origin.buffer)
|
||||||
|
for selection, state := range selections {
|
||||||
|
// We might want to replace the originator as well but it might have
|
||||||
|
// undesirable effects, mainly with PRIMARY.
|
||||||
|
if state != origin {
|
||||||
|
// No need to GetSelectionOwner, XFIXES is more reliable.
|
||||||
|
_ = xproto.SetSelectionOwner(X, wid, selection, time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleEvent(ev nexgb.Event) {
|
||||||
|
switch e := ev.(type) {
|
||||||
|
case xfixes.SelectionNotifyEvent:
|
||||||
|
state, ok := selections[e.Selection]
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ownership request has been granted, don't ask ourselves for data.
|
||||||
|
if e.Owner == wid {
|
||||||
|
state.owning = e.SelectionTimestamp
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should always be true.
|
||||||
|
if state.owning < e.SelectionTimestamp {
|
||||||
|
state.owning = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not checking whether we should give up when our current retrieval
|
||||||
|
// attempt is interrupted--the timeout mostly solves this.
|
||||||
|
if e.Owner == xproto.WindowNone {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't try to process two things at once. Each request gets a few
|
||||||
|
// seconds to finish, then we move on, hoping that a property race
|
||||||
|
// doesn't commence. Ideally we'd set up a separate queue for these
|
||||||
|
// skipped requests and process them later.
|
||||||
|
if state.inProgress != 0 && e.Timestamp-state.inProgress < 5000 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// ICCCM says we should ensure the named property doesn't exist.
|
||||||
|
_ = xproto.DeleteProperty(X, e.Window, e.Selection)
|
||||||
|
|
||||||
|
_ = xproto.ConvertSelection(X, e.Window, e.Selection,
|
||||||
|
atomUTF8String, e.Selection, e.Timestamp)
|
||||||
|
|
||||||
|
state.inProgress = e.Timestamp
|
||||||
|
state.incr = false
|
||||||
|
|
||||||
|
case xproto.SelectionNotifyEvent:
|
||||||
|
state, ok := selections[e.Selection]
|
||||||
|
if e.Requestor != wid || !ok || e.Time != state.inProgress {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
state.inProgress = 0
|
||||||
|
if e.Property == xproto.AtomNone {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
state.buffer = nil
|
||||||
|
reply, err := getProperty(e.Requestor, e.Property)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// When you select a lot of text in VIM, it starts the ICCCM
|
||||||
|
// INCR mechanism, from which there is no opt-out.
|
||||||
|
if reply.Type == atomINCR {
|
||||||
|
state.inProgress = e.Time
|
||||||
|
state.incr = true
|
||||||
|
state.incrFailed = false
|
||||||
|
} else if appendText(state, reply) {
|
||||||
|
requestOwnership(state, e.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = xproto.DeleteProperty(X, e.Requestor, e.Property)
|
||||||
|
|
||||||
|
case xproto.PropertyNotifyEvent:
|
||||||
|
state, ok := selections[e.Atom]
|
||||||
|
if e.Window != wid || e.State != xproto.PropertyNewValue ||
|
||||||
|
!ok || !state.incr {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
reply, err := getProperty(e.Window, e.Atom)
|
||||||
|
if err != nil {
|
||||||
|
state.incrFailed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !appendText(state, reply) {
|
||||||
|
// We need to keep deleting the property.
|
||||||
|
state.incrFailed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if reply.ValueLen == 0 {
|
||||||
|
if !state.incrFailed {
|
||||||
|
requestOwnership(state, e.Time)
|
||||||
|
}
|
||||||
|
state.inProgress = 0
|
||||||
|
state.incr = false
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = xproto.DeleteProperty(X, e.Window, e.Atom)
|
||||||
|
|
||||||
|
case xproto.SelectionRequestEvent:
|
||||||
|
property := e.Property
|
||||||
|
if property == xproto.AtomNone {
|
||||||
|
property = e.Target
|
||||||
|
}
|
||||||
|
|
||||||
|
state, ok := selections[e.Selection]
|
||||||
|
if e.Owner != wid || !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
typ xproto.Atom
|
||||||
|
format byte
|
||||||
|
data []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
// XXX: We should also support the MULTIPLE target but it seems to be
|
||||||
|
// unimportant and largely abandoned today.
|
||||||
|
targets := []xproto.Atom{atomTARGETS, atomTIMESTAMP, atomUTF8String}
|
||||||
|
|
||||||
|
switch e.Target {
|
||||||
|
case atomTARGETS:
|
||||||
|
typ = xproto.AtomAtom
|
||||||
|
format = 32
|
||||||
|
|
||||||
|
data = make([]byte, len(targets)*4)
|
||||||
|
for i, atom := range targets {
|
||||||
|
nexgb.Put32(data[i*4:], uint32(atom))
|
||||||
|
}
|
||||||
|
|
||||||
|
case atomTIMESTAMP:
|
||||||
|
typ = xproto.AtomInteger
|
||||||
|
format = 32
|
||||||
|
|
||||||
|
data = make([]byte, 4)
|
||||||
|
nexgb.Put32(data, uint32(state.owning))
|
||||||
|
|
||||||
|
case atomUTF8String:
|
||||||
|
typ = atomUTF8String
|
||||||
|
format = 8
|
||||||
|
|
||||||
|
data = []byte(contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := xproto.SelectionNotifyEvent{
|
||||||
|
Time: e.Time,
|
||||||
|
Requestor: e.Requestor,
|
||||||
|
Selection: e.Selection,
|
||||||
|
Target: e.Target,
|
||||||
|
Property: xproto.AtomNone,
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ == 0 || len(data) > int(setup.MaximumRequestLength)*4-64 ||
|
||||||
|
state.owning == 0 || e.Time < state.owning {
|
||||||
|
// TODO: Use the INCR mechanism for large data transfers instead
|
||||||
|
// of refusing the request, or at least use PropModeAppend.
|
||||||
|
//
|
||||||
|
// According to the ICCCM we need to set up a queue for concurrent
|
||||||
|
// (requestor, selection, target, timestamp) requests that differ
|
||||||
|
// only in the target property, and process them in order. The ICCCM
|
||||||
|
// has a nice rationale. It seems to only concern INCR. The queue
|
||||||
|
// might be a map[(who, what, how, when)][](where, data, offset).
|
||||||
|
//
|
||||||
|
// NOTE: Even with BigRequests support, it may technically be
|
||||||
|
// missing on the particular X server, and XGB copies buffers to yet
|
||||||
|
// another buffer, making very large transfers a very bad idea.
|
||||||
|
} else if xproto.ChangePropertyChecked(X, xproto.PropModeReplace,
|
||||||
|
e.Requestor, property, typ, format,
|
||||||
|
uint32(len(data)/int(format/8)), data).Check() == nil {
|
||||||
|
response.Property = property
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = xproto.SendEvent(X, false /* propagate */, e.Requestor,
|
||||||
|
0 /* event mask */, string(response.Bytes()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
if X, err = nexgb.NewConn(); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
if err = xfixes.Init(X); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the extension.
|
||||||
|
_ = xfixes.QueryVersion(X, xfixes.MajorVersion, xfixes.MinorVersion)
|
||||||
|
|
||||||
|
setup = xproto.Setup(X)
|
||||||
|
screen = setup.DefaultScreen(X)
|
||||||
|
|
||||||
|
if err = resolveAtoms(); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
if err = setupAuxiliaryWindow(); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we have our atoms, we can initialize state.
|
||||||
|
selections = map[xproto.Atom]*selectionState{
|
||||||
|
xproto.AtomPrimary: {name: "PRIMARY"},
|
||||||
|
atomCLIPBOARD: {name: "CLIPBOARD"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
ev, xerr := X.WaitForEvent()
|
||||||
|
if xerr != nil {
|
||||||
|
log.Printf("Error: %s\n", xerr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ev != nil {
|
||||||
|
handleEvent(ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
nexgb/.gitignore
vendored
Normal file
2
nexgb/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
xgbgen/xgbgen
|
||||||
|
.*.swp
|
26
nexgb/AUTHORS
Normal file
26
nexgb/AUTHORS
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# This is the official list of neXGB authors for copyright purposes.
|
||||||
|
# This file is distinct from the CONTRIBUTORS file.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
Anthony Martin <ality@pbrane.org>
|
||||||
|
Firmansyah Adiputra <frm.adiputra@gmail.com>
|
||||||
|
Google Inc.
|
||||||
|
Scott Lawrence <bytbox@gmail.com>
|
||||||
|
Tor Andersson <tor.andersson@gmail.com>
|
||||||
|
|
||||||
|
# The names above come from the original x-go-binding by Google.
|
||||||
|
# The following list pertains to BurntSushi/xgb and janouch.name/haven/nexgb:
|
||||||
|
|
||||||
|
Andrew Gallant <jamslam@gmail.com>
|
||||||
|
Paul Sbarra <Sbarra.Paul@gmail.com>
|
||||||
|
Axel Wagner <mail@merovius.de>
|
||||||
|
snyh <snyh@snyh.org>
|
||||||
|
Alessandro Arzilli <alessandro.arzilli@gmail.com>
|
||||||
|
fangyuanziti <tiziyuanfang@gmail.com>
|
||||||
|
Bryan Matsuo <bryan.matsuo@gmail.com>
|
||||||
|
Rabin Vincent <rabin@rab.in>
|
||||||
|
Přemysl Janouch <p@janouch.name>
|
35
nexgb/CONTRIBUTORS
Normal file
35
nexgb/CONTRIBUTORS
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# This is the official list of people who may have contributed
|
||||||
|
# code to the neXGB 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.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# Names should be added to this file like so:
|
||||||
|
# Name <email address>
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
# The names above come from the original x-go-binding by Google.
|
||||||
|
# The following list pertains to BurntSushi/xgb and janouch.name/haven/nexgb:
|
||||||
|
|
||||||
|
Andrew Gallant <jamslam@gmail.com>
|
||||||
|
Paul Sbarra <Sbarra.Paul@gmail.com>
|
||||||
|
Axel Wagner <mail@merovius.de>
|
||||||
|
snyh <snyh@snyh.org>
|
||||||
|
Alessandro Arzilli <alessandro.arzilli@gmail.com>
|
||||||
|
fangyuanziti <tiziyuanfang@gmail.com>
|
||||||
|
Bryan Matsuo <bryan.matsuo@gmail.com>
|
||||||
|
Rabin Vincent <rabin@rab.in>
|
||||||
|
Přemysl Janouch <p@janouch.name>
|
27
nexgb/LICENSE
Normal file
27
nexgb/LICENSE
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
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.
|
76
nexgb/Makefile
Normal file
76
nexgb/Makefile
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# This Makefile is used by the developer. It is not needed in any way to build
|
||||||
|
# a checkout of the neXGB 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, Xinput, SYNC.
|
||||||
|
# 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
|
||||||
|
|
49
nexgb/README
Normal file
49
nexgb/README
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
neXGB is a fork of a fork of 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 modelled 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.
|
||||||
|
|
||||||
|
Quick usage
|
||||||
|
-----------
|
||||||
|
go get janouch.name/haven/nexgb
|
||||||
|
go run $GOPATH/src/janouch.name/haven/nexgb/examples/create-window/main.go
|
||||||
|
|
||||||
|
Přemysl Janouch's fork
|
||||||
|
----------------------
|
||||||
|
I've merged BurntSushi/xgb into haven as a subdirectory due to a/ inactivity
|
||||||
|
upstream, and b/ intentions to make incompatible changes meant to be in sync
|
||||||
|
with the rest of the project.
|
||||||
|
|
||||||
|
It's Not Exactly XGB anymore. Notable changes:
|
||||||
|
- included aarzilli's changes to support xcb-proto 1.12+, updated to 1.13
|
||||||
|
- improved documentation, using as much as possible from XCB's <doc> elements
|
||||||
|
- exporting {Major,Minor}Version of extensions for QueryVersion purposes
|
||||||
|
|
||||||
|
BurntSushi's fork
|
||||||
|
-----------------
|
||||||
|
I've forked the XGB repository from Google Code due to inactivity upstream.
|
||||||
|
|
||||||
|
Much of the code has been rewritten in an effort to support thread safety
|
||||||
|
and multiple extensions. Namely, go_client.py has been thrown away in favor
|
||||||
|
of an xgbgen package.
|
||||||
|
|
||||||
|
The biggest parts that *haven't* been rewritten by me are the connection and
|
||||||
|
authentication handshakes. They're inherently messy, and there's really no
|
||||||
|
reason to re-work them. 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*
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
Unless otherwise noted, the neXGB source files are distributed
|
||||||
|
under the BSD-style license found in the LICENSE file.
|
||||||
|
|
29
nexgb/STYLE
Normal file
29
nexgb/STYLE
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Keep all code to 80 columns or less. We, the maintainers, have plenty of screen
|
||||||
|
real estate, but enjoy 80 columns so that we 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 us, we 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.
|
||||||
|
|
110
nexgb/auth.go
Normal file
110
nexgb/auth.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package nexgb
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
}
|
156
nexgb/bigreq/bigreq.go
Normal file
156
nexgb/bigreq/bigreq.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
// 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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 0
|
||||||
|
MinorVersion = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableRequest writes a Enable request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
717
nexgb/composite/composite.go
Normal file
717
nexgb/composite/composite.go
Normal file
@ -0,0 +1,717 @@
|
|||||||
|
// 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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xfixes"
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 0
|
||||||
|
MinorVersion = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// createRegionFromBorderClipRequest writes a CreateRegionFromBorderClip request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOverlayWindowRequest writes a GetOverlayWindow request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// nameWindowPixmapRequest writes a NameWindowPixmap request to a byte slice for transfer.
|
||||||
|
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, 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, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice for transfer.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirectSubwindowsRequest writes a RedirectSubwindows request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirectWindowRequest writes a RedirectWindow request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// releaseOverlayWindowRequest writes a ReleaseOverlayWindow request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unredirectSubwindowsRequest writes a UnredirectSubwindows request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unredirectWindowRequest writes a UnredirectWindow request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
185
nexgb/conn.go
Normal file
185
nexgb/conn.go
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
package nexgb
|
||||||
|
|
||||||
|
/*
|
||||||
|
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:]
|
||||||
|
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
|
||||||
|
}
|
165
nexgb/cookie.go
Normal file
165
nexgb/cookie.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package nexgb
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
592
nexgb/damage/damage.go
Normal file
592
nexgb/damage/damage.go
Normal file
@ -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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xfixes"
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 1
|
||||||
|
MinorVersion = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// addRequest writes a Add request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// createRequest writes a Create request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// destroyRequest writes a Destroy request to a byte slice for transfer.
|
||||||
|
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, 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, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice for transfer.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion, 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, 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, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// subtractRequest writes a Subtract request to a byte slice for transfer.
|
||||||
|
func subtractRequest(c *xgb.Conn, Damage Damage, Repair, 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
|
||||||
|
}
|
144
nexgb/doc.go
Normal file
144
nexgb/doc.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
Package nexgb 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 neXGB. 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 neXGB typically fall under the realm of window manager and GUI kit
|
||||||
|
development, but other applications (like pagers, panels, tilers, etc.) may
|
||||||
|
also require neXGB.
|
||||||
|
|
||||||
|
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"
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
"janouch.name/haven/nexgb/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"
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
"janouch.name/haven/nexgb/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
|
||||||
|
|
||||||
|
neXGB 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 neXGB 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.
|
||||||
|
neXGB (before BurntSushi's 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 nexgb
|
712
nexgb/dpms/dpms.go
Normal file
712
nexgb/dpms/dpms.go
Normal file
@ -0,0 +1,712 @@
|
|||||||
|
// 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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 0
|
||||||
|
MinorVersion = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// capableRequest writes a Capable request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// disableRequest writes a Disable request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableRequest writes a Enable request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// forceLevelRequest writes a ForceLevel request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTimeoutsRequest writes a GetTimeouts request to a byte slice for transfer.
|
||||||
|
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, 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, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVersionRequest writes a GetVersion request to a byte slice for transfer.
|
||||||
|
func getVersionRequest(c *xgb.Conn, ClientMajorVersion, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// infoRequest writes a Info request to a byte slice for transfer.
|
||||||
|
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, SuspendTimeout, 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, SuspendTimeout, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// setTimeoutsRequest writes a SetTimeouts request to a byte slice for transfer.
|
||||||
|
func setTimeoutsRequest(c *xgb.Conn, StandbyTimeout, SuspendTimeout, 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
|
||||||
|
}
|
1812
nexgb/dri2/dri2.go
Normal file
1812
nexgb/dri2/dri2.go
Normal file
File diff suppressed because it is too large
Load Diff
3
nexgb/examples/atoms/.gitignore
vendored
Normal file
3
nexgb/examples/atoms/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
atoms
|
||||||
|
*.info
|
||||||
|
*.prof
|
12
nexgb/examples/atoms/Makefile
Normal file
12
nexgb/examples/atoms/Makefile
Normal file
@ -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
|
91
nexgb/examples/atoms/main.go
Normal file
91
nexgb/examples/atoms/main.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
"janouch.name/haven/nexgb/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)
|
||||||
|
}
|
107
nexgb/examples/create-window/main.go
Normal file
107
nexgb/examples/create-window/main.go
Normal file
@ -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"
|
||||||
|
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
"janouch.name/haven/nexgb/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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
nexgb/examples/doc.go
Normal file
21
nexgb/examples/doc.go
Normal file
@ -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://janouch.name/haven/nexgbutil
|
||||||
|
|
||||||
|
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
|
61
nexgb/examples/get-active-window/main.go
Normal file
61
nexgb/examples/get-active-window/main.go
Normal file
@ -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"
|
||||||
|
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
"janouch.name/haven/nexgb/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))
|
||||||
|
}
|
92
nexgb/examples/randr/main.go
Normal file
92
nexgb/examples/randr/main.go
Normal file
@ -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"
|
||||||
|
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
"janouch.name/haven/nexgb/randr"
|
||||||
|
"janouch.name/haven/nexgb/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)
|
||||||
|
}
|
||||||
|
}
|
40
nexgb/examples/xinerama/main.go
Normal file
40
nexgb/examples/xinerama/main.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Example xinerama shows how to query the geometry of all active heads.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
"janouch.name/haven/nexgb/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)
|
||||||
|
}
|
||||||
|
}
|
169
nexgb/ge/ge.go
Normal file
169
nexgb/ge/ge.go
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// 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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 1
|
||||||
|
MinorVersion = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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, 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, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice for transfer.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion, 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
|
||||||
|
}
|
10826
nexgb/glx/glx.go
Normal file
10826
nexgb/glx/glx.go
Normal file
File diff suppressed because it is too large
Load Diff
105
nexgb/help.go
Normal file
105
nexgb/help.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package nexgb
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
}
|
6566
nexgb/randr/randr.go
Normal file
6566
nexgb/randr/randr.go
Normal file
File diff suppressed because it is too large
Load Diff
1201
nexgb/record/record.go
Normal file
1201
nexgb/record/record.go
Normal file
File diff suppressed because it is too large
Load Diff
4003
nexgb/render/render.go
Normal file
4003
nexgb/render/render.go
Normal file
File diff suppressed because it is too large
Load Diff
1109
nexgb/res/res.go
Normal file
1109
nexgb/res/res.go
Normal file
File diff suppressed because it is too large
Load Diff
695
nexgb/screensaver/screensaver.go
Normal file
695
nexgb/screensaver/screensaver.go
Normal file
@ -0,0 +1,695 @@
|
|||||||
|
// 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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 1
|
||||||
|
MinorVersion = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryInfoRequest writes a QueryInfo request to a byte slice for transfer.
|
||||||
|
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, 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, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice for transfer.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// selectInputRequest writes a SelectInput request to a byte slice for transfer.
|
||||||
|
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, Y int16, Width, Height, BorderWidth uint16, Class, 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, Y int16, Width, Height, BorderWidth uint16, Class, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// setAttributesRequest writes a SetAttributes request to a byte slice for transfer.
|
||||||
|
func setAttributesRequest(c *xgb.Conn, Drawable xproto.Drawable, X, Y int16, Width, Height, BorderWidth uint16, Class, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// suspendRequest writes a Suspend request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsetAttributesRequest writes a UnsetAttributes request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
1021
nexgb/shape/shape.go
Normal file
1021
nexgb/shape/shape.go
Normal file
File diff suppressed because it is too large
Load Diff
942
nexgb/shm/shm.go
Normal file
942
nexgb/shm/shm.go
Normal file
@ -0,0 +1,942 @@
|
|||||||
|
// 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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 1
|
||||||
|
MinorVersion = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// attachRequest writes a Attach request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// attachFdRequest writes a AttachFd request to a byte slice for transfer.
|
||||||
|
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, 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, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// createPixmapRequest writes a CreatePixmap request to a byte slice for transfer.
|
||||||
|
func createPixmapRequest(c *xgb.Conn, Pid xproto.Pixmap, Drawable xproto.Drawable, Width, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// createSegmentRequest writes a CreateSegment request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// detachRequest writes a Detach request to a byte slice for transfer.
|
||||||
|
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, Y int16, Width, 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, Y int16, Width, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getImageRequest writes a GetImage request to a byte slice for transfer.
|
||||||
|
func getImageRequest(c *xgb.Conn, Drawable xproto.Drawable, X, Y int16, Width, 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, TotalHeight, SrcX, SrcY, SrcWidth, SrcHeight uint16, DstX, DstY int16, Depth, Format, 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, TotalHeight, SrcX, SrcY, SrcWidth, SrcHeight uint16, DstX, DstY int16, Depth, Format, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// putImageRequest writes a PutImage request to a byte slice for transfer.
|
||||||
|
func putImageRequest(c *xgb.Conn, Drawable xproto.Drawable, Gc xproto.Gcontext, TotalWidth, TotalHeight, SrcX, SrcY, SrcWidth, SrcHeight uint16, DstX, DstY int16, Depth, Format, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
29
nexgb/sync.go
Normal file
29
nexgb/sync.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package nexgb
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
363
nexgb/xcmisc/xcmisc.go
Normal file
363
nexgb/xcmisc/xcmisc.go
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
// 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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 1
|
||||||
|
MinorVersion = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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, 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, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVersionRequest writes a GetVersion request to a byte slice for transfer.
|
||||||
|
func getVersionRequest(c *xgb.Conn, ClientMajorVersion, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getXIDListRequest writes a GetXIDList request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getXIDRangeRequest writes a GetXIDRange request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
595
nexgb/xevie/xevie.go
Normal file
595
nexgb/xevie/xevie.go
Normal file
@ -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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 1
|
||||||
|
MinorVersion = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// endRequest writes a End request to a byte slice for transfer.
|
||||||
|
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, 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, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice for transfer.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// selectInputRequest writes a SelectInput request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendRequest writes a Send request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// startRequest writes a Start request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
1294
nexgb/xf86dri/xf86dri.go
Normal file
1294
nexgb/xf86dri/xf86dri.go
Normal file
File diff suppressed because it is too large
Load Diff
2672
nexgb/xf86vidmode/xf86vidmode.go
Normal file
2672
nexgb/xf86vidmode/xf86vidmode.go
Normal file
File diff suppressed because it is too large
Load Diff
2807
nexgb/xfixes/xfixes.go
Normal file
2807
nexgb/xfixes/xfixes.go
Normal file
File diff suppressed because it is too large
Load Diff
554
nexgb/xgb.go
Normal file
554
nexgb/xgb.go
Normal file
@ -0,0 +1,554 @@
|
|||||||
|
package nexgb
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
13
nexgb/xgbgen/LICENSE
Normal file
13
nexgb/xgbgen/LICENSE
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Copyright (c) 2012 - 2016, Andrew Gallant <jamslam@gmail.com>
|
||||||
|
Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||||
|
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
182
nexgb/xgbgen/context.go
Normal file
182
nexgb/xgbgen/context.go
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutComment writes each line of comment commented out to 'out'.
|
||||||
|
func (c *Context) PutComment(comment string) {
|
||||||
|
for _, line := range strings.Split(comment, "\n") {
|
||||||
|
c.Putln("// %s", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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("xgb \"janouch.name/haven/nexgb\"")
|
||||||
|
c.Putln("")
|
||||||
|
if c.protocol.isExt() {
|
||||||
|
c.Putln("\"janouch.name/haven/nexgb/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("\"janouch.name/haven/nexgb/%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("const (")
|
||||||
|
c.Putln("MajorVersion = %s", c.protocol.MajorVersion)
|
||||||
|
c.Putln("MinorVersion = %s", c.protocol.MinorVersion)
|
||||||
|
c.Putln(")")
|
||||||
|
c.Putln("")
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
74
nexgb/xgbgen/doc.go
Normal file
74
nexgb/xgbgen/doc.go
Normal file
@ -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
|
436
nexgb/xgbgen/expression.go
Normal file
436
nexgb/xgbgen/expression.go
Normal file
@ -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
|
||||||
|
}
|
402
nexgb/xgbgen/field.go
Normal file
402
nexgb/xgbgen/field.go
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
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
|
||||||
|
Comment string
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
MaskComment string
|
||||||
|
ListComment 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
|
||||||
|
Comment string
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
229
nexgb/xgbgen/go.go
Normal file
229
nexgb/xgbgen/go.go
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
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) {
|
||||||
|
if f.MaskComment != "" {
|
||||||
|
c.PutComment(f.MaskComment)
|
||||||
|
}
|
||||||
|
c.Putln("%s %s", f.MaskName, f.SrcType())
|
||||||
|
if f.ListComment != "" {
|
||||||
|
c.PutComment(f.ListComment)
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
if f.Comment != "" {
|
||||||
|
c.PutComment(f.Comment)
|
||||||
|
}
|
||||||
|
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)")
|
||||||
|
}
|
179
nexgb/xgbgen/go_error.go
Normal file
179
nexgb/xgbgen/go_error.go
Normal file
@ -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)
|
||||||
|
}
|
209
nexgb/xgbgen/go_event.go
Normal file
209
nexgb/xgbgen/go_event.go
Normal file
@ -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)
|
||||||
|
}
|
107
nexgb/xgbgen/go_list.go
Normal file
107
nexgb/xgbgen/go_list.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
288
nexgb/xgbgen/go_request_reply.go
Normal file
288
nexgb/xgbgen/go_request_reply.go
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"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.Doc.Description != "" {
|
||||||
|
c.PutComment(r.Doc.Description)
|
||||||
|
c.Putln("//")
|
||||||
|
}
|
||||||
|
|
||||||
|
allErrors := make([]string, 0, len(r.Doc.Errors))
|
||||||
|
for kind := range r.Doc.Errors {
|
||||||
|
allErrors = append(allErrors, kind)
|
||||||
|
}
|
||||||
|
sort.Strings(allErrors)
|
||||||
|
|
||||||
|
undocErrors := make([]string, 0)
|
||||||
|
for _, kind := range allErrors {
|
||||||
|
if desc := r.Doc.Errors[kind]; desc == "" {
|
||||||
|
undocErrors = append(undocErrors, kind)
|
||||||
|
} else {
|
||||||
|
c.PutComment(fmt.Sprintf("May return a %s error if %s%s", kind,
|
||||||
|
strings.ToLower(desc[:1]), desc[1:]))
|
||||||
|
c.Putln("//")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(undocErrors) > 0 {
|
||||||
|
c.Putln("// May return %s errors.", strings.Join(undocErrors, ", "))
|
||||||
|
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("// %s writes a %s request to a byte slice for transfer.",
|
||||||
|
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))
|
||||||
|
|
||||||
|
chunk := make([]string, 0)
|
||||||
|
chunkType := ""
|
||||||
|
flushChunk := func() {
|
||||||
|
if len(chunk) > 0 {
|
||||||
|
nameTypes = append(nameTypes, chunk[:len(chunk)-1]...)
|
||||||
|
nameTypes = append(nameTypes,
|
||||||
|
fmt.Sprintf("%s %s", chunk[len(chunk)-1], chunkType))
|
||||||
|
}
|
||||||
|
chunk = nil
|
||||||
|
chunkType = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, field := range r.Fields {
|
||||||
|
switch f := field.(type) {
|
||||||
|
case *ValueField:
|
||||||
|
flushChunk()
|
||||||
|
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:
|
||||||
|
curType := field.SrcType()
|
||||||
|
if curType != chunkType {
|
||||||
|
flushChunk()
|
||||||
|
}
|
||||||
|
chunk = append(chunk, field.SrcName())
|
||||||
|
chunkType = curType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flushChunk()
|
||||||
|
return strings.Join(nameTypes, ", ")
|
||||||
|
}
|
169
nexgb/xgbgen/go_single_field.go
Normal file
169
nexgb/xgbgen/go_single_field.go
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *SingleField) Define(c *Context) {
|
||||||
|
if f.Comment != "" {
|
||||||
|
c.PutComment(f.Comment)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
118
nexgb/xgbgen/go_struct.go
Normal file
118
nexgb/xgbgen/go_struct.go
Normal file
@ -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("")
|
||||||
|
}
|
147
nexgb/xgbgen/go_union.go
Normal file
147
nexgb/xgbgen/go_union.go
Normal file
@ -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("")
|
||||||
|
}
|
64
nexgb/xgbgen/main.go
Normal file
64
nexgb/xgbgen/main.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
protoPath = flag.String("proto-path",
|
||||||
|
"/usr/share/xcb", "path to directory of X protocol XML files")
|
||||||
|
gofmt = flag.Bool("gofmt", true,
|
||||||
|
"When disabled, gofmt will not be run before outputting Go code")
|
||||||
|
)
|
||||||
|
|
||||||
|
func usage() {
|
||||||
|
basename := os.Args[0]
|
||||||
|
if lastSlash := strings.LastIndex(basename, "/"); lastSlash > -1 {
|
||||||
|
basename = basename[lastSlash+1:]
|
||||||
|
}
|
||||||
|
log.Printf("Usage: %s [flags] xml-file", basename)
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = usage
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if flag.NArg() != 1 {
|
||||||
|
log.Printf("A single XML protocol file can be processed at once.")
|
||||||
|
flag.Usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the single XML file into []byte
|
||||||
|
xmlBytes, err := ioutil.ReadFile(flag.Arg(0))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the buffer, parse it, and filter it through gofmt.
|
||||||
|
c := newContext()
|
||||||
|
c.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
nexgb/xgbgen/misc.go
Normal file
49
nexgb/xgbgen/misc.go
Normal file
@ -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, "")
|
||||||
|
}
|
70
nexgb/xgbgen/protocol.go
Normal file
70
nexgb/xgbgen/protocol.go
Normal file
@ -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"
|
||||||
|
}
|
170
nexgb/xgbgen/request_reply.go
Normal file
170
nexgb/xgbgen/request_reply.go
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Doc contains any documentation, if present. Example C code is excluded.
|
||||||
|
type Doc struct {
|
||||||
|
Brief string // short description
|
||||||
|
Description string // long description
|
||||||
|
Fields map[string]string // from field name to description
|
||||||
|
Errors map[string]string // from error type to description
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescribeField is an accessor that supports nil receivers.
|
||||||
|
func (d *Doc) DescribeField(name string) string {
|
||||||
|
if d == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return d.Fields[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
Doc Doc // Documentation.
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
Doc Doc
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
31
nexgb/xgbgen/size.go
Normal file
31
nexgb/xgbgen/size.go
Normal file
@ -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}
|
||||||
|
}
|
498
nexgb/xgbgen/translation.go
Normal file
498
nexgb/xgbgen/translation.go
Normal file
@ -0,0 +1,498 @@
|
|||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"regexp"
|
||||||
|
"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)),
|
||||||
|
Doc: x.Doc.Translate(),
|
||||||
|
}
|
||||||
|
for _, field := range x.Fields {
|
||||||
|
ev.Fields = append(ev.Fields, field.Translate(ev, &ev.Doc))
|
||||||
|
}
|
||||||
|
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, nil)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLErrorCopy) Translate() *ErrorCopy {
|
||||||
|
return &ErrorCopy{
|
||||||
|
xmlName: x.Name,
|
||||||
|
Number: x.Number,
|
||||||
|
Old: newTranslation(x.Ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XCB documentation is positively stuffed with TODOs. We'd like to make it
|
||||||
|
// look a bit less shitty, so remove those as they don't convey information.
|
||||||
|
//
|
||||||
|
// ^TODO
|
||||||
|
// ^TODO:
|
||||||
|
// ^TODO: question?
|
||||||
|
// ^TODO: Some words
|
||||||
|
// ^TODO: some words
|
||||||
|
// ^TODO: some words with full stop.
|
||||||
|
// ^TODO: some words with full stop. and a question?
|
||||||
|
// ... (TODO),
|
||||||
|
// ... (TODO).
|
||||||
|
// ... (TODO: a question?).
|
||||||
|
// ... TODO: a question?
|
||||||
|
// ... (word TODO) ...
|
||||||
|
var todoRE = regexp.MustCompile(`(?m:^TODO.*| \([^)]*TODO[^)]*\)| TODO:.*)`)
|
||||||
|
var paraRE = regexp.MustCompile(`\n{3,}`)
|
||||||
|
|
||||||
|
var backticksRE = regexp.MustCompile("`(?:xcb_|XCB_)?(.*?)(?:_t)?`")
|
||||||
|
|
||||||
|
// fixDocumentation tries to translate XCB documentation to match XGB.
|
||||||
|
// Note that <doc> blocks only appear in xproto, so this doesn't have that much
|
||||||
|
// of a value--users still need to read Xlib or X11 protocol docs.
|
||||||
|
// Despite that, it's better to have something than nothing.
|
||||||
|
//
|
||||||
|
// We don't attempt to add proper prefixes to enum values or guess at
|
||||||
|
// specific XCB_NONE types (the latter is undecidable).
|
||||||
|
//
|
||||||
|
// We can't decide whether `fields_len` should be converted to len(Fields)
|
||||||
|
// or FieldsLen because e.g. StringLen is retained in ImageText8/16 but
|
||||||
|
// PointsLen is implied by the length of the Points slice in PolyLine.
|
||||||
|
func fixDocumentation(xcb string) string {
|
||||||
|
last, result := 0, make([]byte, 0, len(xcb))
|
||||||
|
for _, m := range backticksRE.FindAllStringSubmatchIndex(xcb, -1) {
|
||||||
|
result = append(result, xcb[last:m[0]]...)
|
||||||
|
inner := xcb[m[2]:m[3]]
|
||||||
|
last = m[1]
|
||||||
|
|
||||||
|
// Do not convert atom names to identifiers, mainly _NET_WM_*.
|
||||||
|
if strings.Contains(inner, "WM_") {
|
||||||
|
result = append(result, inner...)
|
||||||
|
} else {
|
||||||
|
result = append(result, splitAndTitle(inner)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = todoRE.ReplaceAllLiteral(append(result, xcb[last:]...), nil)
|
||||||
|
result = paraRE.ReplaceAllLiteral(result, []byte("\n\n"))
|
||||||
|
return strings.TrimSpace(string(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XMLDoc) Translate() Doc {
|
||||||
|
if x == nil {
|
||||||
|
return Doc{}
|
||||||
|
}
|
||||||
|
d := Doc{
|
||||||
|
Brief: fixDocumentation(x.Brief),
|
||||||
|
Description: fixDocumentation(x.Description),
|
||||||
|
Fields: make(map[string]string),
|
||||||
|
Errors: make(map[string]string),
|
||||||
|
}
|
||||||
|
for _, x := range x.Fields {
|
||||||
|
d.Fields[x.Name] = fixDocumentation(x.Description)
|
||||||
|
}
|
||||||
|
for _, x := range x.Errors {
|
||||||
|
d.Errors[x.Type] = fixDocumentation(x.Description)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
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, nil)
|
||||||
|
}
|
||||||
|
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, nil)
|
||||||
|
}
|
||||||
|
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(),
|
||||||
|
Doc: x.Doc.Translate(),
|
||||||
|
}
|
||||||
|
for _, field := range x.Fields {
|
||||||
|
if field.XMLName.Local == "fd" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.Fields = append(r.Fields, field.Translate(r, &r.Doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)),
|
||||||
|
Doc: x.Doc.Translate(),
|
||||||
|
}
|
||||||
|
for _, field := range x.Fields {
|
||||||
|
if field.XMLName.Local == "fd" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.Fields = append(r.Fields, field.Translate(r, &r.Doc))
|
||||||
|
}
|
||||||
|
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{}, doc *Doc) 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),
|
||||||
|
Comment: doc.DescribeField(x.Name),
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
MaskComment: doc.DescribeField(x.ValueMaskName),
|
||||||
|
ListComment: doc.DescribeField(x.ValueListName),
|
||||||
|
}
|
||||||
|
case "switch":
|
||||||
|
swtch := &SwitchField{
|
||||||
|
Name: x.Name,
|
||||||
|
Expr: x.Expr.Translate(),
|
||||||
|
Bitcases: make([]*Bitcase, len(x.Bitcases)),
|
||||||
|
Comment: doc.DescribeField(x.Name),
|
||||||
|
}
|
||||||
|
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, nil)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
391
nexgb/xgbgen/type.go
Normal file
391
nexgb/xgbgen/type.go
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
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
|
||||||
|
Doc Doc
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
159
nexgb/xgbgen/xml.go
Normal file
159
nexgb/xgbgen/xml.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
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 XMLDocField struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Description string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLDocError struct {
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Description string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLDoc struct {
|
||||||
|
Brief string `xml:"brief"`
|
||||||
|
Description string `xml:"description"`
|
||||||
|
Example string `xml:"example"`
|
||||||
|
Fields []*XMLDocField `xml:"field"`
|
||||||
|
Errors []*XMLDocError `xml:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
Doc *XMLDoc `xml:"doc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLReply struct {
|
||||||
|
Fields []*XMLField `xml:",any"`
|
||||||
|
Doc *XMLDoc `xml:"doc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMLEvent struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Number int `xml:"number,attr"`
|
||||||
|
NoSequence bool `xml:"no-sequence-number,attr"`
|
||||||
|
Fields []*XMLField `xml:",any"`
|
||||||
|
Doc *XMLDoc `xml:"doc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
86
nexgb/xgbgen/xml_fields.go
Normal file
86
nexgb/xgbgen/xml_fields.go
Normal file
@ -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
|
||||||
|
}
|
717
nexgb/xinerama/xinerama.go
Normal file
717
nexgb/xinerama/xinerama.go
Normal file
@ -0,0 +1,717 @@
|
|||||||
|
// 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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 1
|
||||||
|
MinorVersion = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getScreenCountRequest writes a GetScreenCount request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getScreenSizeRequest writes a GetScreenSize request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStateRequest writes a GetState request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// isActiveRequest writes a IsActive request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryScreensRequest writes a QueryScreens request to a byte slice for transfer.
|
||||||
|
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, 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, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryVersionRequest writes a QueryVersion request to a byte slice for transfer.
|
||||||
|
func queryVersionRequest(c *xgb.Conn, Major, 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
|
||||||
|
}
|
2520
nexgb/xprint/xprint.go
Normal file
2520
nexgb/xprint/xprint.go
Normal file
File diff suppressed because it is too large
Load Diff
15513
nexgb/xproto/xproto.go
Normal file
15513
nexgb/xproto/xproto.go
Normal file
File diff suppressed because it is too large
Load Diff
384
nexgb/xproto/xproto_test.go
Normal file
384
nexgb/xproto/xproto_test.go
Normal file
@ -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"
|
||||||
|
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
2249
nexgb/xselinux/xselinux.go
Normal file
2249
nexgb/xselinux/xselinux.go
Normal file
File diff suppressed because it is too large
Load Diff
417
nexgb/xtest/xtest.go
Normal file
417
nexgb/xtest/xtest.go
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
// 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 (
|
||||||
|
xgb "janouch.name/haven/nexgb"
|
||||||
|
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MajorVersion = 2
|
||||||
|
MinorVersion = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// compareCursorRequest writes a CompareCursor request to a byte slice for transfer.
|
||||||
|
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, Detail byte, Time uint32, Root xproto.Window, RootX, 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, Detail byte, Time uint32, Root xproto.Window, RootX, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// fakeInputRequest writes a FakeInput request to a byte slice for transfer.
|
||||||
|
func fakeInputRequest(c *xgb.Conn, Type, Detail byte, Time uint32, Root xproto.Window, RootX, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVersionRequest writes a GetVersion request to a byte slice for transfer.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// grabControlRequest writes a GrabControl request to a byte slice for transfer.
|
||||||
|
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
|
||||||
|
}
|
3048
nexgb/xv/xv.go
Normal file
3048
nexgb/xv/xv.go
Normal file
File diff suppressed because it is too large
Load Diff
1037
nexgb/xvmc/xvmc.go
Normal file
1037
nexgb/xvmc/xvmc.go
Normal file
File diff suppressed because it is too large
Load Diff
199
prototypes/xgb-selection.go
Normal file
199
prototypes/xgb-selection.go
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
// Follow X11 selection contents as they are being changed.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"janouch.name/haven/nexgb"
|
||||||
|
"janouch.name/haven/nexgb/xfixes"
|
||||||
|
"janouch.name/haven/nexgb/xproto"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
X, err := nexgb.NewConn()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
if err := xfixes.Init(X); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
setup := xproto.Setup(X)
|
||||||
|
screen := setup.DefaultScreen(X)
|
||||||
|
|
||||||
|
// Resolve a few required atoms that are not static in the core protocol.
|
||||||
|
const (
|
||||||
|
clipboard = "CLIPBOARD"
|
||||||
|
utf8String = "UTF8_STRING"
|
||||||
|
incr = "INCR"
|
||||||
|
)
|
||||||
|
|
||||||
|
atomCLIPBOARD, err := xproto.InternAtom(X, false,
|
||||||
|
uint16(len(clipboard)), clipboard).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomUTF8String, err := xproto.InternAtom(X, false,
|
||||||
|
uint16(len(utf8String)), utf8String).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomINCR, err := xproto.InternAtom(X, false,
|
||||||
|
uint16(len(incr)), incr).Reply()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a window.
|
||||||
|
wid, err := xproto.NewWindowId(X)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, 0, 0, 1, 1,
|
||||||
|
0, xproto.WindowClassInputOutput, screen.RootVisual, xproto.CwEventMask,
|
||||||
|
[]uint32{xproto.EventMaskPropertyChange})
|
||||||
|
|
||||||
|
// Select for update events of each selection.
|
||||||
|
_ = xfixes.QueryVersion(X, xfixes.MajorVersion, xfixes.MinorVersion)
|
||||||
|
|
||||||
|
_ = xfixes.SelectSelectionInput(X, wid,
|
||||||
|
xproto.AtomPrimary, xfixes.SelectionEventMaskSetSelectionOwner|
|
||||||
|
xfixes.SelectionEventMaskSelectionWindowDestroy|
|
||||||
|
xfixes.SelectionEventMaskSelectionClientClose)
|
||||||
|
|
||||||
|
_ = xfixes.SelectSelectionInput(X, wid,
|
||||||
|
atomCLIPBOARD.Atom, xfixes.SelectionEventMaskSetSelectionOwner|
|
||||||
|
xfixes.SelectionEventMaskSelectionWindowDestroy|
|
||||||
|
xfixes.SelectionEventMaskSelectionClientClose)
|
||||||
|
|
||||||
|
type selectionState struct {
|
||||||
|
name string // name of the selection
|
||||||
|
inProgress xproto.Timestamp // timestamp of retrieved selection
|
||||||
|
buffer []byte // UTF-8 text buffer
|
||||||
|
incr bool // INCR running
|
||||||
|
incrFailed bool // INCR failure indicator
|
||||||
|
}
|
||||||
|
states := map[xproto.Atom]*selectionState{
|
||||||
|
xproto.AtomPrimary: {name: "PRIMARY"},
|
||||||
|
atomCLIPBOARD.Atom: {name: "CLIPBOARD"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
ev, xerr := X.WaitForEvent()
|
||||||
|
if xerr != nil {
|
||||||
|
log.Printf("Error: %s\n", xerr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ev == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e := ev.(type) {
|
||||||
|
case xfixes.SelectionNotifyEvent:
|
||||||
|
state, ok := states[e.Selection]
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not checking whether we should give up when our current retrieval
|
||||||
|
// attempt is interrupted--the timeout mostly solves this.
|
||||||
|
if e.Owner == xproto.WindowNone {
|
||||||
|
// This may potentially log before a request for past data
|
||||||
|
// finishes, if only because of INCR. Just saying.
|
||||||
|
log.Printf("%s: -\n", state.name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't try to process two things at once. Each request gets a few
|
||||||
|
// seconds to finish, then we move on, hoping that a property race
|
||||||
|
// doesn't commence. Ideally we'd set up a separate queue for these
|
||||||
|
// skipped requests and process them later.
|
||||||
|
if state.inProgress != 0 && e.Timestamp-state.inProgress < 5000 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// ICCCM says we should ensure the named property doesn't exist.
|
||||||
|
_ = xproto.DeleteProperty(X, e.Window, e.Selection)
|
||||||
|
|
||||||
|
_ = xproto.ConvertSelection(X, e.Window, e.Selection,
|
||||||
|
atomUTF8String.Atom, e.Selection, e.Timestamp)
|
||||||
|
|
||||||
|
state.inProgress = e.Timestamp
|
||||||
|
state.incr = false
|
||||||
|
|
||||||
|
case xproto.SelectionNotifyEvent:
|
||||||
|
state, ok := states[e.Selection]
|
||||||
|
if e.Requestor != wid || !ok || e.Time != state.inProgress {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
state.inProgress = 0
|
||||||
|
if e.Property == xproto.AtomNone {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: This is simplified and doesn't necessarily read it all.
|
||||||
|
// Maybe we could use setup.MaximumRequestLength.
|
||||||
|
// Though xorg-xserver doesn't seem to limit the length of replies
|
||||||
|
// or even the length of properties in the first place.
|
||||||
|
// It only has a huge (0xffffffff - sizeof(xChangePropertyReq))/4
|
||||||
|
// limit for ChangeProperty requests, which is way more than
|
||||||
|
// max-request-len (normally 0xffff), even though I can't
|
||||||
|
// XChangeProperty more than 0xffffe0 bytes at a time.
|
||||||
|
reply, err := xproto.GetProperty(X, false, /* delete */
|
||||||
|
e.Requestor, e.Property, xproto.GetPropertyTypeAny,
|
||||||
|
0, 0x8000).Reply()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
state.buffer = nil
|
||||||
|
|
||||||
|
// When you select a lot of text in VIM, it starts the ICCCM
|
||||||
|
// INCR mechanism, from which there is no opt-out.
|
||||||
|
if reply.Type == atomINCR.Atom {
|
||||||
|
state.inProgress = e.Time
|
||||||
|
state.incr = true
|
||||||
|
state.incrFailed = false
|
||||||
|
} else if reply.Type == atomUTF8String.Atom && reply.Format == 8 {
|
||||||
|
log.Printf("%s: '%s'\n", state.name, string(reply.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = xproto.DeleteProperty(X, e.Requestor, e.Property)
|
||||||
|
|
||||||
|
case xproto.PropertyNotifyEvent:
|
||||||
|
state, ok := states[e.Atom]
|
||||||
|
if e.Window != wid || e.State != xproto.PropertyNewValue ||
|
||||||
|
!ok || !state.incr {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
reply, err := xproto.GetProperty(X, false, /* delete */
|
||||||
|
e.Window, e.Atom, xproto.GetPropertyTypeAny, 0, 0x8000).Reply()
|
||||||
|
if err != nil {
|
||||||
|
state.incrFailed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if reply.Type == atomUTF8String.Atom && reply.Format == 8 {
|
||||||
|
state.buffer = append(state.buffer, reply.Value...)
|
||||||
|
} else {
|
||||||
|
// We need to keep deleting the property.
|
||||||
|
state.incrFailed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if reply.ValueLen == 0 {
|
||||||
|
if !state.incrFailed {
|
||||||
|
log.Printf("%s: '%s'\n",
|
||||||
|
state.name, string(state.buffer))
|
||||||
|
}
|
||||||
|
state.inProgress = 0
|
||||||
|
state.incr = false
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = xproto.DeleteProperty(X, e.Window, e.Atom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user