Compare commits
200 Commits
4fa84cc877
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
9bbe70de85
|
|||
|
486b58525f
|
|||
|
c3cc608570
|
|||
|
d643187333
|
|||
|
103704b183
|
|||
|
18e8e11ad4
|
|||
|
1528ed2db0
|
|||
|
5f0d5bca70
|
|||
|
442fa5d660
|
|||
|
8b376694d3
|
|||
|
22e3861851
|
|||
|
9603456cd6
|
|||
|
b832a38ca6
|
|||
|
6353dd156a
|
|||
|
7a2ea02c8d
|
|||
|
bafd5ef221
|
|||
|
ca245e4aca
|
|||
|
c3905349b0
|
|||
|
f9e1f9a244
|
|||
|
4aab0b22ae
|
|||
|
5ab2977548
|
|||
|
a927713a81
|
|||
|
5ae8c24b8d
|
|||
|
ef24d7980c
|
|||
|
6228693b22
|
|||
|
be27f00685
|
|||
|
61083027a3
|
|||
|
5b432fcc0b
|
|||
|
04e19f5186
|
|||
|
49a685c32e
|
|||
|
d763ce619d
|
|||
|
8276f6bcb9
|
|||
|
dd5c583e8b
|
|||
|
63d18d068d
|
|||
|
d47a8d2237
|
|||
|
a979edf5b7
|
|||
|
9db34a955b
|
|||
|
35974efe21
|
|||
|
8f542c7120
|
|||
|
ea8c59961c
|
|||
|
750f2139f5
|
|||
|
3d002bc540
|
|||
|
084a0a94b0
|
|||
|
91b1120c4a
|
|||
|
f7f892fb59
|
|||
|
d0ce3e3e66
|
|||
|
3a5cc216bb
|
|||
|
a049249d81
|
|||
|
d4eb9cde39
|
|||
|
cb1c8f8563
|
|||
|
13275f1dd3
|
|||
|
030a23c1a2
|
|||
|
0d37e5bc8a
|
|||
|
eae39b13c2
|
|||
|
95f183aa48
|
|||
|
e7ea35f304
|
|||
|
056391eeca
|
|||
|
f99615c850
|
|||
|
d04c140a69
|
|||
|
cd6c9e4d8c
|
|||
|
bf14fd5e04
|
|||
|
228c3f3914
|
|||
|
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
|
||
|
632b3ae494
|
|||
|
e8381d86ce
|
|||
|
254ceb810c
|
|||
|
df082e1dee
|
|||
|
3e42402e2b
|
|||
|
c8fd1068d1
|
|||
|
cea1792913
|
|||
|
ff7518c74d
|
|||
|
9e070e9648
|
|||
|
0c2853a8ae
|
|||
|
41e04fdc9f
|
|||
|
23586eae01
|
|||
|
9424579c75
|
|||
|
32beda3c90
|
|||
|
30f2366f9a
|
|||
|
215e3e8630
|
|||
|
44b01ccb17
|
|||
|
0f7fcca7ce
|
|||
|
1fdf14f351
|
|||
|
0ef66c7282
|
|||
|
68d7e34b03
|
|||
|
cc08b5457c
|
|||
|
f8bcfe447c
|
|||
|
fb648c37be
|
|||
|
bb0113021a
|
|||
|
5a40d7c2ed
|
|||
|
f32e2f1483
|
|||
|
62418ebb54
|
|||
|
6b45865e3d
|
|||
|
198cb87036
|
|||
|
23f637dd47
|
|||
|
4cd460886e
|
|||
|
4d8376fd3c
|
|||
|
01c3933a07
|
|||
|
|
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 |
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1 +1,15 @@
|
||||
/build
|
||||
/hasp/hasp
|
||||
/hid/hid
|
||||
/hnc/hnc
|
||||
/hpcu/hpcu
|
||||
/hswg/hswg
|
||||
/ht/ht
|
||||
/prototypes/tls-autodetect
|
||||
/prototypes/xgb-draw
|
||||
/prototypes/xgb-image
|
||||
/prototypes/xgb-keys
|
||||
/prototypes/xgb-monitors
|
||||
/prototypes/xgb-selection
|
||||
/prototypes/xgb-text-viewer
|
||||
/prototypes/xgb-window
|
||||
/prototypes/xgb-xrender
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
|
||||
Copyright (c) 2018 - 2022, Přemysl Eric Janouch <p@janouch.name>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
246
README
246
README
@@ -1,246 +0,0 @@
|
||||
Project haven
|
||||
=============
|
||||
|
||||
haven is an umbrella project for a range of mostly desktop applications.
|
||||
|
||||
Goal
|
||||
----
|
||||
The greater goal is to create a fresh computing environment for daily work and
|
||||
play--easily controllable, reasonably complex, both visually and internally
|
||||
unified and last but not least responsive. One should be able to use it
|
||||
comfortably with a 60% keyboard and no pointing device.
|
||||
|
||||
haven serves as a testing ground, leaning on the side of compromises. It aims
|
||||
to use today's Linux desktop as a support, relying on X11/Wayland, existing
|
||||
window managers and web browsers.
|
||||
|
||||
The focus is therefore on going breadth-first, not depth-first. Applications
|
||||
only need to be good enough to be able to replace their older siblings at all.
|
||||
I.e. for me personally.
|
||||
|
||||
Scope
|
||||
-----
|
||||
Subproject names aim to have the minimum viable, reasonably identifiable name.
|
||||
To group them together, a common prefix of "h" is used. The second column is
|
||||
what should be used as the name in .desktop files, just like the GNOME project
|
||||
figured out it would make sense:
|
||||
|
||||
- hbe - bitmap editor
|
||||
- hbfe - bitmap font editor
|
||||
- he - text editor
|
||||
- hfm - file manager
|
||||
- hib - IRC bouncer
|
||||
- hic - IRC client
|
||||
- hid - IRC daemon
|
||||
- hiv - image viewer
|
||||
- hm - mail client
|
||||
- hmpc - MPD client
|
||||
- hnc - netcat-alike
|
||||
- ho - all-powerful organizer
|
||||
- hsm - system monitor
|
||||
- hss - spreadsheets
|
||||
- htd - translation dictionary
|
||||
- htk - GUI toolkit library
|
||||
|
||||
See Projects for more information about the individual projects.
|
||||
|
||||
Some taken names in Debian: hd (important), hte, hy.
|
||||
|
||||
Identity
|
||||
--------
|
||||
The name merely hints at motivations and otherwise isn't of any practical
|
||||
significance other than that we need an identifier. This time I resisted using
|
||||
something offensive.
|
||||
|
||||
A logo covering the entire project is not needed and it seems hard to figure out
|
||||
anything meaninful anyway, though we might pick a specific font to use for the
|
||||
project name <1>.
|
||||
|
||||
The only mascot I can think of would be a black and white or generally grayscale
|
||||
My Little Pony OC but I don't really want to bring my own kinks into the project
|
||||
and I'd also need to learn how to draw one so that I don't infringe on someone
|
||||
else's copyright, or find someone else to do it. Anyway, in lack of a proper
|
||||
logo, she could have a simple "h" or "hvn" for a cutie mark <2>.
|
||||
|
||||
__
|
||||
<2> _/ /_ <1> | _ _ _ _
|
||||
\ _ \ |/ \ / \| \ / /_\ |/ \
|
||||
/ / / / | | | | \ / | | |
|
||||
/_/ /_/ | | \_/| \/ \_/ | |
|
||||
|
||||
I'm not sure where I took this "h" letter styling from, it seems too familiar.
|
||||
The above illustrations also show how awful it looks when a logo is just
|
||||
a stylized version of the first letter of a name when you put the two next to
|
||||
each other. Distinctly redundant. Facebook and Twitter are doing fine, though,
|
||||
perhaps because they do not use them together like that.
|
||||
|
||||
Technicalities
|
||||
--------------
|
||||
|
||||
Languages
|
||||
~~~~~~~~~
|
||||
Primarily Golang with limited C interfacing glue, secondarily C++17, mostly for
|
||||
when the former cannot be reasonably used because of dependencies.
|
||||
|
||||
Build system
|
||||
~~~~~~~~~~~~
|
||||
https://github.com/zevv/bucklespring is a good example of a rather simplified
|
||||
project that makes do with a single Makefile, even for cross-compilation on
|
||||
Windows. Let us avoid CMake and the likes of it.
|
||||
|
||||
It seems that Go can link dynamically, therefore I could build libhaven.so
|
||||
https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ
|
||||
https://stackoverflow.com/questions/1757090/shared-library-in-go
|
||||
and have the rest of the package as rather small binaries linking to it.
|
||||
The "cannot implicitly include runtime/cgo in a shared library" error is solved
|
||||
by "go install", which again requires "-pkgdir" because of privileges.
|
||||
libstd.so is a beautiful 30 megabytes (compared to libc.a: 4.9M).
|
||||
|
||||
GUI
|
||||
~~~
|
||||
Probably build on top of X11/Xlib or xgb<1>. Wayland can wait until it
|
||||
stabilizes--it should not be a major issue switching the backends.
|
||||
Vector graphics can be handled by draw2d<2>.
|
||||
<1> https://rosettacode.org/wiki/Window_creation/X11#Go
|
||||
<2> https://github.com/llgcode/draw2d
|
||||
|
||||
The c2 wiki unsurprisingly has a lot of material around the design and
|
||||
realisation of GUIs, which might be useful.
|
||||
|
||||
It seems like an aligning/constraint-based "layout manager" will be one of the
|
||||
first harder problems here. However I certainly don't want to used fixed
|
||||
coordinates as they would introduce problems with different fonts and i18n.
|
||||
|
||||
We could use BDF fonts from the X11 distribution, but draw2d has native support
|
||||
for FreeType fonts and it's more of a choice between vectors and bitmaps.
|
||||
|
||||
The looks will be heavily inspired by Haiku and Windows 2000 and the user will
|
||||
have no say in this, for simplicity.
|
||||
|
||||
Internationalisation
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
For i18n https://github.com/leonelquinteros/gotext could be used, however I'll
|
||||
probably give up on this issue as I'm fine enough with English.
|
||||
|
||||
Go also has x/text packages for this purpose, which might be better than GNU,
|
||||
but is essentially still in development.
|
||||
|
||||
Versioning
|
||||
~~~~~~~~~~
|
||||
Versions are for end users. We can use a zero-based, strictly increasing
|
||||
number, be it a simple sequence or date-based numbers such as 1807. If we do
|
||||
this at all, we will eventually also need to release patch versions.
|
||||
|
||||
Since dates don't seem to convey important information, let us settle on 0, 1,
|
||||
1.1, 2, 3, 3.1, 3.2, ... In practice releases are going to be scarce, unless
|
||||
we find a person to take care of it. Note that there is no major/minor pair--
|
||||
a new project will be created when radical philosophical or architectural
|
||||
changes are to be made. See Goal.
|
||||
|
||||
Projects
|
||||
--------
|
||||
These are sorted in the order in which they should be created in order to gain
|
||||
the best possible momentum. The htk GUI toolkit is implied as a side product
|
||||
permeating the entire list.
|
||||
|
||||
Some information is omitted from these descriptions and lies either in my head
|
||||
or in my other notes.
|
||||
|
||||
hid -- IRC daemon
|
||||
~~~~~~~~~~~~~~~~~
|
||||
This project is unimportant by itself, its sole purpose is to gain experience
|
||||
with Go on something that I have already done and understand well. Nothing
|
||||
beyond achieving feature parity is in the initial scope.
|
||||
|
||||
One possibility of complicating would be adding simple WebSocket listeners but
|
||||
that's already been done for me https://github.com/kiwiirc/webircgateway and
|
||||
it's even in Go, I just need to set up kiwiirc.
|
||||
|
||||
Later, when we have a pleasant IRC client, implement either the P10 or the TS6
|
||||
server-linking protocol and make atheme work with a generic module.
|
||||
Alternatively add support for plugins. The goal is to allow creating integrated
|
||||
bridges to various public forums.
|
||||
|
||||
hnc -- netcat-alike
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
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
|
||||
a good implementation with TLS support.
|
||||
|
||||
hib and hic -- IRC bouncer and client
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
An IRC client is a good starting application for building a GUI toolkit, as the
|
||||
UI can afford to be truly minimalistic and most of it is text.
|
||||
|
||||
To resolve an issue I have with my current IRC client, the client is going to be
|
||||
split into two parts: a bouncer that manages all connections and state, and
|
||||
a separate GUI that communicates with the backend over TLS/WebSocket. Perhaps
|
||||
only the per-buffer input line is going to be desynchronized.
|
||||
|
||||
https://godoc.org/github.com/gorilla/websocket
|
||||
|
||||
htd -- translation dictionary
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
This specific kind of application doesn't need a lot of user interface either,
|
||||
just a tab bar, text entry and two columns of text with simple formatting.
|
||||
|
||||
For simplicity we will establish a custom dictionary format based on either
|
||||
simple compress/gzip with separate files in StarDict style or, since we don't
|
||||
really strive for random access and memory-efficiency (those 120M that sdtui
|
||||
takes with my 10 dictionaries isn't particularly bad), pack everything with
|
||||
archive/zip.
|
||||
|
||||
Instead of ICU we may use x/text/collate and that's about everything we need.
|
||||
Since we have our own format, we may expect the indexed to be ordered by the
|
||||
locale's rules, assuming they don't change between versions.
|
||||
|
||||
hmpc -- MPD client
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Here the focus will be on the GUI toolkit. I don't expect this application to
|
||||
get big, since its predecessor nncmpp isn't either. The daemon takes care of
|
||||
all complex stuff. It would be nice to add lyrics and search later, though.
|
||||
|
||||
hiv -- image viewer
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
JPG, PNG, first frame of GIF. Zoom. Going through adjacent files in directory
|
||||
using cursor keys. Possibly a dialog with image metadata.
|
||||
|
||||
he -- text editor
|
||||
~~~~~~~~~~~~~~~~~
|
||||
VIM controls, no scripting, no syntax highlight, single-file, made for variable-
|
||||
-width/proportional fonts. Initially done primarily to produce a text editing
|
||||
widget, which is going to be an interesting challenge, arguably better solved by
|
||||
whole program composition. Scintilla may provide some inspiration.
|
||||
|
||||
In the second stage, support for the Language Server Protocol will be added so
|
||||
that the project can be edited using its own tools. Some scripting, perhaps
|
||||
a tiny subset of VimL, might be desirable. Or other means of configuration.
|
||||
|
||||
The real model for the editor is Qt Creator with FakeVIM, though this is not to
|
||||
be a clone of it, e.g. the various "Output" lists could be just special buffers,
|
||||
which may be have names starting on "// ".
|
||||
|
||||
hfm -- file manager
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
All we need to achieve here is replace Midnight Commander, which besides the
|
||||
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,
|
||||
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.
|
||||
|
||||
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,
|
||||
which might or might not deserve its own program.
|
||||
|
||||
ho -- all-powerful organizer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Zettelkasten with fulltext search, arbitrary reciprocal links, arbitrary tags.
|
||||
Flat storage. Should be able to use translation dictionaries for search hints.
|
||||
|
||||
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/
|
||||
https://www.sqlite.org/fts3.html#full_text_index_queries (FTS4 seems better)
|
||||
|
||||
The rest
|
||||
~~~~~~~~
|
||||
Currently there are no significant, specific plans about the other applications.
|
||||
155
README.adoc
Normal file
155
README.adoc
Normal file
@@ -0,0 +1,155 @@
|
||||
Project haven
|
||||
=============
|
||||
|
||||
haven is an umbrella project for a range of mostly desktop applications.
|
||||
|
||||
This README is being converted into a wiki on the wiki branch, also publicly
|
||||
available at https://p.janouch.name/haven/Haven.html[]. We should only document
|
||||
here that which has been created, as the extent of the rationale and plans is
|
||||
gigantic and seems to have a life of its own.
|
||||
|
||||
Goal
|
||||
----
|
||||
The greater goal is to create a fresh computing environment for daily work and
|
||||
play--easily controllable, reasonably complex, both visually and internally
|
||||
unified and last but not least responsive. One should be able to use it
|
||||
comfortably with a 60% keyboard and no pointing device.
|
||||
|
||||
haven serves as a testing ground, leaning on the side of compromises. It aims
|
||||
to use today's Linux desktop as a support, relying on X11/Wayland, existing
|
||||
window managers and web browsers.
|
||||
|
||||
The focus is therefore on going breadth-first, not depth-first. Applications
|
||||
only need to be good enough to be able to replace their older siblings at all.
|
||||
I.e. for me personally.
|
||||
|
||||
Scope
|
||||
-----
|
||||
Subproject names aim to have the minimum viable, reasonably identifiable name.
|
||||
To group them together, a common prefix of "h" is used. The second column is
|
||||
what should be used as the name in .desktop files, just like the GNOME project
|
||||
figured out it would make sense:
|
||||
|
||||
- hbe - bitmap editor
|
||||
- hbfe - bitmap font editor
|
||||
- he - text editor
|
||||
- hfm - file manager
|
||||
- hm - mail client
|
||||
- hnc - netcat-alike
|
||||
- ho - all-powerful organizer
|
||||
- hsm - system monitor
|
||||
- hss - spreadsheets
|
||||
- htd - translation dictionary
|
||||
- ht - terminal emulator
|
||||
- htk - GUI toolkit library
|
||||
|
||||
See Projects for more information about the individual projects.
|
||||
|
||||
Some taken names in Debian: hd (important), hte, hy.
|
||||
|
||||
Projects
|
||||
--------
|
||||
These are sorted in the order in which they should be created in order to gain
|
||||
the best possible momentum. The htk GUI toolkit is implied as a side product
|
||||
permeating the entire list.
|
||||
|
||||
Some information is omitted from these descriptions and lies either in my head
|
||||
or in my other notes.
|
||||
|
||||
hnc -- netcat-alike
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
The result of testing xK/xS with telnet, OpenSSL s_client, OpenBSD nc, GNU nc,
|
||||
and Ncat is that neither of them can properly shutdown the connection.
|
||||
We need 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.
|
||||
|
||||
hswg -- static website generator
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
link:hswg/README.adoc[See hswg's README for details.]
|
||||
|
||||
ht -- terminal emulator
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Similar scope to st(1). Clever display of internal padding for better looks.
|
||||
|
||||
he -- text editor
|
||||
~~~~~~~~~~~~~~~~~
|
||||
VIM controls, no scripting, no syntax highlight, single-file, made for
|
||||
variable-width/proportional fonts. Initially done primarily to produce a text
|
||||
editing widget, which is going to be an interesting challenge, arguably better
|
||||
solved by whole program composition. Scintilla may provide some inspiration.
|
||||
|
||||
In the second stage, support for the Language Server Protocol will be added so
|
||||
that the project can be edited using its own tools. Some scripting, perhaps
|
||||
a tiny subset of VimL, might be desirable. Or other means of configuration.
|
||||
|
||||
Visual block mode or the color column may still be implemented.
|
||||
|
||||
The real model for the editor is Qt Creator with FakeVIM, though this is not to
|
||||
be a clone of it, e.g. the various "Output" lists could be just special buffers,
|
||||
which may be have names starting on "// ".
|
||||
|
||||
.Resources:
|
||||
- http://doc.cat-v.org/plan_9/4th_edition/papers/sam/
|
||||
|
||||
ho -- all-powerful organizer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Zettelkasten with fulltext search, arbitrary reciprocal links, arbitrary tags.
|
||||
Flat storage. Should be able to use translation dictionaries for search hints.
|
||||
|
||||
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/
|
||||
- https://www.sqlite.org/fts3.html#full_text_index_queries (FTS4 seems better)
|
||||
|
||||
htd -- translation dictionary
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
This specific kind of application doesn't need a lot of user interface either,
|
||||
just a tab bar, text entry and two columns of text with simple formatting.
|
||||
|
||||
For simplicity we will establish a custom dictionary format based on either
|
||||
simple compress/gzip with separate files in StarDict style or, since we don't
|
||||
really strive for random access and memory-efficiency (those 120M that sdtui
|
||||
takes with my 10 dictionaries isn't particularly bad), pack everything with
|
||||
archive/zip.
|
||||
|
||||
Instead of ICU we may use x/text/collate and that's about everything we need.
|
||||
Since we have our own format, we may expect the index to be ordered by the
|
||||
locale's rules, assuming they don't change between versions.
|
||||
|
||||
hfm -- file manager
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
All we need to achieve here is replace Midnight Commander, which besides the
|
||||
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,
|
||||
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.
|
||||
ht could probably also be integrated.
|
||||
|
||||
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,
|
||||
which might or might not deserve its own program.
|
||||
|
||||
hss -- spreadsheets
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
The first version doesn't need to be able to reference other cells, and can more
|
||||
or less be a CSV editor.
|
||||
|
||||
We can take inspiration from Excel:
|
||||
|
||||
- https://docs.microsoft.com/en-us/office/client-developer/excel/excel-recalculation
|
||||
- https://www.microsoft.com/en-us/research/uploads/prod/2018/03/build-systems.pdf
|
||||
|
||||
The rest
|
||||
~~~~~~~~
|
||||
Currently there are no significant, specific plans about the other applications.
|
||||
26
go.mod
Normal file
26
go.mod
Normal file
@@ -0,0 +1,26 @@
|
||||
module janouch.name/haven
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802
|
||||
github.com/bytesparadise/libasciidoc v0.7.1-0.20221008082129-967103fe8df6
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b
|
||||
)
|
||||
|
||||
require github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
|
||||
require (
|
||||
github.com/alecthomas/chroma/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.9 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
418
go.sum
Normal file
418
go.sum
Normal file
@@ -0,0 +1,418 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/chroma/v2 v2.3.0 h1:83xfxrnjv8eK+Cf8qZDzNo3PPF9IbTWHs7z28GY6D0U=
|
||||
github.com/alecthomas/chroma/v2 v2.3.0/go.mod h1:mZxeWZlxP2Dy+/8cBob2PYd8O2DwNAzave5AY7A2eQw=
|
||||
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||
github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/bytesparadise/libasciidoc v0.7.1-0.20221008082129-967103fe8df6 h1:H6OENzfxMVi4XJvFZWpbnttN5ncGnhC2qS15slyDdbw=
|
||||
github.com/bytesparadise/libasciidoc v0.7.1-0.20221008082129-967103fe8df6/go.mod h1:Q2ZeBQ1fko5+NTUTs8rGu9gjTtbVaD6Qxg37GOPYdN4=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/felixge/fgtrace v0.1.0/go.mod h1:VYPh/jE5zczuRiQge0AtcpNmcLhV/epE/wpfVYQALlU=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mna/pigeon v1.1.0 h1:EjlvVbkGnNGemf8OrjeJX0nH8orujY/HkJgzJtd7kxc=
|
||||
github.com/mna/pigeon v1.1.0/go.mod h1:rkFeDZ0gc+YbnrXPw0q2RlI0QRuKBBPu67fgYIyGRNg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190830223141-573d9926052a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
LC_ALL=C exec awk '
|
||||
/^[0-9]+ *(ERR|RPL)_[A-Z]+ *".*"$/ {
|
||||
match($0, /".*"/);
|
||||
ids[$1] = $2;
|
||||
texts[$2] = substr($0, RSTART, RLENGTH);
|
||||
}
|
||||
END {
|
||||
print "package " ENVIRON["GOPACKAGE"] "\n\nconst ("
|
||||
for (i in ids)
|
||||
printf("\t%s = %s\n", ids[i], i)
|
||||
print ")\n\nvar defaultReplies = map[int]string{"
|
||||
for (i in ids)
|
||||
print "\t" ids[i] ": " texts[ids[i]] ","
|
||||
print "}"
|
||||
}'
|
||||
@@ -1,87 +0,0 @@
|
||||
1 RPL_WELCOME ":Welcome to the Internet Relay Network %s!%s@%s"
|
||||
2 RPL_YOURHOST ":Your host is %s, running version %s"
|
||||
3 RPL_CREATED ":This server was created %s"
|
||||
4 RPL_MYINFO "%s %s %s %s"
|
||||
5 RPL_ISUPPORT "%s :are supported by this server"
|
||||
211 RPL_STATSLINKINFO "%s %d %d %d %d %d %d"
|
||||
212 RPL_STATSCOMMANDS "%s %d %d %d"
|
||||
219 RPL_ENDOFSTATS "%c :End of STATS report"
|
||||
221 RPL_UMODEIS "+%s"
|
||||
242 RPL_STATSUPTIME ":Server Up %d days %d:%02d:%02d"
|
||||
251 RPL_LUSERCLIENT ":There are %d users and %d services on %d servers"
|
||||
252 RPL_LUSEROP "%d :operator(s) online"
|
||||
253 RPL_LUSERUNKNOWN "%d :unknown connection(s)"
|
||||
254 RPL_LUSERCHANNELS "%d :channels formed"
|
||||
255 RPL_LUSERME ":I have %d clients and %d servers"
|
||||
301 RPL_AWAY "%s :%s"
|
||||
302 RPL_USERHOST ":%s"
|
||||
303 RPL_ISON ":%s"
|
||||
305 RPL_UNAWAY ":You are no longer marked as being away"
|
||||
306 RPL_NOWAWAY ":You have been marked as being away"
|
||||
311 RPL_WHOISUSER "%s %s %s * :%s"
|
||||
312 RPL_WHOISSERVER "%s %s :%s"
|
||||
313 RPL_WHOISOPERATOR "%s :is an IRC operator"
|
||||
314 RPL_WHOWASUSER "%s %s %s * :%s"
|
||||
315 RPL_ENDOFWHO "%s :End of WHO list"
|
||||
317 RPL_WHOISIDLE "%s %d :seconds idle"
|
||||
318 RPL_ENDOFWHOIS "%s :End of WHOIS list"
|
||||
319 RPL_WHOISCHANNELS "%s :%s"
|
||||
322 RPL_LIST "%s %d :%s"
|
||||
323 RPL_LISTEND ":End of LIST"
|
||||
324 RPL_CHANNELMODEIS "%s +%s"
|
||||
329 RPL_CREATIONTIME "%s %d"
|
||||
331 RPL_NOTOPIC "%s :No topic is set"
|
||||
332 RPL_TOPIC "%s :%s"
|
||||
333 RPL_TOPICWHOTIME "%s %s %d"
|
||||
341 RPL_INVITING "%s %s"
|
||||
346 RPL_INVITELIST "%s %s"
|
||||
347 RPL_ENDOFINVITELIST "%s :End of channel invite list"
|
||||
348 RPL_EXCEPTLIST "%s %s"
|
||||
349 RPL_ENDOFEXCEPTLIST "%s :End of channel exception list"
|
||||
351 RPL_VERSION "%s.%d %s :%s"
|
||||
352 RPL_WHOREPLY "%s %s %s %s %s %s :%d %s"
|
||||
353 RPL_NAMREPLY "%c %s :%s"
|
||||
364 RPL_LINKS "%s %s :%d %s"
|
||||
365 RPL_ENDOFLINKS "%s :End of LINKS list"
|
||||
366 RPL_ENDOFNAMES "%s :End of NAMES list"
|
||||
367 RPL_BANLIST "%s %s"
|
||||
368 RPL_ENDOFBANLIST "%s :End of channel ban list"
|
||||
369 RPL_ENDOFWHOWAS "%s :End of WHOWAS"
|
||||
372 RPL_MOTD ":- %s"
|
||||
375 RPL_MOTDSTART ":- %s Message of the day - "
|
||||
376 RPL_ENDOFMOTD ":End of MOTD command"
|
||||
391 RPL_TIME "%s :%s"
|
||||
401 ERR_NOSUCHNICK "%s :No such nick/channel"
|
||||
402 ERR_NOSUCHSERVER "%s :No such server"
|
||||
403 ERR_NOSUCHCHANNEL "%s :No such channel"
|
||||
404 ERR_CANNOTSENDTOCHAN "%s :Cannot send to channel"
|
||||
406 ERR_WASNOSUCHNICK "%s :There was no such nickname"
|
||||
409 ERR_NOORIGIN ":No origin specified"
|
||||
410 ERR_INVALIDCAPCMD "%s :%s"
|
||||
411 ERR_NORECIPIENT ":No recipient given (%s)"
|
||||
412 ERR_NOTEXTTOSEND ":No text to send"
|
||||
421 ERR_UNKNOWNCOMMAND "%s: Unknown command"
|
||||
422 ERR_NOMOTD ":MOTD File is missing"
|
||||
423 ERR_NOADMININFO "%s :No administrative info available"
|
||||
431 ERR_NONICKNAMEGIVEN ":No nickname given"
|
||||
432 ERR_ERRONEOUSNICKNAME "%s :Erroneous nickname"
|
||||
433 ERR_NICKNAMEINUSE "%s :Nickname is already in use"
|
||||
441 ERR_USERNOTINCHANNEL "%s %s :They aren't on that channel"
|
||||
442 ERR_NOTONCHANNEL "%s :You're not on that channel"
|
||||
443 ERR_USERONCHANNEL "%s %s :is already on channel"
|
||||
445 ERR_SUMMONDISABLED ":SUMMON has been disabled"
|
||||
446 ERR_USERSDISABLED ":USERS has been disabled"
|
||||
451 ERR_NOTREGISTERED ":You have not registered"
|
||||
461 ERR_NEEDMOREPARAMS "%s :Not enough parameters"
|
||||
462 ERR_ALREADYREGISTERED ":Unauthorized command (already registered)"
|
||||
467 ERR_KEYSET "%s :Channel key already set"
|
||||
471 ERR_CHANNELISFULL "%s :Cannot join channel (+l)"
|
||||
472 ERR_UNKNOWNMODE "%c :is unknown mode char to me for %s"
|
||||
473 ERR_INVITEONLYCHAN "%s :Cannot join channel (+i)"
|
||||
474 ERR_BANNEDFROMCHAN "%s :Cannot join channel (+b)"
|
||||
475 ERR_BADCHANNELKEY "%s :Cannot join channel (+k)"
|
||||
476 ERR_BADCHANMASK "%s :Bad Channel Mask"
|
||||
481 ERR_NOPRIVILEGES ":Permission Denied- You're not an IRC operator"
|
||||
482 ERR_CHANOPRIVSNEEDED "%s :You're not channel operator"
|
||||
501 ERR_UMODEUNKNOWNFLAG ":Unknown MODE flag"
|
||||
502 ERR_USERSDONTMATCH ":Cannot change mode for other users"
|
||||
3313
hid/main.go
3313
hid/main.go
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
|
||||
// Copyright (c) 2018, Přemysl Eric Janouch <p@janouch.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted.
|
||||
@@ -129,7 +129,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
if result.err != nil {
|
||||
log("%s: %s", "stdin", result.err)
|
||||
log("stdin: %s", result.err)
|
||||
fromUser = nil
|
||||
if err := conn.CloseWrite(); err != nil {
|
||||
log("remote: %s", err)
|
||||
|
||||
367
hpcu/main.go
Normal file
367
hpcu/main.go
Normal file
@@ -0,0 +1,367 @@
|
||||
// hpcu unifies the PRIMARY and CLIPBOARD X11 selections for text contents.
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"janouch.name/haven/nexgb"
|
||||
"janouch.name/haven/nexgb/xfixes"
|
||||
"janouch.name/haven/nexgb/xproto"
|
||||
)
|
||||
|
||||
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.Sequence == 0 && 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 handleXfixesSelectionNotify(e xfixes.SelectionNotifyEvent) {
|
||||
state, ok := selections[e.Selection]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Ownership request has been granted, don't ask ourselves for data.
|
||||
if e.Owner == wid {
|
||||
state.owning = e.SelectionTimestamp
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func handleSelectionNotify(e xproto.SelectionNotifyEvent) {
|
||||
state, ok := selections[e.Selection]
|
||||
if e.Requestor != wid || !ok || e.Time != state.inProgress {
|
||||
return
|
||||
}
|
||||
|
||||
state.inProgress = 0
|
||||
if e.Property == xproto.AtomNone {
|
||||
return
|
||||
}
|
||||
|
||||
state.buffer = nil
|
||||
reply, err := getProperty(e.Requestor, e.Property)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
func handlePropertyNotify(e xproto.PropertyNotifyEvent) {
|
||||
state, ok := selections[e.Atom]
|
||||
if e.Window != wid || e.State != xproto.PropertyNewValue ||
|
||||
!ok || !state.incr {
|
||||
return
|
||||
}
|
||||
|
||||
reply, err := getProperty(e.Window, e.Atom)
|
||||
if err != nil {
|
||||
state.incrFailed = true
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
func handleSelectionRequest(e xproto.SelectionRequestEvent) {
|
||||
property := e.Property
|
||||
if property == xproto.AtomNone {
|
||||
property = e.Target
|
||||
}
|
||||
|
||||
state, ok := selections[e.Selection]
|
||||
if e.Owner != wid || !ok {
|
||||
return
|
||||
}
|
||||
|
||||
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 != xproto.TimeCurrentTime && 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 handleXEvent(ev nexgb.Event) {
|
||||
switch e := ev.(type) {
|
||||
case xfixes.SelectionNotifyEvent:
|
||||
handleXfixesSelectionNotify(e)
|
||||
case xproto.SelectionNotifyEvent:
|
||||
handleSelectionNotify(e)
|
||||
case xproto.PropertyNotifyEvent:
|
||||
handlePropertyNotify(e)
|
||||
case xproto.SelectionRequestEvent:
|
||||
handleSelectionRequest(e)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
handleXEvent(ev)
|
||||
}
|
||||
}
|
||||
}
|
||||
101
hswc/main.go
Normal file
101
hswc/main.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
long = regexp.MustCompile(`(.{72}\S*)\s+`)
|
||||
file, requests *os.File
|
||||
m sync.Mutex
|
||||
)
|
||||
|
||||
func wrap(s string) string {
|
||||
return strings.ReplaceAll(long.ReplaceAllString(
|
||||
strings.ReplaceAll(s, "\r", ""), "$1\n"), "\n", "\n ")
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
if err := r.ParseForm(); err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
text := r.FormValue("text")
|
||||
if len(text) > 64<<10 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
j, _ := json.Marshal(struct {
|
||||
URI string
|
||||
Headers http.Header
|
||||
Form url.Values
|
||||
}{
|
||||
URI: r.RequestURI,
|
||||
Headers: r.Header,
|
||||
Form: r.Form,
|
||||
})
|
||||
|
||||
if s, err := file.Stat(); err != nil {
|
||||
log.Fatalln(err)
|
||||
} else if s.Size()+int64(len(text)) > 64<<20 {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
} else if r.Form.Has("submit") {
|
||||
// <input type="submit"> should not be named, and thus received.
|
||||
//
|
||||
// If this is not enough to filter out most spammers, consider also:
|
||||
// - Header: "Origin" should not be missing for POST.
|
||||
// - Header: "Accept" should not be "*/*".
|
||||
// - Header: "Accept-Language" and "Accept-Encoding" should be present.
|
||||
// - Form: _charset_ should not be kept verbatim,
|
||||
// seeing as Safari/Chromium/Firefox all pass UTF-8,
|
||||
// in accordance with HTML5.
|
||||
w.WriteHeader(http.StatusTeapot)
|
||||
} else {
|
||||
fmt.Fprintf(file, "%s %s\n %s\n",
|
||||
time.Now().Local().Format(time.RFC1123), r.RequestURI, wrap(text))
|
||||
if err := file.Sync(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// To help filter out spammers.
|
||||
fmt.Fprintf(requests, "%s\n", j)
|
||||
if err := requests.Sync(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "Saved.")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 3 {
|
||||
log.Fatalf("Usage: %s BIND DB\n", os.Args[0])
|
||||
}
|
||||
|
||||
var err error
|
||||
if file, err = os.OpenFile(os.Args[2],
|
||||
os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if requests, err = os.OpenFile(os.Args[2]+".requests",
|
||||
os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
http.HandleFunc("/", handler)
|
||||
log.Fatalln(http.ListenAndServe(os.Args[1], nil))
|
||||
}
|
||||
62
hswg/README.adoc
Normal file
62
hswg/README.adoc
Normal file
@@ -0,0 +1,62 @@
|
||||
hswg: a static website generator
|
||||
================================
|
||||
|
||||
hswg wraps libasciidoc to make it understand more syntax, namely
|
||||
two-line/underlined titles, and can be run either as a filter, or as a simple
|
||||
wiki-like site generator.
|
||||
|
||||
Gitea/cgit AsciiDoc processor
|
||||
-----------------------------
|
||||
Wrap hswg in the following script to give it a few superpowers:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
# Make this also work for cgit which, strangely enough, is willing to render
|
||||
# /anything/ via the /about route, only passing through image/* unchanged.
|
||||
if [ -z "$GITEA_PREFIX_SRC" ]; then
|
||||
test "${1%.adoc}" != "$1" || exit 1
|
||||
cgit_fixups='s/<div class="content">/<div>/'
|
||||
export GITEA_PREFIX_SRC=. GITEA_PREFIX_RAW=.
|
||||
fi
|
||||
|
||||
# libasciidoc can't be helped in other ways so far, adding support for:
|
||||
# - the original 'italics' syntax
|
||||
# - double-line headings (part of haven's hswg which invokes libasciidoc)
|
||||
# - make links to other documents work, normally an attribute could be used
|
||||
perl -pe "s|'([-~/\\.\\w]+)'|_\$1_|g;" | hswg 2>/dev/null | \
|
||||
perl -pe 's|(href=")([^/][^:]*?")|$1$ENV{GITEA_PREFIX_SRC}/$2|;' \
|
||||
-e 's|(src=")([^/][^:]*?")|$1$ENV{GITEA_PREFIX_RAW}/$2|;' \
|
||||
-e "$cgit_fixups"
|
||||
```
|
||||
|
||||
Then, to set it up in Gitea, include the following snippet in your _app.ini_:
|
||||
|
||||
```
|
||||
[markup.asciidoc]
|
||||
ENABLED = true
|
||||
FILE_EXTENSIONS = .adoc,.asciidoc
|
||||
RENDER_COMMAND = /usr/local/bin/hswg-gitea
|
||||
IS_INPUT_FILE = false
|
||||
```
|
||||
|
||||
Similarly for cgit, the following _cgitrc_ snippet might do the job:
|
||||
|
||||
```
|
||||
about-filter=/usr/local/bin/hswg-gitea
|
||||
readme=:README.adoc
|
||||
```
|
||||
|
||||
If parsing fails for some reason, the contents will be wrapped in HTML verbatim
|
||||
as plain text.
|
||||
|
||||
Wiki mode
|
||||
---------
|
||||
The program will read a Go template for the index page from its standard input,
|
||||
and another template for rendered pages from the path given as its first
|
||||
argument. The second argument specifies the output filename for the index page,
|
||||
and the last one is the document directory.
|
||||
|
||||
Consult the source code for a list template variables.
|
||||
|
||||
All pages will be initially rerendered on startup, and then the directory will
|
||||
be watched for changes in real time using the inotify API.
|
||||
54
hswg/asciidoc.go
Normal file
54
hswg/asciidoc.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// isTitle returns the title level if the lines seem to form a title,
|
||||
// zero otherwise. Input lines may inclide trailing newlines.
|
||||
func isTitle(line1, line2 []byte) int {
|
||||
// This is a very naĂŻve method, we should target graphemes (thus at least
|
||||
// NFC normalize the lines first) and account for wide characters.
|
||||
diff := utf8.RuneCount(line1) - utf8.RuneCount(line2)
|
||||
if len(line2) < 2 || diff < -1 || diff > 1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// "Don't be fooled by back-to-back delimited blocks."
|
||||
// Still gets fooled by other things, though.
|
||||
if bytes.IndexFunc(line1, func(r rune) bool {
|
||||
return unicode.IsLetter(r) || unicode.IsNumber(r)
|
||||
}) < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// The underline must be homogenous.
|
||||
for _, r := range bytes.TrimRight(line2, "\r\n") {
|
||||
if r != line2[0] {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 1 + strings.IndexByte("=-~^+", line2[0])
|
||||
}
|
||||
|
||||
func writeLine(w *io.PipeWriter, cur, next []byte) []byte {
|
||||
if level := isTitle(cur, next); level > 0 {
|
||||
w.Write(append(bytes.Repeat([]byte{'='}, level), ' '))
|
||||
next = nil
|
||||
}
|
||||
w.Write(cur)
|
||||
return next
|
||||
}
|
||||
|
||||
// ConvertTitles converts AsciiDoc two-line (underlined) titles to single-line.
|
||||
func ConvertTitles(w *io.PipeWriter, input []byte) {
|
||||
var last []byte
|
||||
for _, cur := range bytes.SplitAfter(input, []byte{'\n'}) {
|
||||
last = writeLine(w, last, cur)
|
||||
}
|
||||
writeLine(w, last, nil)
|
||||
}
|
||||
529
hswg/main.go
Normal file
529
hswg/main.go
Normal file
@@ -0,0 +1,529 @@
|
||||
// Program hswg is a static website generator employing libasciidoc with added
|
||||
// support for two-line/underlined titles, and postprocessing "wiki" InterLinks.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/bytesparadise/libasciidoc/pkg/configuration"
|
||||
"github.com/bytesparadise/libasciidoc/pkg/parser"
|
||||
"github.com/bytesparadise/libasciidoc/pkg/renderer/sgml/html5"
|
||||
"github.com/bytesparadise/libasciidoc/pkg/types"
|
||||
"github.com/bytesparadise/libasciidoc/pkg/validator"
|
||||
)
|
||||
|
||||
// Metadata contains select metadata about a rendered document.
|
||||
type Metadata struct {
|
||||
types.Metadata
|
||||
|
||||
// Note that this includes entries from the front-matter
|
||||
// (see parser.ApplySubstitutions <- parser.ParseDocument).
|
||||
Attributes types.Attributes
|
||||
}
|
||||
|
||||
// IsDraft returns whether the document is marked as a draft, and should not
|
||||
// be linked anywhere else.
|
||||
func (m *Metadata) IsDraft() bool { return m.Attributes.Has("draft") }
|
||||
|
||||
// Attr is a shortcut for retrieving document attributes by name.
|
||||
func (m *Metadata) Attr(name string) string {
|
||||
return m.Attributes.GetAsStringWithDefault(name, "")
|
||||
}
|
||||
|
||||
// AttrList is similar to Attr, but splits the result at commas,
|
||||
// and trims whitespace around array elements.
|
||||
func (m *Metadata) AttrList(name string) []string {
|
||||
if !m.Attributes.Has(name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := strings.Split(m.Attr(name), ",")
|
||||
for i := range res {
|
||||
res[i] = strings.TrimSpace(res[i])
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Render converts an io.Reader with an AsciiDoc document to HTML. So long as
|
||||
// the file could be read at all, it will always return a non-empty document.
|
||||
func Render(r io.Reader, config *configuration.Configuration) (
|
||||
html *bytes.Buffer, meta Metadata, err error) {
|
||||
html = bytes.NewBuffer(nil)
|
||||
|
||||
var input []byte
|
||||
if input, err = ioutil.ReadAll(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
defer pw.Close()
|
||||
ConvertTitles(pw, input)
|
||||
}()
|
||||
|
||||
// io.Copy(os.Stdout, pr)
|
||||
// return
|
||||
|
||||
var doc *types.Document
|
||||
if doc, err = parser.ParseDocument(pr, config); err == nil {
|
||||
doctype := config.Attributes.GetAsStringWithDefault(
|
||||
types.AttrDocType, "article")
|
||||
problems, verr := validator.Validate(doc, doctype)
|
||||
if verr != nil {
|
||||
fmt.Fprintln(os.Stderr, verr)
|
||||
}
|
||||
for _, problem := range problems {
|
||||
fmt.Fprintln(os.Stderr, problem.Message)
|
||||
}
|
||||
meta.Metadata, err = html5.Render(doc, config, html)
|
||||
}
|
||||
if err != nil {
|
||||
// Fallback: output all the text sanitized for direct inclusion.
|
||||
html.Reset()
|
||||
|
||||
_, _ = html.WriteString("<pre>")
|
||||
for _, line := range bytes.Split(input, []byte{'\n'}) {
|
||||
_ = xml.EscapeText(html, line)
|
||||
_, _ = html.WriteString("\n")
|
||||
}
|
||||
_, _ = html.WriteString("</pre>")
|
||||
}
|
||||
meta.Attributes = config.Attributes
|
||||
return
|
||||
}
|
||||
|
||||
// Entry contains all context information about a single page.
|
||||
type Entry struct {
|
||||
Metadata // metadata
|
||||
PathSource string // path to source AsciiDoc
|
||||
PathDestination string // path to destination HTML
|
||||
mtime time.Time // modification time
|
||||
raw []byte // raw inner document
|
||||
Content template.HTML // inner document with expanded LinkWords
|
||||
backlinks map[string]bool // what documents link back here
|
||||
Backlinks []template.HTML
|
||||
}
|
||||
|
||||
// Published returns the date when the entry was published, or nil if unknown.
|
||||
func (e *Entry) Published() *time.Time {
|
||||
if d, ok := e.Attributes.GetAsString("date"); !ok {
|
||||
return nil
|
||||
} else if t, err := time.Parse(time.RFC3339, d); err == nil {
|
||||
return &t
|
||||
} else if t, err := time.Parse("2006-01-02", d); err == nil {
|
||||
return &t
|
||||
} else {
|
||||
log.Printf("%s: date: %s\n", e.PathSource, err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
globs = []string{"*.adoc", "*.asciidoc"}
|
||||
extRE = regexp.MustCompile(`\.[^/.]*$`)
|
||||
)
|
||||
|
||||
func pathToName(path string) string {
|
||||
return stripExtension(filepath.Base(path))
|
||||
}
|
||||
|
||||
func stripExtension(path string) string {
|
||||
return extRE.ReplaceAllString(path, "")
|
||||
}
|
||||
|
||||
func resultPath(path string) string {
|
||||
if m := extRE.FindStringIndex(path); m != nil {
|
||||
return path[:m[0]] + ".html"
|
||||
}
|
||||
return path + ".html"
|
||||
}
|
||||
|
||||
func makeLink(m *map[string]*Entry, name string) string {
|
||||
e := (*m)[name]
|
||||
return fmt.Sprintf("<a href='%s'>%s</a>",
|
||||
filepath.Clean(e.PathDestination), name)
|
||||
}
|
||||
|
||||
var linkWordRE = regexp.MustCompile(`\b\p{Lu}\p{L}*\b`)
|
||||
|
||||
func expand(m *map[string]*Entry, name string, chunk []byte) []byte {
|
||||
return linkWordRE.ReplaceAllFunc(chunk, func(match []byte) []byte {
|
||||
if link, ok := (*m)[string(match)]; ok && string(match) != name &&
|
||||
!link.IsDraft() {
|
||||
link.backlinks[name] = true
|
||||
return []byte(makeLink(m, string(match)))
|
||||
}
|
||||
return match
|
||||
})
|
||||
}
|
||||
|
||||
var tagRE = regexp.MustCompile(`<[^<>]+>`)
|
||||
|
||||
func renderEntry(name string, e *Entry) error {
|
||||
f, err := os.Open(e.PathSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i, err := f.Stat(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
e.mtime = i.ModTime()
|
||||
}
|
||||
|
||||
var html *bytes.Buffer
|
||||
if html, e.Metadata, err = Render(f, configuration.NewConfiguration(
|
||||
configuration.WithFilename(e.PathSource),
|
||||
configuration.WithLastUpdated(e.mtime),
|
||||
configuration.WithAttribute("toc", "preamble"),
|
||||
configuration.WithAttribute("toc-title", "<h2>Contents</h2>"),
|
||||
configuration.WithAttribute("source-highlighter", "chroma"),
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Every page needs to have a title.
|
||||
if e.Title == "" {
|
||||
e.Title = name
|
||||
}
|
||||
|
||||
e.raw = html.Bytes()
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeEntry(path string) *Entry {
|
||||
return &Entry{
|
||||
PathSource: path,
|
||||
PathDestination: resultPath(path),
|
||||
}
|
||||
}
|
||||
|
||||
// loadEntries creates a map from document names to their page entries.
|
||||
func loadEntries(dirname string) (map[string]*Entry, error) {
|
||||
entries := map[string]*Entry{}
|
||||
for _, glob := range globs {
|
||||
matches, err := filepath.Glob(filepath.Join(dirname, glob))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", dirname, err)
|
||||
}
|
||||
for _, path := range matches {
|
||||
name := pathToName(path)
|
||||
if conflict, ok := entries[name]; ok {
|
||||
return nil, fmt.Errorf("%s: conflicts with %s",
|
||||
name, conflict.PathSource)
|
||||
}
|
||||
entries[name] = makeEntry(path)
|
||||
}
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func writeEntry(e *Entry, t *template.Template,
|
||||
entries *map[string]*Entry) error {
|
||||
f, err := os.Create(e.PathDestination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
backlinks := []string{}
|
||||
for name := range e.backlinks {
|
||||
backlinks = append(backlinks, name)
|
||||
}
|
||||
sort.Strings(backlinks)
|
||||
for _, name := range backlinks {
|
||||
e.Backlinks =
|
||||
append(e.Backlinks, template.HTML(makeLink(entries, name)))
|
||||
}
|
||||
|
||||
return t.Execute(f, e)
|
||||
}
|
||||
|
||||
func writeIndex(path string, t *template.Template,
|
||||
entries *map[string]*Entry) error {
|
||||
// Reorder entries reversely, primarily by date, secondarily by filename.
|
||||
ordered := []*Entry{}
|
||||
for _, e := range *entries {
|
||||
ordered = append(ordered, e)
|
||||
}
|
||||
|
||||
sort.Slice(ordered, func(i, j int) bool {
|
||||
a, b := ordered[i], ordered[j]
|
||||
p1, p2 := a.Published(), b.Published()
|
||||
if p1 == nil && p2 != nil {
|
||||
return true
|
||||
}
|
||||
if p1 == nil && p2 == nil {
|
||||
return a.PathSource > b.PathSource
|
||||
}
|
||||
if p2 == nil {
|
||||
return false
|
||||
}
|
||||
if p1.Equal(*p2) {
|
||||
return a.PathSource > b.PathSource
|
||||
}
|
||||
return p2.Before(*p1)
|
||||
})
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(p): Splitting content to categories would be nice. Or tags.
|
||||
return t.Execute(f, ordered)
|
||||
}
|
||||
|
||||
func finalizeEntries(entries *map[string]*Entry, t *template.Template,
|
||||
indexPath string, indexT *template.Template) {
|
||||
// The initial render of a large amount of entries is resource-intensive.
|
||||
var wg sync.WaitGroup
|
||||
for name, e := range *entries {
|
||||
e.backlinks = map[string]bool{}
|
||||
if e.raw != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func(name string, e *Entry) {
|
||||
defer wg.Done()
|
||||
if err := renderEntry(name, e); err != nil {
|
||||
log.Printf("%s: %s\n", name, err)
|
||||
}
|
||||
}(name, e)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
for name, e := range *entries {
|
||||
// Expand LinkWords anywhere between <tags>.
|
||||
// We want something like the inverse of Regexp.ReplaceAllStringFunc.
|
||||
raw, last, expanded := e.raw, 0, bytes.NewBuffer(nil)
|
||||
for _, where := range tagRE.FindAllIndex(raw, -1) {
|
||||
_, _ = expanded.Write(expand(entries, name, raw[last:where[0]]))
|
||||
_, _ = expanded.Write(raw[where[0]:where[1]])
|
||||
last = where[1]
|
||||
}
|
||||
_, _ = expanded.Write(expand(entries, name, raw[last:]))
|
||||
e.Content = template.HTML(expanded.String())
|
||||
}
|
||||
for name, e := range *entries {
|
||||
// Don't overwrite failed renders.
|
||||
if e.raw == nil {
|
||||
continue
|
||||
}
|
||||
if err := writeEntry(e, t, entries); err != nil {
|
||||
log.Printf("%s: %s\n", name, err)
|
||||
}
|
||||
}
|
||||
if err := writeIndex(indexPath, indexT, entries); err != nil {
|
||||
log.Printf("%s: %s\n", indexPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
type watchEvent struct {
|
||||
path string // the path of the target
|
||||
present bool // if not, the file has been removed
|
||||
}
|
||||
|
||||
func dispatchEvents(dirname string, r io.Reader, ch chan<- *watchEvent) error {
|
||||
var e syscall.InotifyEvent
|
||||
for {
|
||||
// FIXME(p): This has to respect the machine's endianness.
|
||||
// Perhaps use the unsafe package.
|
||||
err := binary.Read(r, binary.LittleEndian, &e)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
base := make([]byte, e.Len)
|
||||
if e.Len != 0 {
|
||||
if n, err := r.Read(base); err != nil {
|
||||
return err
|
||||
} else if n < int(e.Len) {
|
||||
return fmt.Errorf("short read")
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case e.Mask&syscall.IN_IGNORED != 0:
|
||||
return fmt.Errorf("watch removed by kernel")
|
||||
case e.Mask&syscall.IN_Q_OVERFLOW != 0:
|
||||
log.Println("inotify: queue overflowed")
|
||||
ch <- nil
|
||||
continue
|
||||
case e.Len == 0:
|
||||
continue
|
||||
}
|
||||
|
||||
basename, interesting := string(base[:bytes.IndexByte(base, 0)]), false
|
||||
for _, glob := range globs {
|
||||
if matches, _ := filepath.Match(glob, basename); matches {
|
||||
interesting = true
|
||||
}
|
||||
}
|
||||
if !interesting {
|
||||
continue
|
||||
}
|
||||
|
||||
event := &watchEvent{path: filepath.Join(dirname, basename)}
|
||||
if e.Mask&syscall.IN_MODIFY != 0 || e.Mask&syscall.IN_MOVED_TO != 0 ||
|
||||
e.Mask&syscall.IN_CLOSE_WRITE != 0 {
|
||||
event.present = true
|
||||
ch <- event
|
||||
}
|
||||
if e.Mask&syscall.IN_DELETE != 0 || e.Mask&syscall.IN_MOVED_FROM != 0 {
|
||||
event.present = false
|
||||
ch <- event
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func watchDirectory(dirname string) (<-chan *watchEvent, error) {
|
||||
inotifyFD, err := syscall.InotifyInit1(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We're ignoring IN_CREATE, as it doesn't seem to be useful,
|
||||
// and we're leaving out IN_MODIFY since VIM always triggers IN_CLOSE_WRITE,
|
||||
// saving us from having to coalesce plentiful similar events.
|
||||
_, err = syscall.InotifyAddWatch(inotifyFD, dirname, syscall.IN_ONLYDIR|
|
||||
syscall.IN_MOVE|syscall.IN_DELETE|syscall.IN_CLOSE_WRITE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inotifyFile := os.NewFile(uintptr(inotifyFD), "inotify")
|
||||
buf := make([]byte, syscall.SizeofInotifyEvent+syscall.PathMax+1)
|
||||
ch := make(chan *watchEvent)
|
||||
go func() {
|
||||
// Trigger an initial rendering run.
|
||||
ch <- nil
|
||||
|
||||
defer close(ch)
|
||||
for {
|
||||
n, err := inotifyFile.Read(buf)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = dispatchEvents(dirname, bytes.NewReader(buf[:n]), ch)
|
||||
if err != nil {
|
||||
log.Printf("inotify: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
var funcs = template.FuncMap{
|
||||
"contains": func(needle string, haystack []string) bool {
|
||||
for _, el := range haystack {
|
||||
if el == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
func singleFile() {
|
||||
html, meta, err := Render(os.Stdin, configuration.NewConfiguration())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else if meta.Title != "" {
|
||||
_, _ = os.Stdout.WriteString("<h1>")
|
||||
_ = xml.EscapeText(os.Stdout, []byte(meta.Title))
|
||||
_, _ = os.Stdout.WriteString("</h1>\n")
|
||||
}
|
||||
_, _ = io.Copy(os.Stdout, html)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
singleFile()
|
||||
return
|
||||
}
|
||||
if len(os.Args) != 4 {
|
||||
log.Fatalf("usage: %s TEMPLATE INDEX DIRECTORY\n", os.Args[0])
|
||||
}
|
||||
|
||||
argTemplate, argIndex, argDirectory := os.Args[1], os.Args[2], os.Args[3]
|
||||
|
||||
// Read a template for entries.
|
||||
header, err := ioutil.ReadFile(argTemplate)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
tmplEntry, err := template.New("entry").Funcs(funcs).Parse(string(header))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Read a template for the index from the standard input.
|
||||
index, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
tmplIndex, err := template.New("index").Funcs(funcs).Parse(string(index))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Re-render as needed, avoid having to trigger anything manually.
|
||||
var entries map[string]*Entry
|
||||
directoryWatch, err := watchDirectory(argDirectory)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGHUP, syscall.SIGTERM)
|
||||
for {
|
||||
select {
|
||||
case <-signals:
|
||||
os.Exit(0)
|
||||
case event, ok := <-directoryWatch:
|
||||
if !ok {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if event == nil {
|
||||
log.Println("reloading all files")
|
||||
if entries, err = loadEntries(argDirectory); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
} else if event.present {
|
||||
log.Printf("updating %s\n", event.path)
|
||||
entries[pathToName(event.path)] = makeEntry(event.path)
|
||||
} else {
|
||||
log.Printf("removing %s\n", event.path)
|
||||
delete(entries, pathToName(event.path))
|
||||
os.Remove(resultPath(event.path))
|
||||
}
|
||||
|
||||
finalizeEntries(&entries, tmplEntry, argIndex, tmplIndex)
|
||||
log.Println("done")
|
||||
}
|
||||
}
|
||||
}
|
||||
49
ht/gen-keysyms.sh
Executable file
49
ht/gen-keysyms.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
gofmt <<EOF | sed 's, *//$,,'
|
||||
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
|
||||
|
||||
package $GOPACKAGE
|
||||
|
||||
import "janouch.name/haven/nexgb/xproto"
|
||||
|
||||
$(curl --silent --show-error \
|
||||
https://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h \
|
||||
https://cgit.freedesktop.org/xorg/proto/x11proto/plain/XF86keysym.h \
|
||||
| perl -lne '
|
||||
next unless /^\#define\s+
|
||||
(XF86)?(XK_)([a-zA-Z_0-9]+)\s+
|
||||
0x([0-9a-fA-F]+)\s*
|
||||
(?:\/\*\s*(.*?)\s*\*\/)?\s*
|
||||
$/x;
|
||||
|
||||
my ($name, $ident, $hex, $comment) =
|
||||
(($1 // "") . $3, ($1 // "") . $2 . $3, lc $4, ($5 // ""));
|
||||
|
||||
# They are already somewhat sorted in the source file.
|
||||
push @a, { hex => $hex, ident => $ident, comment => $comment };
|
||||
|
||||
$nametokeysym{$name} = $ident;
|
||||
|
||||
# All but the first name listed should be considered deprecated.
|
||||
$keysymtoname{$ident} = $name unless exists $seen{$hex};
|
||||
$seen{$hex}++;
|
||||
|
||||
END {
|
||||
print "const (";
|
||||
print "$_->{ident} = 0x$_->{hex} // $_->{comment}" for @a;
|
||||
print ")";
|
||||
|
||||
# Very large tables, should be an on-demand package :(
|
||||
|
||||
print "// KeysymToName maps X11 keysym constants to their names";
|
||||
print "var KeysymToName = map[xproto.Keysym]string{";
|
||||
print "$_: \"$keysymtoname{$_}\"," for sort keys %keysymtoname;
|
||||
print "}";
|
||||
|
||||
print "// NameToKeysym maps X11 keysym names to their constants";
|
||||
print "var NameToKeysym = map[string]xproto.Keysym{";
|
||||
print "\"$_\": $nametokeysym{$_}," for sort keys %nametokeysym;
|
||||
print "}";
|
||||
}
|
||||
')
|
||||
EOF
|
||||
84
ht/gen-rune-width.sh
Executable file
84
ht/gen-rune-width.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/bin/sh
|
||||
u=https://www.unicode.org/Public/
|
||||
|
||||
# Download and filter Unicode data files with the given category expression,
|
||||
# producing a list of possibly duplicitous codepoints in decimal format
|
||||
retrieve() {
|
||||
curl --silent --show-error --location "$2" | perl -lne 's/#.*//; s/ //g;
|
||||
next unless /^([0-9A-F]+)(?:\.\.([0-9A-F]+))?;('"$1"')$/;
|
||||
print for hex $1 .. hex ($2 // $1);'
|
||||
}
|
||||
|
||||
togo() {
|
||||
sort -nu | perl -lne '
|
||||
sub flush { printf "{0x%04x, 0x%04x},\n", $first, $last }
|
||||
BEGIN { $first = $last = <> }
|
||||
if ($_ != $last + 1) { flush; $first = $_; }
|
||||
$last = $_;
|
||||
END { flush if defined $first }' | column -xc 72
|
||||
}
|
||||
|
||||
gofmt <<EOF
|
||||
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
|
||||
|
||||
package $GOPACKAGE
|
||||
|
||||
// RuneWidth returns the column width of Go runes, using an algorithm and tables
|
||||
// derived from https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c:
|
||||
// - The null character (U+0000) has a column width of 0.
|
||||
// - Other C0/C1 control characters and DEL will lead to a return value of -1.
|
||||
// - Non-spacing and enclosing combining characters (general category code
|
||||
// Mn or Me in the Unicode database) have a column width of 0.
|
||||
// - SOFT HYPHEN (U+00AD) has a column width of 1.
|
||||
// - Other format characters (general category code Cf in the Unicode database)
|
||||
// and ZERO WIDTH SPACE (U+200B) have a column width of 0.
|
||||
// - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) have
|
||||
// a column width of 0.
|
||||
// - Spacing characters in the East Asian Wide (W) or East Asian Full-width (F)
|
||||
// category as defined in Unicode UAX #11 have a column width of 2.
|
||||
// - All remaining characters (including all printable ISO 8859-1 and WGL4
|
||||
// characters, Unicode control characters, etc.) have a column width of 1.
|
||||
//
|
||||
// Local changes:
|
||||
// - Tables are generated from the latest available version of Unicode.
|
||||
func RuneWidth(r rune) int {
|
||||
switch {
|
||||
case r == 0:
|
||||
return 0
|
||||
case r < 32 || r >= 0x7f && r < 0xa0:
|
||||
return -1
|
||||
case zeroWidthRunes.contains(r):
|
||||
return 0
|
||||
case fullWidthRunes.contains(r):
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
type runeRange struct{ first, last rune }
|
||||
type runeRangeTable []runeRange
|
||||
|
||||
func (table runeRangeTable) contains(r rune) bool {
|
||||
min, max := 0, len(table)-1
|
||||
for max >= min {
|
||||
mid := (min + max) / 2
|
||||
if table[mid].last < r {
|
||||
min = mid + 1
|
||||
} else if table[mid].first > r {
|
||||
max = mid - 1
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var zeroWidthRunes = runeRangeTable{
|
||||
$({ retrieve 'Me|Mn|Cf' $u/UCD/latest/ucd/extracted/DerivedGeneralCategory.txt;
|
||||
seq 0x1160 0x11ff; echo $((0x200B)); } | grep -xv $((0x00AD)) | togo)
|
||||
}
|
||||
|
||||
var fullWidthRunes = runeRangeTable{
|
||||
$(retrieve 'W|F' $u/UCD/latest/ucd/EastAsianWidth.txt | togo)
|
||||
}
|
||||
EOF
|
||||
49
ht/gen-unicode-map.sh
Executable file
49
ht/gen-unicode-map.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
gofmt <<EOF
|
||||
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
|
||||
|
||||
package $GOPACKAGE
|
||||
|
||||
import "janouch.name/haven/nexgb/xproto"
|
||||
|
||||
// KeysymToRune tries to translate an X11 keysym to an appropriate rune.
|
||||
// Returns -1 when no match is found.
|
||||
func KeysymToRune(ks xproto.Keysym) rune {
|
||||
// Visible Latin-1 is mapped 1:1
|
||||
if (ks >= 0x20 && ks <= 0x7e) || (ks >= 0xa0 && ks <= 0xff) {
|
||||
return rune(ks)
|
||||
}
|
||||
// Directly encoded 24-bit Unicode (going even above plane 16)
|
||||
if (ks & 0xff000000) == 0x01000000 {
|
||||
return rune(ks & 0x00ffffff)
|
||||
}
|
||||
|
||||
min, max := 0, len(keysymToRuneTable)-1
|
||||
for max >= min {
|
||||
mid := (min + max) / 2
|
||||
if keysymToRuneTable[mid].keysym < ks {
|
||||
min = mid + 1
|
||||
} else if keysymToRuneTable[mid].keysym > ks {
|
||||
max = mid - 1
|
||||
} else {
|
||||
return keysymToRuneTable[mid].unicode
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
var keysymToRuneTable = []struct{
|
||||
keysym xproto.Keysym
|
||||
unicode rune
|
||||
}{
|
||||
$(curl --silent --show-error --location \
|
||||
https://invisible-island.net/datafiles/release/xterm.tar.gz | \
|
||||
tar --wildcards -xzOf - 'xterm-*/unicode/keysym.map' | \
|
||||
sort | perl -lne '
|
||||
next unless /^(0x([0-9a-f]{4}))\s+U([0-9a-f]{4})\s*(?:\#\s*(.*))?$/;
|
||||
my ($keysym, $ks, $unicode, $comment) = ($1, hex($2), $3, ($4 // ""));
|
||||
print "{$keysym, 0x$unicode}, // $comment"
|
||||
if $ks >= 0x100 && $unicode ne "0000";
|
||||
')
|
||||
}
|
||||
EOF
|
||||
6761
ht/keysyms.go
Normal file
6761
ht/keysyms.go
Normal file
File diff suppressed because it is too large
Load Diff
204
ht/rune_width.go
Normal file
204
ht/rune_width.go
Normal file
@@ -0,0 +1,204 @@
|
||||
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
|
||||
|
||||
package main
|
||||
|
||||
// RuneWidth returns the column width of Go runes, using an algorithm and tables
|
||||
// derived from https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c:
|
||||
// - The null character (U+0000) has a column width of 0.
|
||||
// - Other C0/C1 control characters and DEL will lead to a return value of -1.
|
||||
// - Non-spacing and enclosing combining characters (general category code
|
||||
// Mn or Me in the Unicode database) have a column width of 0.
|
||||
// - SOFT HYPHEN (U+00AD) has a column width of 1.
|
||||
// - Other format characters (general category code Cf in the Unicode database)
|
||||
// and ZERO WIDTH SPACE (U+200B) have a column width of 0.
|
||||
// - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) have
|
||||
// a column width of 0.
|
||||
// - Spacing characters in the East Asian Wide (W) or East Asian Full-width (F)
|
||||
// category as defined in Unicode UAX #11 have a column width of 2.
|
||||
// - All remaining characters (including all printable ISO 8859-1 and WGL4
|
||||
// characters, Unicode control characters, etc.) have a column width of 1.
|
||||
//
|
||||
// Local changes:
|
||||
// - Tables are generated from the latest available version of Unicode.
|
||||
func RuneWidth(r rune) int {
|
||||
switch {
|
||||
case r == 0:
|
||||
return 0
|
||||
case r < 32 || r >= 0x7f && r < 0xa0:
|
||||
return -1
|
||||
case zeroWidthRunes.contains(r):
|
||||
return 0
|
||||
case fullWidthRunes.contains(r):
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
type runeRange struct{ first, last rune }
|
||||
type runeRangeTable []runeRange
|
||||
|
||||
func (table runeRangeTable) contains(r rune) bool {
|
||||
min, max := 0, len(table)-1
|
||||
for max >= min {
|
||||
mid := (min + max) / 2
|
||||
if table[mid].last < r {
|
||||
min = mid + 1
|
||||
} else if table[mid].first > r {
|
||||
max = mid - 1
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var zeroWidthRunes = runeRangeTable{
|
||||
{0x0300, 0x036f}, {0x0483, 0x0489}, {0x0591, 0x05bd},
|
||||
{0x05bf, 0x05bf}, {0x05c1, 0x05c2}, {0x05c4, 0x05c5},
|
||||
{0x05c7, 0x05c7}, {0x0600, 0x0605}, {0x0610, 0x061a},
|
||||
{0x061c, 0x061c}, {0x064b, 0x065f}, {0x0670, 0x0670},
|
||||
{0x06d6, 0x06dd}, {0x06df, 0x06e4}, {0x06e7, 0x06e8},
|
||||
{0x06ea, 0x06ed}, {0x070f, 0x070f}, {0x0711, 0x0711},
|
||||
{0x0730, 0x074a}, {0x07a6, 0x07b0}, {0x07eb, 0x07f3},
|
||||
{0x07fd, 0x07fd}, {0x0816, 0x0819}, {0x081b, 0x0823},
|
||||
{0x0825, 0x0827}, {0x0829, 0x082d}, {0x0859, 0x085b},
|
||||
{0x08d3, 0x0902}, {0x093a, 0x093a}, {0x093c, 0x093c},
|
||||
{0x0941, 0x0948}, {0x094d, 0x094d}, {0x0951, 0x0957},
|
||||
{0x0962, 0x0963}, {0x0981, 0x0981}, {0x09bc, 0x09bc},
|
||||
{0x09c1, 0x09c4}, {0x09cd, 0x09cd}, {0x09e2, 0x09e3},
|
||||
{0x09fe, 0x09fe}, {0x0a01, 0x0a02}, {0x0a3c, 0x0a3c},
|
||||
{0x0a41, 0x0a42}, {0x0a47, 0x0a48}, {0x0a4b, 0x0a4d},
|
||||
{0x0a51, 0x0a51}, {0x0a70, 0x0a71}, {0x0a75, 0x0a75},
|
||||
{0x0a81, 0x0a82}, {0x0abc, 0x0abc}, {0x0ac1, 0x0ac5},
|
||||
{0x0ac7, 0x0ac8}, {0x0acd, 0x0acd}, {0x0ae2, 0x0ae3},
|
||||
{0x0afa, 0x0aff}, {0x0b01, 0x0b01}, {0x0b3c, 0x0b3c},
|
||||
{0x0b3f, 0x0b3f}, {0x0b41, 0x0b44}, {0x0b4d, 0x0b4d},
|
||||
{0x0b56, 0x0b56}, {0x0b62, 0x0b63}, {0x0b82, 0x0b82},
|
||||
{0x0bc0, 0x0bc0}, {0x0bcd, 0x0bcd}, {0x0c00, 0x0c00},
|
||||
{0x0c04, 0x0c04}, {0x0c3e, 0x0c40}, {0x0c46, 0x0c48},
|
||||
{0x0c4a, 0x0c4d}, {0x0c55, 0x0c56}, {0x0c62, 0x0c63},
|
||||
{0x0c81, 0x0c81}, {0x0cbc, 0x0cbc}, {0x0cbf, 0x0cbf},
|
||||
{0x0cc6, 0x0cc6}, {0x0ccc, 0x0ccd}, {0x0ce2, 0x0ce3},
|
||||
{0x0d00, 0x0d01}, {0x0d3b, 0x0d3c}, {0x0d41, 0x0d44},
|
||||
{0x0d4d, 0x0d4d}, {0x0d62, 0x0d63}, {0x0dca, 0x0dca},
|
||||
{0x0dd2, 0x0dd4}, {0x0dd6, 0x0dd6}, {0x0e31, 0x0e31},
|
||||
{0x0e34, 0x0e3a}, {0x0e47, 0x0e4e}, {0x0eb1, 0x0eb1},
|
||||
{0x0eb4, 0x0eb9}, {0x0ebb, 0x0ebc}, {0x0ec8, 0x0ecd},
|
||||
{0x0f18, 0x0f19}, {0x0f35, 0x0f35}, {0x0f37, 0x0f37},
|
||||
{0x0f39, 0x0f39}, {0x0f71, 0x0f7e}, {0x0f80, 0x0f84},
|
||||
{0x0f86, 0x0f87}, {0x0f8d, 0x0f97}, {0x0f99, 0x0fbc},
|
||||
{0x0fc6, 0x0fc6}, {0x102d, 0x1030}, {0x1032, 0x1037},
|
||||
{0x1039, 0x103a}, {0x103d, 0x103e}, {0x1058, 0x1059},
|
||||
{0x105e, 0x1060}, {0x1071, 0x1074}, {0x1082, 0x1082},
|
||||
{0x1085, 0x1086}, {0x108d, 0x108d}, {0x109d, 0x109d},
|
||||
{0x1160, 0x11ff}, {0x135d, 0x135f}, {0x1712, 0x1714},
|
||||
{0x1732, 0x1734}, {0x1752, 0x1753}, {0x1772, 0x1773},
|
||||
{0x17b4, 0x17b5}, {0x17b7, 0x17bd}, {0x17c6, 0x17c6},
|
||||
{0x17c9, 0x17d3}, {0x17dd, 0x17dd}, {0x180b, 0x180e},
|
||||
{0x1885, 0x1886}, {0x18a9, 0x18a9}, {0x1920, 0x1922},
|
||||
{0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193b},
|
||||
{0x1a17, 0x1a18}, {0x1a1b, 0x1a1b}, {0x1a56, 0x1a56},
|
||||
{0x1a58, 0x1a5e}, {0x1a60, 0x1a60}, {0x1a62, 0x1a62},
|
||||
{0x1a65, 0x1a6c}, {0x1a73, 0x1a7c}, {0x1a7f, 0x1a7f},
|
||||
{0x1ab0, 0x1abe}, {0x1b00, 0x1b03}, {0x1b34, 0x1b34},
|
||||
{0x1b36, 0x1b3a}, {0x1b3c, 0x1b3c}, {0x1b42, 0x1b42},
|
||||
{0x1b6b, 0x1b73}, {0x1b80, 0x1b81}, {0x1ba2, 0x1ba5},
|
||||
{0x1ba8, 0x1ba9}, {0x1bab, 0x1bad}, {0x1be6, 0x1be6},
|
||||
{0x1be8, 0x1be9}, {0x1bed, 0x1bed}, {0x1bef, 0x1bf1},
|
||||
{0x1c2c, 0x1c33}, {0x1c36, 0x1c37}, {0x1cd0, 0x1cd2},
|
||||
{0x1cd4, 0x1ce0}, {0x1ce2, 0x1ce8}, {0x1ced, 0x1ced},
|
||||
{0x1cf4, 0x1cf4}, {0x1cf8, 0x1cf9}, {0x1dc0, 0x1df9},
|
||||
{0x1dfb, 0x1dff}, {0x200b, 0x200f}, {0x202a, 0x202e},
|
||||
{0x2060, 0x2064}, {0x2066, 0x206f}, {0x20d0, 0x20f0},
|
||||
{0x2cef, 0x2cf1}, {0x2d7f, 0x2d7f}, {0x2de0, 0x2dff},
|
||||
{0x302a, 0x302d}, {0x3099, 0x309a}, {0xa66f, 0xa672},
|
||||
{0xa674, 0xa67d}, {0xa69e, 0xa69f}, {0xa6f0, 0xa6f1},
|
||||
{0xa802, 0xa802}, {0xa806, 0xa806}, {0xa80b, 0xa80b},
|
||||
{0xa825, 0xa826}, {0xa8c4, 0xa8c5}, {0xa8e0, 0xa8f1},
|
||||
{0xa8ff, 0xa8ff}, {0xa926, 0xa92d}, {0xa947, 0xa951},
|
||||
{0xa980, 0xa982}, {0xa9b3, 0xa9b3}, {0xa9b6, 0xa9b9},
|
||||
{0xa9bc, 0xa9bc}, {0xa9e5, 0xa9e5}, {0xaa29, 0xaa2e},
|
||||
{0xaa31, 0xaa32}, {0xaa35, 0xaa36}, {0xaa43, 0xaa43},
|
||||
{0xaa4c, 0xaa4c}, {0xaa7c, 0xaa7c}, {0xaab0, 0xaab0},
|
||||
{0xaab2, 0xaab4}, {0xaab7, 0xaab8}, {0xaabe, 0xaabf},
|
||||
{0xaac1, 0xaac1}, {0xaaec, 0xaaed}, {0xaaf6, 0xaaf6},
|
||||
{0xabe5, 0xabe5}, {0xabe8, 0xabe8}, {0xabed, 0xabed},
|
||||
{0xfb1e, 0xfb1e}, {0xfe00, 0xfe0f}, {0xfe20, 0xfe2f},
|
||||
{0xfeff, 0xfeff}, {0xfff9, 0xfffb}, {0x101fd, 0x101fd},
|
||||
{0x102e0, 0x102e0}, {0x10376, 0x1037a}, {0x10a01, 0x10a03},
|
||||
{0x10a05, 0x10a06}, {0x10a0c, 0x10a0f}, {0x10a38, 0x10a3a},
|
||||
{0x10a3f, 0x10a3f}, {0x10ae5, 0x10ae6}, {0x10d24, 0x10d27},
|
||||
{0x10f46, 0x10f50}, {0x11001, 0x11001}, {0x11038, 0x11046},
|
||||
{0x1107f, 0x11081}, {0x110b3, 0x110b6}, {0x110b9, 0x110ba},
|
||||
{0x110bd, 0x110bd}, {0x110cd, 0x110cd}, {0x11100, 0x11102},
|
||||
{0x11127, 0x1112b}, {0x1112d, 0x11134}, {0x11173, 0x11173},
|
||||
{0x11180, 0x11181}, {0x111b6, 0x111be}, {0x111c9, 0x111cc},
|
||||
{0x1122f, 0x11231}, {0x11234, 0x11234}, {0x11236, 0x11237},
|
||||
{0x1123e, 0x1123e}, {0x112df, 0x112df}, {0x112e3, 0x112ea},
|
||||
{0x11300, 0x11301}, {0x1133b, 0x1133c}, {0x11340, 0x11340},
|
||||
{0x11366, 0x1136c}, {0x11370, 0x11374}, {0x11438, 0x1143f},
|
||||
{0x11442, 0x11444}, {0x11446, 0x11446}, {0x1145e, 0x1145e},
|
||||
{0x114b3, 0x114b8}, {0x114ba, 0x114ba}, {0x114bf, 0x114c0},
|
||||
{0x114c2, 0x114c3}, {0x115b2, 0x115b5}, {0x115bc, 0x115bd},
|
||||
{0x115bf, 0x115c0}, {0x115dc, 0x115dd}, {0x11633, 0x1163a},
|
||||
{0x1163d, 0x1163d}, {0x1163f, 0x11640}, {0x116ab, 0x116ab},
|
||||
{0x116ad, 0x116ad}, {0x116b0, 0x116b5}, {0x116b7, 0x116b7},
|
||||
{0x1171d, 0x1171f}, {0x11722, 0x11725}, {0x11727, 0x1172b},
|
||||
{0x1182f, 0x11837}, {0x11839, 0x1183a}, {0x11a01, 0x11a0a},
|
||||
{0x11a33, 0x11a38}, {0x11a3b, 0x11a3e}, {0x11a47, 0x11a47},
|
||||
{0x11a51, 0x11a56}, {0x11a59, 0x11a5b}, {0x11a8a, 0x11a96},
|
||||
{0x11a98, 0x11a99}, {0x11c30, 0x11c36}, {0x11c38, 0x11c3d},
|
||||
{0x11c3f, 0x11c3f}, {0x11c92, 0x11ca7}, {0x11caa, 0x11cb0},
|
||||
{0x11cb2, 0x11cb3}, {0x11cb5, 0x11cb6}, {0x11d31, 0x11d36},
|
||||
{0x11d3a, 0x11d3a}, {0x11d3c, 0x11d3d}, {0x11d3f, 0x11d45},
|
||||
{0x11d47, 0x11d47}, {0x11d90, 0x11d91}, {0x11d95, 0x11d95},
|
||||
{0x11d97, 0x11d97}, {0x11ef3, 0x11ef4}, {0x16af0, 0x16af4},
|
||||
{0x16b30, 0x16b36}, {0x16f8f, 0x16f92}, {0x1bc9d, 0x1bc9e},
|
||||
{0x1bca0, 0x1bca3}, {0x1d167, 0x1d169}, {0x1d173, 0x1d182},
|
||||
{0x1d185, 0x1d18b}, {0x1d1aa, 0x1d1ad}, {0x1d242, 0x1d244},
|
||||
{0x1da00, 0x1da36}, {0x1da3b, 0x1da6c}, {0x1da75, 0x1da75},
|
||||
{0x1da84, 0x1da84}, {0x1da9b, 0x1da9f}, {0x1daa1, 0x1daaf},
|
||||
{0x1e000, 0x1e006}, {0x1e008, 0x1e018}, {0x1e01b, 0x1e021},
|
||||
{0x1e023, 0x1e024}, {0x1e026, 0x1e02a}, {0x1e8d0, 0x1e8d6},
|
||||
{0x1e944, 0x1e94a}, {0xe0001, 0xe0001}, {0xe0020, 0xe007f},
|
||||
{0xe0100, 0xe01ef},
|
||||
}
|
||||
|
||||
var fullWidthRunes = runeRangeTable{
|
||||
{0x1100, 0x115f}, {0x231a, 0x231b}, {0x2329, 0x232a},
|
||||
{0x23e9, 0x23ec}, {0x23f0, 0x23f0}, {0x23f3, 0x23f3},
|
||||
{0x25fd, 0x25fe}, {0x2614, 0x2615}, {0x2648, 0x2653},
|
||||
{0x267f, 0x267f}, {0x2693, 0x2693}, {0x26a1, 0x26a1},
|
||||
{0x26aa, 0x26ab}, {0x26bd, 0x26be}, {0x26c4, 0x26c5},
|
||||
{0x26ce, 0x26ce}, {0x26d4, 0x26d4}, {0x26ea, 0x26ea},
|
||||
{0x26f2, 0x26f3}, {0x26f5, 0x26f5}, {0x26fa, 0x26fa},
|
||||
{0x26fd, 0x26fd}, {0x2705, 0x2705}, {0x270a, 0x270b},
|
||||
{0x2728, 0x2728}, {0x274c, 0x274c}, {0x274e, 0x274e},
|
||||
{0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797},
|
||||
{0x27b0, 0x27b0}, {0x27bf, 0x27bf}, {0x2b1b, 0x2b1c},
|
||||
{0x2b50, 0x2b50}, {0x2b55, 0x2b55}, {0x2e80, 0x2e99},
|
||||
{0x2e9b, 0x2ef3}, {0x2f00, 0x2fd5}, {0x2ff0, 0x2ffb},
|
||||
{0x3000, 0x303e}, {0x3041, 0x3096}, {0x3099, 0x30ff},
|
||||
{0x3105, 0x312f}, {0x3131, 0x318e}, {0x3190, 0x31ba},
|
||||
{0x31c0, 0x31e3}, {0x31f0, 0x321e}, {0x3220, 0x3247},
|
||||
{0x3250, 0x32fe}, {0x3300, 0x4dbf}, {0x4e00, 0xa48c},
|
||||
{0xa490, 0xa4c6}, {0xa960, 0xa97c}, {0xac00, 0xd7a3},
|
||||
{0xf900, 0xfaff}, {0xfe10, 0xfe19}, {0xfe30, 0xfe52},
|
||||
{0xfe54, 0xfe66}, {0xfe68, 0xfe6b}, {0xff01, 0xff60},
|
||||
{0xffe0, 0xffe6}, {0x16fe0, 0x16fe1}, {0x17000, 0x187f1},
|
||||
{0x18800, 0x18af2}, {0x1b000, 0x1b11e}, {0x1b170, 0x1b2fb},
|
||||
{0x1f004, 0x1f004}, {0x1f0cf, 0x1f0cf}, {0x1f18e, 0x1f18e},
|
||||
{0x1f191, 0x1f19a}, {0x1f200, 0x1f202}, {0x1f210, 0x1f23b},
|
||||
{0x1f240, 0x1f248}, {0x1f250, 0x1f251}, {0x1f260, 0x1f265},
|
||||
{0x1f300, 0x1f320}, {0x1f32d, 0x1f335}, {0x1f337, 0x1f37c},
|
||||
{0x1f37e, 0x1f393}, {0x1f3a0, 0x1f3ca}, {0x1f3cf, 0x1f3d3},
|
||||
{0x1f3e0, 0x1f3f0}, {0x1f3f4, 0x1f3f4}, {0x1f3f8, 0x1f43e},
|
||||
{0x1f440, 0x1f440}, {0x1f442, 0x1f4fc}, {0x1f4ff, 0x1f53d},
|
||||
{0x1f54b, 0x1f54e}, {0x1f550, 0x1f567}, {0x1f57a, 0x1f57a},
|
||||
{0x1f595, 0x1f596}, {0x1f5a4, 0x1f5a4}, {0x1f5fb, 0x1f64f},
|
||||
{0x1f680, 0x1f6c5}, {0x1f6cc, 0x1f6cc}, {0x1f6d0, 0x1f6d2},
|
||||
{0x1f6eb, 0x1f6ec}, {0x1f6f4, 0x1f6f9}, {0x1f910, 0x1f93e},
|
||||
{0x1f940, 0x1f970}, {0x1f973, 0x1f976}, {0x1f97a, 0x1f97a},
|
||||
{0x1f97c, 0x1f9a2}, {0x1f9b0, 0x1f9b9}, {0x1f9c0, 0x1f9c2},
|
||||
{0x1f9d0, 0x1f9ff}, {0x20000, 0x2fffd}, {0x30000, 0x3fffd},
|
||||
}
|
||||
859
ht/unicode_map.go
Normal file
859
ht/unicode_map.go
Normal file
@@ -0,0 +1,859 @@
|
||||
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
|
||||
|
||||
package main
|
||||
|
||||
import "janouch.name/haven/nexgb/xproto"
|
||||
|
||||
// KeysymToRune tries to translate an X11 keysym to an appropriate rune.
|
||||
// Returns -1 when no match is found.
|
||||
func KeysymToRune(ks xproto.Keysym) rune {
|
||||
// Visible Latin-1 is mapped 1:1
|
||||
if (ks >= 0x20 && ks <= 0x7e) || (ks >= 0xa0 && ks <= 0xff) {
|
||||
return rune(ks)
|
||||
}
|
||||
// Directly encoded 24-bit Unicode (going even above plane 16)
|
||||
if (ks & 0xff000000) == 0x01000000 {
|
||||
return rune(ks & 0x00ffffff)
|
||||
}
|
||||
|
||||
min, max := 0, len(keysymToRuneTable)-1
|
||||
for max >= min {
|
||||
mid := (min + max) / 2
|
||||
if keysymToRuneTable[mid].keysym < ks {
|
||||
min = mid + 1
|
||||
} else if keysymToRuneTable[mid].keysym > ks {
|
||||
max = mid - 1
|
||||
} else {
|
||||
return keysymToRuneTable[mid].unicode
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
var keysymToRuneTable = []struct {
|
||||
keysym xproto.Keysym
|
||||
unicode rune
|
||||
}{
|
||||
{0x01a1, 0x0104}, // Aogonek
|
||||
{0x01a2, 0x02d8}, // breve
|
||||
{0x01a3, 0x0141}, // Lstroke
|
||||
{0x01a5, 0x013d}, // Lcaron
|
||||
{0x01a6, 0x015a}, // Sacute
|
||||
{0x01a9, 0x0160}, // Scaron
|
||||
{0x01aa, 0x015e}, // Scedilla
|
||||
{0x01ab, 0x0164}, // Tcaron
|
||||
{0x01ac, 0x0179}, // Zacute
|
||||
{0x01ae, 0x017d}, // Zcaron
|
||||
{0x01af, 0x017b}, // Zabovedot
|
||||
{0x01b1, 0x0105}, // aogonek
|
||||
{0x01b2, 0x02db}, // ogonek
|
||||
{0x01b3, 0x0142}, // lstroke
|
||||
{0x01b5, 0x013e}, // lcaron
|
||||
{0x01b6, 0x015b}, // sacute
|
||||
{0x01b7, 0x02c7}, // caron
|
||||
{0x01b9, 0x0161}, // scaron
|
||||
{0x01ba, 0x015f}, // scedilla
|
||||
{0x01bb, 0x0165}, // tcaron
|
||||
{0x01bc, 0x017a}, // zacute
|
||||
{0x01bd, 0x02dd}, // doubleacute
|
||||
{0x01be, 0x017e}, // zcaron
|
||||
{0x01bf, 0x017c}, // zabovedot
|
||||
{0x01c0, 0x0154}, // Racute
|
||||
{0x01c3, 0x0102}, // Abreve
|
||||
{0x01c5, 0x0139}, // Lacute
|
||||
{0x01c6, 0x0106}, // Cacute
|
||||
{0x01c8, 0x010c}, // Ccaron
|
||||
{0x01ca, 0x0118}, // Eogonek
|
||||
{0x01cc, 0x011a}, // Ecaron
|
||||
{0x01cf, 0x010e}, // Dcaron
|
||||
{0x01d0, 0x0110}, // Dstroke
|
||||
{0x01d1, 0x0143}, // Nacute
|
||||
{0x01d2, 0x0147}, // Ncaron
|
||||
{0x01d5, 0x0150}, // Odoubleacute
|
||||
{0x01d8, 0x0158}, // Rcaron
|
||||
{0x01d9, 0x016e}, // Uring
|
||||
{0x01db, 0x0170}, // Udoubleacute
|
||||
{0x01de, 0x0162}, // Tcedilla
|
||||
{0x01e0, 0x0155}, // racute
|
||||
{0x01e3, 0x0103}, // abreve
|
||||
{0x01e5, 0x013a}, // lacute
|
||||
{0x01e6, 0x0107}, // cacute
|
||||
{0x01e8, 0x010d}, // ccaron
|
||||
{0x01ea, 0x0119}, // eogonek
|
||||
{0x01ec, 0x011b}, // ecaron
|
||||
{0x01ef, 0x010f}, // dcaron
|
||||
{0x01f0, 0x0111}, // dstroke
|
||||
{0x01f1, 0x0144}, // nacute
|
||||
{0x01f2, 0x0148}, // ncaron
|
||||
{0x01f5, 0x0151}, // odoubleacute
|
||||
{0x01f8, 0x0159}, // rcaron
|
||||
{0x01f9, 0x016f}, // uring
|
||||
{0x01fb, 0x0171}, // udoubleacute
|
||||
{0x01fe, 0x0163}, // tcedilla
|
||||
{0x01ff, 0x02d9}, // abovedot
|
||||
{0x02a1, 0x0126}, // Hstroke
|
||||
{0x02a6, 0x0124}, // Hcircumflex
|
||||
{0x02a9, 0x0130}, // Iabovedot
|
||||
{0x02ab, 0x011e}, // Gbreve
|
||||
{0x02ac, 0x0134}, // Jcircumflex
|
||||
{0x02b1, 0x0127}, // hstroke
|
||||
{0x02b6, 0x0125}, // hcircumflex
|
||||
{0x02b9, 0x0131}, // idotless
|
||||
{0x02bb, 0x011f}, // gbreve
|
||||
{0x02bc, 0x0135}, // jcircumflex
|
||||
{0x02c5, 0x010a}, // Cabovedot
|
||||
{0x02c6, 0x0108}, // Ccircumflex
|
||||
{0x02d5, 0x0120}, // Gabovedot
|
||||
{0x02d8, 0x011c}, // Gcircumflex
|
||||
{0x02dd, 0x016c}, // Ubreve
|
||||
{0x02de, 0x015c}, // Scircumflex
|
||||
{0x02e5, 0x010b}, // cabovedot
|
||||
{0x02e6, 0x0109}, // ccircumflex
|
||||
{0x02f5, 0x0121}, // gabovedot
|
||||
{0x02f8, 0x011d}, // gcircumflex
|
||||
{0x02fd, 0x016d}, // ubreve
|
||||
{0x02fe, 0x015d}, // scircumflex
|
||||
{0x03a2, 0x0138}, // kra
|
||||
{0x03a3, 0x0156}, // Rcedilla
|
||||
{0x03a5, 0x0128}, // Itilde
|
||||
{0x03a6, 0x013b}, // Lcedilla
|
||||
{0x03aa, 0x0112}, // Emacron
|
||||
{0x03ab, 0x0122}, // Gcedilla
|
||||
{0x03ac, 0x0166}, // Tslash
|
||||
{0x03b3, 0x0157}, // rcedilla
|
||||
{0x03b5, 0x0129}, // itilde
|
||||
{0x03b6, 0x013c}, // lcedilla
|
||||
{0x03ba, 0x0113}, // emacron
|
||||
{0x03bb, 0x0123}, // gcedilla
|
||||
{0x03bc, 0x0167}, // tslash
|
||||
{0x03bd, 0x014a}, // ENG
|
||||
{0x03bf, 0x014b}, // eng
|
||||
{0x03c0, 0x0100}, // Amacron
|
||||
{0x03c7, 0x012e}, // Iogonek
|
||||
{0x03cc, 0x0116}, // Eabovedot
|
||||
{0x03cf, 0x012a}, // Imacron
|
||||
{0x03d1, 0x0145}, // Ncedilla
|
||||
{0x03d2, 0x014c}, // Omacron
|
||||
{0x03d3, 0x0136}, // Kcedilla
|
||||
{0x03d9, 0x0172}, // Uogonek
|
||||
{0x03dd, 0x0168}, // Utilde
|
||||
{0x03de, 0x016a}, // Umacron
|
||||
{0x03e0, 0x0101}, // amacron
|
||||
{0x03e7, 0x012f}, // iogonek
|
||||
{0x03ec, 0x0117}, // eabovedot
|
||||
{0x03ef, 0x012b}, // imacron
|
||||
{0x03f1, 0x0146}, // ncedilla
|
||||
{0x03f2, 0x014d}, // omacron
|
||||
{0x03f3, 0x0137}, // kcedilla
|
||||
{0x03f9, 0x0173}, // uogonek
|
||||
{0x03fd, 0x0169}, // utilde
|
||||
{0x03fe, 0x016b}, // umacron
|
||||
{0x047e, 0x203e}, // overline
|
||||
{0x04a1, 0x3002}, // kana_fullstop
|
||||
{0x04a2, 0x300c}, // kana_openingbracket
|
||||
{0x04a3, 0x300d}, // kana_closingbracket
|
||||
{0x04a4, 0x3001}, // kana_comma
|
||||
{0x04a5, 0x30fb}, // kana_conjunctive
|
||||
{0x04a6, 0x30f2}, // kana_WO
|
||||
{0x04a7, 0x30a1}, // kana_a
|
||||
{0x04a8, 0x30a3}, // kana_i
|
||||
{0x04a9, 0x30a5}, // kana_u
|
||||
{0x04aa, 0x30a7}, // kana_e
|
||||
{0x04ab, 0x30a9}, // kana_o
|
||||
{0x04ac, 0x30e3}, // kana_ya
|
||||
{0x04ad, 0x30e5}, // kana_yu
|
||||
{0x04ae, 0x30e7}, // kana_yo
|
||||
{0x04af, 0x30c3}, // kana_tsu
|
||||
{0x04b0, 0x30fc}, // prolongedsound
|
||||
{0x04b1, 0x30a2}, // kana_A
|
||||
{0x04b2, 0x30a4}, // kana_I
|
||||
{0x04b3, 0x30a6}, // kana_U
|
||||
{0x04b4, 0x30a8}, // kana_E
|
||||
{0x04b5, 0x30aa}, // kana_O
|
||||
{0x04b6, 0x30ab}, // kana_KA
|
||||
{0x04b7, 0x30ad}, // kana_KI
|
||||
{0x04b8, 0x30af}, // kana_KU
|
||||
{0x04b9, 0x30b1}, // kana_KE
|
||||
{0x04ba, 0x30b3}, // kana_KO
|
||||
{0x04bb, 0x30b5}, // kana_SA
|
||||
{0x04bc, 0x30b7}, // kana_SHI
|
||||
{0x04bd, 0x30b9}, // kana_SU
|
||||
{0x04be, 0x30bb}, // kana_SE
|
||||
{0x04bf, 0x30bd}, // kana_SO
|
||||
{0x04c0, 0x30bf}, // kana_TA
|
||||
{0x04c1, 0x30c1}, // kana_CHI
|
||||
{0x04c2, 0x30c4}, // kana_TSU
|
||||
{0x04c3, 0x30c6}, // kana_TE
|
||||
{0x04c4, 0x30c8}, // kana_TO
|
||||
{0x04c5, 0x30ca}, // kana_NA
|
||||
{0x04c6, 0x30cb}, // kana_NI
|
||||
{0x04c7, 0x30cc}, // kana_NU
|
||||
{0x04c8, 0x30cd}, // kana_NE
|
||||
{0x04c9, 0x30ce}, // kana_NO
|
||||
{0x04ca, 0x30cf}, // kana_HA
|
||||
{0x04cb, 0x30d2}, // kana_HI
|
||||
{0x04cc, 0x30d5}, // kana_FU
|
||||
{0x04cd, 0x30d8}, // kana_HE
|
||||
{0x04ce, 0x30db}, // kana_HO
|
||||
{0x04cf, 0x30de}, // kana_MA
|
||||
{0x04d0, 0x30df}, // kana_MI
|
||||
{0x04d1, 0x30e0}, // kana_MU
|
||||
{0x04d2, 0x30e1}, // kana_ME
|
||||
{0x04d3, 0x30e2}, // kana_MO
|
||||
{0x04d4, 0x30e4}, // kana_YA
|
||||
{0x04d5, 0x30e6}, // kana_YU
|
||||
{0x04d6, 0x30e8}, // kana_YO
|
||||
{0x04d7, 0x30e9}, // kana_RA
|
||||
{0x04d8, 0x30ea}, // kana_RI
|
||||
{0x04d9, 0x30eb}, // kana_RU
|
||||
{0x04da, 0x30ec}, // kana_RE
|
||||
{0x04db, 0x30ed}, // kana_RO
|
||||
{0x04dc, 0x30ef}, // kana_WA
|
||||
{0x04dd, 0x30f3}, // kana_N
|
||||
{0x04de, 0x309b}, // voicedsound
|
||||
{0x04df, 0x309c}, // semivoicedsound
|
||||
{0x05ac, 0x060c}, // Arabic_comma
|
||||
{0x05bb, 0x061b}, // Arabic_semicolon
|
||||
{0x05bf, 0x061f}, // Arabic_question_mark
|
||||
{0x05c1, 0x0621}, // Arabic_hamza
|
||||
{0x05c2, 0x0622}, // Arabic_maddaonalef
|
||||
{0x05c3, 0x0623}, // Arabic_hamzaonalef
|
||||
{0x05c4, 0x0624}, // Arabic_hamzaonwaw
|
||||
{0x05c5, 0x0625}, // Arabic_hamzaunderalef
|
||||
{0x05c6, 0x0626}, // Arabic_hamzaonyeh
|
||||
{0x05c7, 0x0627}, // Arabic_alef
|
||||
{0x05c8, 0x0628}, // Arabic_beh
|
||||
{0x05c9, 0x0629}, // Arabic_tehmarbuta
|
||||
{0x05ca, 0x062a}, // Arabic_teh
|
||||
{0x05cb, 0x062b}, // Arabic_theh
|
||||
{0x05cc, 0x062c}, // Arabic_jeem
|
||||
{0x05cd, 0x062d}, // Arabic_hah
|
||||
{0x05ce, 0x062e}, // Arabic_khah
|
||||
{0x05cf, 0x062f}, // Arabic_dal
|
||||
{0x05d0, 0x0630}, // Arabic_thal
|
||||
{0x05d1, 0x0631}, // Arabic_ra
|
||||
{0x05d2, 0x0632}, // Arabic_zain
|
||||
{0x05d3, 0x0633}, // Arabic_seen
|
||||
{0x05d4, 0x0634}, // Arabic_sheen
|
||||
{0x05d5, 0x0635}, // Arabic_sad
|
||||
{0x05d6, 0x0636}, // Arabic_dad
|
||||
{0x05d7, 0x0637}, // Arabic_tah
|
||||
{0x05d8, 0x0638}, // Arabic_zah
|
||||
{0x05d9, 0x0639}, // Arabic_ain
|
||||
{0x05da, 0x063a}, // Arabic_ghain
|
||||
{0x05e0, 0x0640}, // Arabic_tatweel
|
||||
{0x05e1, 0x0641}, // Arabic_feh
|
||||
{0x05e2, 0x0642}, // Arabic_qaf
|
||||
{0x05e3, 0x0643}, // Arabic_kaf
|
||||
{0x05e4, 0x0644}, // Arabic_lam
|
||||
{0x05e5, 0x0645}, // Arabic_meem
|
||||
{0x05e6, 0x0646}, // Arabic_noon
|
||||
{0x05e7, 0x0647}, // Arabic_ha
|
||||
{0x05e8, 0x0648}, // Arabic_waw
|
||||
{0x05e9, 0x0649}, // Arabic_alefmaksura
|
||||
{0x05ea, 0x064a}, // Arabic_yeh
|
||||
{0x05eb, 0x064b}, // Arabic_fathatan
|
||||
{0x05ec, 0x064c}, // Arabic_dammatan
|
||||
{0x05ed, 0x064d}, // Arabic_kasratan
|
||||
{0x05ee, 0x064e}, // Arabic_fatha
|
||||
{0x05ef, 0x064f}, // Arabic_damma
|
||||
{0x05f0, 0x0650}, // Arabic_kasra
|
||||
{0x05f1, 0x0651}, // Arabic_shadda
|
||||
{0x05f2, 0x0652}, // Arabic_sukun
|
||||
{0x06a1, 0x0452}, // Serbian_dje
|
||||
{0x06a2, 0x0453}, // Macedonia_gje
|
||||
{0x06a3, 0x0451}, // Cyrillic_io
|
||||
{0x06a4, 0x0454}, // Ukrainian_ie
|
||||
{0x06a5, 0x0455}, // Macedonia_dse
|
||||
{0x06a6, 0x0456}, // Ukrainian_i
|
||||
{0x06a7, 0x0457}, // Ukrainian_yi
|
||||
{0x06a8, 0x0458}, // Cyrillic_je
|
||||
{0x06a9, 0x0459}, // Cyrillic_lje
|
||||
{0x06aa, 0x045a}, // Cyrillic_nje
|
||||
{0x06ab, 0x045b}, // Serbian_tshe
|
||||
{0x06ac, 0x045c}, // Macedonia_kje
|
||||
{0x06ad, 0x0491}, // Ukrainian_ghe_with_upturn
|
||||
{0x06ae, 0x045e}, // Byelorussian_shortu
|
||||
{0x06af, 0x045f}, // Cyrillic_dzhe
|
||||
{0x06b0, 0x2116}, // numerosign
|
||||
{0x06b1, 0x0402}, // Serbian_DJE
|
||||
{0x06b2, 0x0403}, // Macedonia_GJE
|
||||
{0x06b3, 0x0401}, // Cyrillic_IO
|
||||
{0x06b4, 0x0404}, // Ukrainian_IE
|
||||
{0x06b5, 0x0405}, // Macedonia_DSE
|
||||
{0x06b6, 0x0406}, // Ukrainian_I
|
||||
{0x06b7, 0x0407}, // Ukrainian_YI
|
||||
{0x06b8, 0x0408}, // Cyrillic_JE
|
||||
{0x06b9, 0x0409}, // Cyrillic_LJE
|
||||
{0x06ba, 0x040a}, // Cyrillic_NJE
|
||||
{0x06bb, 0x040b}, // Serbian_TSHE
|
||||
{0x06bc, 0x040c}, // Macedonia_KJE
|
||||
{0x06bd, 0x0490}, // Ukrainian_GHE_WITH_UPTURN
|
||||
{0x06be, 0x040e}, // Byelorussian_SHORTU
|
||||
{0x06bf, 0x040f}, // Cyrillic_DZHE
|
||||
{0x06c0, 0x044e}, // Cyrillic_yu
|
||||
{0x06c1, 0x0430}, // Cyrillic_a
|
||||
{0x06c2, 0x0431}, // Cyrillic_be
|
||||
{0x06c3, 0x0446}, // Cyrillic_tse
|
||||
{0x06c4, 0x0434}, // Cyrillic_de
|
||||
{0x06c5, 0x0435}, // Cyrillic_ie
|
||||
{0x06c6, 0x0444}, // Cyrillic_ef
|
||||
{0x06c7, 0x0433}, // Cyrillic_ghe
|
||||
{0x06c8, 0x0445}, // Cyrillic_ha
|
||||
{0x06c9, 0x0438}, // Cyrillic_i
|
||||
{0x06ca, 0x0439}, // Cyrillic_shorti
|
||||
{0x06cb, 0x043a}, // Cyrillic_ka
|
||||
{0x06cc, 0x043b}, // Cyrillic_el
|
||||
{0x06cd, 0x043c}, // Cyrillic_em
|
||||
{0x06ce, 0x043d}, // Cyrillic_en
|
||||
{0x06cf, 0x043e}, // Cyrillic_o
|
||||
{0x06d0, 0x043f}, // Cyrillic_pe
|
||||
{0x06d1, 0x044f}, // Cyrillic_ya
|
||||
{0x06d2, 0x0440}, // Cyrillic_er
|
||||
{0x06d3, 0x0441}, // Cyrillic_es
|
||||
{0x06d4, 0x0442}, // Cyrillic_te
|
||||
{0x06d5, 0x0443}, // Cyrillic_u
|
||||
{0x06d6, 0x0436}, // Cyrillic_zhe
|
||||
{0x06d7, 0x0432}, // Cyrillic_ve
|
||||
{0x06d8, 0x044c}, // Cyrillic_softsign
|
||||
{0x06d9, 0x044b}, // Cyrillic_yeru
|
||||
{0x06da, 0x0437}, // Cyrillic_ze
|
||||
{0x06db, 0x0448}, // Cyrillic_sha
|
||||
{0x06dc, 0x044d}, // Cyrillic_e
|
||||
{0x06dd, 0x0449}, // Cyrillic_shcha
|
||||
{0x06de, 0x0447}, // Cyrillic_che
|
||||
{0x06df, 0x044a}, // Cyrillic_hardsign
|
||||
{0x06e0, 0x042e}, // Cyrillic_YU
|
||||
{0x06e1, 0x0410}, // Cyrillic_A
|
||||
{0x06e2, 0x0411}, // Cyrillic_BE
|
||||
{0x06e3, 0x0426}, // Cyrillic_TSE
|
||||
{0x06e4, 0x0414}, // Cyrillic_DE
|
||||
{0x06e5, 0x0415}, // Cyrillic_IE
|
||||
{0x06e6, 0x0424}, // Cyrillic_EF
|
||||
{0x06e7, 0x0413}, // Cyrillic_GHE
|
||||
{0x06e8, 0x0425}, // Cyrillic_HA
|
||||
{0x06e9, 0x0418}, // Cyrillic_I
|
||||
{0x06ea, 0x0419}, // Cyrillic_SHORTI
|
||||
{0x06eb, 0x041a}, // Cyrillic_KA
|
||||
{0x06ec, 0x041b}, // Cyrillic_EL
|
||||
{0x06ed, 0x041c}, // Cyrillic_EM
|
||||
{0x06ee, 0x041d}, // Cyrillic_EN
|
||||
{0x06ef, 0x041e}, // Cyrillic_O
|
||||
{0x06f0, 0x041f}, // Cyrillic_PE
|
||||
{0x06f1, 0x042f}, // Cyrillic_YA
|
||||
{0x06f2, 0x0420}, // Cyrillic_ER
|
||||
{0x06f3, 0x0421}, // Cyrillic_ES
|
||||
{0x06f4, 0x0422}, // Cyrillic_TE
|
||||
{0x06f5, 0x0423}, // Cyrillic_U
|
||||
{0x06f6, 0x0416}, // Cyrillic_ZHE
|
||||
{0x06f7, 0x0412}, // Cyrillic_VE
|
||||
{0x06f8, 0x042c}, // Cyrillic_SOFTSIGN
|
||||
{0x06f9, 0x042b}, // Cyrillic_YERU
|
||||
{0x06fa, 0x0417}, // Cyrillic_ZE
|
||||
{0x06fb, 0x0428}, // Cyrillic_SHA
|
||||
{0x06fc, 0x042d}, // Cyrillic_E
|
||||
{0x06fd, 0x0429}, // Cyrillic_SHCHA
|
||||
{0x06fe, 0x0427}, // Cyrillic_CHE
|
||||
{0x06ff, 0x042a}, // Cyrillic_HARDSIGN
|
||||
{0x07a1, 0x0386}, // Greek_ALPHAaccent
|
||||
{0x07a2, 0x0388}, // Greek_EPSILONaccent
|
||||
{0x07a3, 0x0389}, // Greek_ETAaccent
|
||||
{0x07a4, 0x038a}, // Greek_IOTAaccent
|
||||
{0x07a5, 0x03aa}, // Greek_IOTAdieresis
|
||||
{0x07a7, 0x038c}, // Greek_OMICRONaccent
|
||||
{0x07a8, 0x038e}, // Greek_UPSILONaccent
|
||||
{0x07a9, 0x03ab}, // Greek_UPSILONdieresis
|
||||
{0x07ab, 0x038f}, // Greek_OMEGAaccent
|
||||
{0x07ae, 0x0385}, // Greek_accentdieresis
|
||||
{0x07af, 0x2015}, // Greek_horizbar
|
||||
{0x07b1, 0x03ac}, // Greek_alphaaccent
|
||||
{0x07b2, 0x03ad}, // Greek_epsilonaccent
|
||||
{0x07b3, 0x03ae}, // Greek_etaaccent
|
||||
{0x07b4, 0x03af}, // Greek_iotaaccent
|
||||
{0x07b5, 0x03ca}, // Greek_IOTAdieresis
|
||||
{0x07b6, 0x0390}, // Greek_iotaaccentdieresis
|
||||
{0x07b7, 0x03cc}, // Greek_omicronaccent
|
||||
{0x07b8, 0x03cd}, // Greek_upsilonaccent
|
||||
{0x07b9, 0x03cb}, // Greek_upsilondieresis
|
||||
{0x07ba, 0x03b0}, // Greek_upsilonaccentdieresis
|
||||
{0x07bb, 0x03ce}, // Greek_omegaaccent
|
||||
{0x07c1, 0x0391}, // Greek_ALPHA
|
||||
{0x07c2, 0x0392}, // Greek_BETA
|
||||
{0x07c3, 0x0393}, // Greek_GAMMA
|
||||
{0x07c4, 0x0394}, // Greek_DELTA
|
||||
{0x07c5, 0x0395}, // Greek_EPSILON
|
||||
{0x07c6, 0x0396}, // Greek_ZETA
|
||||
{0x07c7, 0x0397}, // Greek_ETA
|
||||
{0x07c8, 0x0398}, // Greek_THETA
|
||||
{0x07c9, 0x0399}, // Greek_IOTA
|
||||
{0x07ca, 0x039a}, // Greek_KAPPA
|
||||
{0x07cb, 0x039b}, // Greek_LAMBDA
|
||||
{0x07cb, 0x039b}, // Greek_LAMDA
|
||||
{0x07cc, 0x039c}, // Greek_MU
|
||||
{0x07cd, 0x039d}, // Greek_NU
|
||||
{0x07ce, 0x039e}, // Greek_XI
|
||||
{0x07cf, 0x039f}, // Greek_OMICRON
|
||||
{0x07d0, 0x03a0}, // Greek_PI
|
||||
{0x07d1, 0x03a1}, // Greek_RHO
|
||||
{0x07d2, 0x03a3}, // Greek_SIGMA
|
||||
{0x07d4, 0x03a4}, // Greek_TAU
|
||||
{0x07d5, 0x03a5}, // Greek_UPSILON
|
||||
{0x07d6, 0x03a6}, // Greek_PHI
|
||||
{0x07d7, 0x03a7}, // Greek_CHI
|
||||
{0x07d8, 0x03a8}, // Greek_PSI
|
||||
{0x07d9, 0x03a9}, // Greek_OMEGA
|
||||
{0x07e1, 0x03b1}, // Greek_alpha
|
||||
{0x07e2, 0x03b2}, // Greek_beta
|
||||
{0x07e3, 0x03b3}, // Greek_gamma
|
||||
{0x07e4, 0x03b4}, // Greek_delta
|
||||
{0x07e5, 0x03b5}, // Greek_epsilon
|
||||
{0x07e6, 0x03b6}, // Greek_zeta
|
||||
{0x07e7, 0x03b7}, // Greek_eta
|
||||
{0x07e8, 0x03b8}, // Greek_theta
|
||||
{0x07e9, 0x03b9}, // Greek_iota
|
||||
{0x07ea, 0x03ba}, // Greek_kappa
|
||||
{0x07eb, 0x03bb}, // Greek_lambda
|
||||
{0x07ec, 0x03bc}, // Greek_mu
|
||||
{0x07ed, 0x03bd}, // Greek_nu
|
||||
{0x07ee, 0x03be}, // Greek_xi
|
||||
{0x07ef, 0x03bf}, // Greek_omicron
|
||||
{0x07f0, 0x03c0}, // Greek_pi
|
||||
{0x07f1, 0x03c1}, // Greek_rho
|
||||
{0x07f2, 0x03c3}, // Greek_sigma
|
||||
{0x07f3, 0x03c2}, // Greek_finalsmallsigma
|
||||
{0x07f4, 0x03c4}, // Greek_tau
|
||||
{0x07f5, 0x03c5}, // Greek_upsilon
|
||||
{0x07f6, 0x03c6}, // Greek_phi
|
||||
{0x07f7, 0x03c7}, // Greek_chi
|
||||
{0x07f8, 0x03c8}, // Greek_psi
|
||||
{0x07f9, 0x03c9}, // Greek_omega
|
||||
{0x08a1, 0x23b7}, // leftradical
|
||||
{0x08a2, 0x250c}, // topleftradical
|
||||
{0x08a3, 0x2500}, // horizconnector
|
||||
{0x08a4, 0x2320}, // topintegral
|
||||
{0x08a5, 0x2321}, // botintegral
|
||||
{0x08a6, 0x2502}, // vertconnector
|
||||
{0x08a7, 0x23a1}, // topleftsqbracket
|
||||
{0x08a8, 0x23a3}, // botleftsqbracket
|
||||
{0x08a9, 0x23a4}, // toprightsqbracket
|
||||
{0x08aa, 0x23a6}, // botrightsqbracket
|
||||
{0x08ab, 0x239b}, // topleftparens
|
||||
{0x08ac, 0x239d}, // botleftparens
|
||||
{0x08ad, 0x239e}, // toprightparens
|
||||
{0x08ae, 0x23a0}, // botrightparens
|
||||
{0x08af, 0x23a8}, // leftmiddlecurlybrace
|
||||
{0x08b0, 0x23ac}, // rightmiddlecurlybrace
|
||||
{0x08bc, 0x2264}, // lessthanequal
|
||||
{0x08bd, 0x2260}, // notequal
|
||||
{0x08be, 0x2265}, // greaterthanequal
|
||||
{0x08bf, 0x222b}, // integral
|
||||
{0x08c0, 0x2234}, // therefore
|
||||
{0x08c1, 0x221d}, // variation
|
||||
{0x08c2, 0x221e}, // infinity
|
||||
{0x08c5, 0x2207}, // nabla
|
||||
{0x08c8, 0x223c}, // approximate
|
||||
{0x08c9, 0x2243}, // similarequal
|
||||
{0x08cd, 0x21d4}, // ifonlyif
|
||||
{0x08ce, 0x21d2}, // implies
|
||||
{0x08cf, 0x2261}, // identical
|
||||
{0x08d6, 0x221a}, // radical
|
||||
{0x08da, 0x2282}, // includedin
|
||||
{0x08db, 0x2283}, // includes
|
||||
{0x08dc, 0x2229}, // intersection
|
||||
{0x08dd, 0x222a}, // union
|
||||
{0x08de, 0x2227}, // logicaland
|
||||
{0x08df, 0x2228}, // logicalor
|
||||
{0x08ef, 0x2202}, // partialderivative
|
||||
{0x08f6, 0x0192}, // function
|
||||
{0x08fb, 0x2190}, // leftarrow
|
||||
{0x08fc, 0x2191}, // uparrow
|
||||
{0x08fd, 0x2192}, // rightarrow
|
||||
{0x08fe, 0x2193}, // downarrow
|
||||
{0x09df, 0x2422}, // blank
|
||||
{0x09e0, 0x25c6}, // soliddiamond
|
||||
{0x09e1, 0x2592}, // checkerboard
|
||||
{0x09e2, 0x2409}, // ht
|
||||
{0x09e3, 0x240c}, // ff
|
||||
{0x09e4, 0x240d}, // cr
|
||||
{0x09e5, 0x240a}, // lf
|
||||
{0x09e8, 0x2424}, // nl
|
||||
{0x09e9, 0x240b}, // vt
|
||||
{0x09ea, 0x2518}, // lowrightcorner
|
||||
{0x09eb, 0x2510}, // uprightcorner
|
||||
{0x09ec, 0x250c}, // upleftcorner
|
||||
{0x09ed, 0x2514}, // lowleftcorner
|
||||
{0x09ee, 0x253c}, // crossinglines
|
||||
{0x09ef, 0x23ba}, // horizlinescan1
|
||||
{0x09f0, 0x23bb}, // horizlinescan3
|
||||
{0x09f1, 0x2500}, // horizlinescan5
|
||||
{0x09f2, 0x23bc}, // horizlinescan7
|
||||
{0x09f3, 0x23bd}, // horizlinescan9
|
||||
{0x09f4, 0x251c}, // leftt
|
||||
{0x09f5, 0x2524}, // rightt
|
||||
{0x09f6, 0x2534}, // bott
|
||||
{0x09f7, 0x252c}, // topt
|
||||
{0x09f8, 0x2502}, // vertbar
|
||||
{0x0aa1, 0x2003}, // emspace
|
||||
{0x0aa2, 0x2002}, // enspace
|
||||
{0x0aa3, 0x2004}, // em3space
|
||||
{0x0aa4, 0x2005}, // em4space
|
||||
{0x0aa5, 0x2007}, // digitspace
|
||||
{0x0aa6, 0x2008}, // punctspace
|
||||
{0x0aa7, 0x2009}, // thinspace
|
||||
{0x0aa8, 0x200a}, // hairspace
|
||||
{0x0aa9, 0x2014}, // emdash
|
||||
{0x0aaa, 0x2013}, // endash
|
||||
{0x0aac, 0x2423}, // signifblank
|
||||
{0x0aae, 0x2026}, // ellipsis
|
||||
{0x0aaf, 0x2025}, // doubbaselinedot
|
||||
{0x0ab0, 0x2153}, // onethird
|
||||
{0x0ab1, 0x2154}, // twothirds
|
||||
{0x0ab2, 0x2155}, // onefifth
|
||||
{0x0ab3, 0x2156}, // twofifths
|
||||
{0x0ab4, 0x2157}, // threefifths
|
||||
{0x0ab5, 0x2158}, // fourfifths
|
||||
{0x0ab6, 0x2159}, // onesixth
|
||||
{0x0ab7, 0x215a}, // fivesixths
|
||||
{0x0ab8, 0x2105}, // careof
|
||||
{0x0abb, 0x2012}, // figdash
|
||||
{0x0abc, 0x2329}, // leftanglebracket (not U+27E8)
|
||||
{0x0abd, 0x002e}, // decimalpoint
|
||||
{0x0abe, 0x232a}, // rightanglebracket (not U+27E9)
|
||||
{0x0ac3, 0x215b}, // oneeighth
|
||||
{0x0ac4, 0x215c}, // threeeighths
|
||||
{0x0ac5, 0x215d}, // fiveeighths
|
||||
{0x0ac6, 0x215e}, // seveneighths
|
||||
{0x0ac9, 0x2122}, // trademark
|
||||
{0x0aca, 0x2613}, // signaturemark
|
||||
{0x0acc, 0x25c1}, // leftopentriangle
|
||||
{0x0acd, 0x25b7}, // rightopentriangle
|
||||
{0x0ace, 0x25cb}, // emopencircle
|
||||
{0x0acf, 0x25af}, // emopenrectangle
|
||||
{0x0ad0, 0x2018}, // leftsinglequotemark
|
||||
{0x0ad1, 0x2019}, // rightsinglequotemark
|
||||
{0x0ad2, 0x201c}, // leftdoublequotemark
|
||||
{0x0ad3, 0x201d}, // rightdoublequotemark
|
||||
{0x0ad4, 0x211e}, // prescription
|
||||
{0x0ad5, 0x2030}, // per mille
|
||||
{0x0ad6, 0x2032}, // minutes
|
||||
{0x0ad7, 0x2033}, // seconds
|
||||
{0x0ad9, 0x271d}, // latincross
|
||||
{0x0adb, 0x25ac}, // filledrectbullet
|
||||
{0x0adc, 0x25c0}, // filledlefttribullet
|
||||
{0x0add, 0x25b6}, // filledrighttribullet
|
||||
{0x0ade, 0x25cf}, // emfilledcircle
|
||||
{0x0adf, 0x25ae}, // emfilledrect
|
||||
{0x0ae0, 0x25e6}, // enopencircbullet
|
||||
{0x0ae1, 0x25ab}, // enopensquarebullet
|
||||
{0x0ae2, 0x25ad}, // openrectbullet
|
||||
{0x0ae3, 0x25b3}, // opentribulletup
|
||||
{0x0ae4, 0x25bd}, // opentribulletdown
|
||||
{0x0ae5, 0x2606}, // openstar
|
||||
{0x0ae6, 0x2022}, // enfilledcircbullet
|
||||
{0x0ae7, 0x25aa}, // enfilledsqbullet
|
||||
{0x0ae8, 0x25b2}, // filledtribulletup
|
||||
{0x0ae9, 0x25bc}, // filledtribulletdown
|
||||
{0x0aea, 0x261c}, // leftpointer
|
||||
{0x0aeb, 0x261e}, // rightpointer
|
||||
{0x0aec, 0x2663}, // club
|
||||
{0x0aed, 0x2666}, // diamond
|
||||
{0x0aee, 0x2665}, // heart
|
||||
{0x0af0, 0x2720}, // maltesecross
|
||||
{0x0af1, 0x2020}, // dagger
|
||||
{0x0af2, 0x2021}, // doubledagger
|
||||
{0x0af3, 0x2713}, // checkmark
|
||||
{0x0af4, 0x2717}, // ballotcross
|
||||
{0x0af5, 0x266f}, // musicalsharp
|
||||
{0x0af6, 0x266d}, // musicalflat
|
||||
{0x0af7, 0x2642}, // malesymbol
|
||||
{0x0af8, 0x2640}, // femalesymbol
|
||||
{0x0af9, 0x260e}, // telephone
|
||||
{0x0afa, 0x2315}, // telephonerecorder
|
||||
{0x0afb, 0x2117}, // phonographcopyright
|
||||
{0x0afc, 0x2038}, // caret
|
||||
{0x0afd, 0x201a}, // singlelowquotemark
|
||||
{0x0afe, 0x201e}, // doublelowquotemark
|
||||
{0x0ba3, 0x003c}, // leftcaret
|
||||
{0x0ba6, 0x003e}, // rightcaret
|
||||
{0x0ba8, 0x2228}, // downcaret
|
||||
{0x0ba9, 0x2227}, // upcaret
|
||||
{0x0bc0, 0x00af}, // overbar
|
||||
{0x0bc2, 0x22a4}, // downtack
|
||||
{0x0bc3, 0x2229}, // upshoe
|
||||
{0x0bc4, 0x230a}, // downstile
|
||||
{0x0bc6, 0x005f}, // underbar
|
||||
{0x0bca, 0x2218}, // jot
|
||||
{0x0bcc, 0x2395}, // quad
|
||||
{0x0bce, 0x22a5}, // uptack
|
||||
{0x0bcf, 0x25cb}, // circle
|
||||
{0x0bd3, 0x2308}, // upstile
|
||||
{0x0bd6, 0x222a}, // downshoe
|
||||
{0x0bd8, 0x2283}, // rightshoe
|
||||
{0x0bda, 0x2282}, // leftshoe
|
||||
{0x0bdc, 0x22a3}, // lefttack
|
||||
{0x0bfc, 0x22a2}, // righttack
|
||||
{0x0cdf, 0x2017}, // hebrew_doublelowline
|
||||
{0x0ce0, 0x05d0}, // hebrew_aleph
|
||||
{0x0ce1, 0x05d1}, // hebrew_bet
|
||||
{0x0ce2, 0x05d2}, // hebrew_gimel
|
||||
{0x0ce3, 0x05d3}, // hebrew_dalet
|
||||
{0x0ce4, 0x05d4}, // hebrew_he
|
||||
{0x0ce5, 0x05d5}, // hebrew_waw
|
||||
{0x0ce6, 0x05d6}, // hebrew_zain
|
||||
{0x0ce7, 0x05d7}, // hebrew_chet
|
||||
{0x0ce8, 0x05d8}, // hebrew_tet
|
||||
{0x0ce9, 0x05d9}, // hebrew_yod
|
||||
{0x0cea, 0x05da}, // hebrew_finalkaph
|
||||
{0x0ceb, 0x05db}, // hebrew_kaph
|
||||
{0x0cec, 0x05dc}, // hebrew_lamed
|
||||
{0x0ced, 0x05dd}, // hebrew_finalmem
|
||||
{0x0cee, 0x05de}, // hebrew_mem
|
||||
{0x0cef, 0x05df}, // hebrew_finalnun
|
||||
{0x0cf0, 0x05e0}, // hebrew_nun
|
||||
{0x0cf1, 0x05e1}, // hebrew_samech
|
||||
{0x0cf2, 0x05e2}, // hebrew_ayin
|
||||
{0x0cf3, 0x05e3}, // hebrew_finalpe
|
||||
{0x0cf4, 0x05e4}, // hebrew_pe
|
||||
{0x0cf5, 0x05e5}, // hebrew_finalzade
|
||||
{0x0cf6, 0x05e6}, // hebrew_zade
|
||||
{0x0cf7, 0x05e7}, // hebrew_qoph
|
||||
{0x0cf8, 0x05e8}, // hebrew_resh
|
||||
{0x0cf9, 0x05e9}, // hebrew_shin
|
||||
{0x0cfa, 0x05ea}, // hebrew_taw
|
||||
{0x0da1, 0x0e01}, // Thai_kokai
|
||||
{0x0da2, 0x0e02}, // Thai_khokhai
|
||||
{0x0da3, 0x0e03}, // Thai_khokhuat
|
||||
{0x0da4, 0x0e04}, // Thai_khokhwai
|
||||
{0x0da5, 0x0e05}, // Thai_khokhon
|
||||
{0x0da6, 0x0e06}, // Thai_khorakhang
|
||||
{0x0da7, 0x0e07}, // Thai_ngongu
|
||||
{0x0da8, 0x0e08}, // Thai_chochan
|
||||
{0x0da9, 0x0e09}, // Thai_choching
|
||||
{0x0daa, 0x0e0a}, // Thai_chochang
|
||||
{0x0dab, 0x0e0b}, // Thai_soso
|
||||
{0x0dac, 0x0e0c}, // Thai_chochoe
|
||||
{0x0dad, 0x0e0d}, // Thai_yoying
|
||||
{0x0dae, 0x0e0e}, // Thai_dochada
|
||||
{0x0daf, 0x0e0f}, // Thai_topatak
|
||||
{0x0db0, 0x0e10}, // Thai_thothan
|
||||
{0x0db1, 0x0e11}, // Thai_thonangmontho
|
||||
{0x0db2, 0x0e12}, // Thai_thophuthao
|
||||
{0x0db3, 0x0e13}, // Thai_nonen
|
||||
{0x0db4, 0x0e14}, // Thai_dodek
|
||||
{0x0db5, 0x0e15}, // Thai_totao
|
||||
{0x0db6, 0x0e16}, // Thai_thothung
|
||||
{0x0db7, 0x0e17}, // Thai_thothahan
|
||||
{0x0db8, 0x0e18}, // Thai_thothong
|
||||
{0x0db9, 0x0e19}, // Thai_nonu
|
||||
{0x0dba, 0x0e1a}, // Thai_bobaimai
|
||||
{0x0dbb, 0x0e1b}, // Thai_popla
|
||||
{0x0dbc, 0x0e1c}, // Thai_phophung
|
||||
{0x0dbd, 0x0e1d}, // Thai_fofa
|
||||
{0x0dbe, 0x0e1e}, // Thai_phophan
|
||||
{0x0dbf, 0x0e1f}, // Thai_fofan
|
||||
{0x0dc0, 0x0e20}, // Thai_phosamphao
|
||||
{0x0dc1, 0x0e21}, // Thai_moma
|
||||
{0x0dc2, 0x0e22}, // Thai_yoyak
|
||||
{0x0dc3, 0x0e23}, // Thai_rorua
|
||||
{0x0dc4, 0x0e24}, // Thai_ru
|
||||
{0x0dc5, 0x0e25}, // Thai_loling
|
||||
{0x0dc6, 0x0e26}, // Thai_lu
|
||||
{0x0dc7, 0x0e27}, // Thai_wowaen
|
||||
{0x0dc8, 0x0e28}, // Thai_sosala
|
||||
{0x0dc9, 0x0e29}, // Thai_sorusi
|
||||
{0x0dca, 0x0e2a}, // Thai_sosua
|
||||
{0x0dcb, 0x0e2b}, // Thai_hohip
|
||||
{0x0dcc, 0x0e2c}, // Thai_lochula
|
||||
{0x0dcd, 0x0e2d}, // Thai_oang
|
||||
{0x0dce, 0x0e2e}, // Thai_honokhuk
|
||||
{0x0dcf, 0x0e2f}, // Thai_paiyannoi
|
||||
{0x0dd0, 0x0e30}, // Thai_saraa
|
||||
{0x0dd1, 0x0e31}, // Thai_maihanakat
|
||||
{0x0dd2, 0x0e32}, // Thai_saraaa
|
||||
{0x0dd3, 0x0e33}, // Thai_saraam
|
||||
{0x0dd4, 0x0e34}, // Thai_sarai
|
||||
{0x0dd5, 0x0e35}, // Thai_saraii
|
||||
{0x0dd6, 0x0e36}, // Thai_saraue
|
||||
{0x0dd7, 0x0e37}, // Thai_sarauee
|
||||
{0x0dd8, 0x0e38}, // Thai_sarau
|
||||
{0x0dd9, 0x0e39}, // Thai_sarauu
|
||||
{0x0dda, 0x0e3a}, // Thai_phinthu
|
||||
{0x0ddf, 0x0e3f}, // Thai_baht
|
||||
{0x0de0, 0x0e40}, // Thai_sarae
|
||||
{0x0de1, 0x0e41}, // Thai_saraae
|
||||
{0x0de2, 0x0e42}, // Thai_sarao
|
||||
{0x0de3, 0x0e43}, // Thai_saraaimaimuan
|
||||
{0x0de4, 0x0e44}, // Thai_saraaimaimalai
|
||||
{0x0de5, 0x0e45}, // Thai_lakkhangyao
|
||||
{0x0de6, 0x0e46}, // Thai_maiyamok
|
||||
{0x0de7, 0x0e47}, // Thai_maitaikhu
|
||||
{0x0de8, 0x0e48}, // Thai_maiek
|
||||
{0x0de9, 0x0e49}, // Thai_maitho
|
||||
{0x0dea, 0x0e4a}, // Thai_maitri
|
||||
{0x0deb, 0x0e4b}, // Thai_maichattawa
|
||||
{0x0dec, 0x0e4c}, // Thai_thanthakhat
|
||||
{0x0ded, 0x0e4d}, // Thai_nikhahit
|
||||
{0x0df0, 0x0e50}, // Thai_leksun
|
||||
{0x0df1, 0x0e51}, // Thai_leknung
|
||||
{0x0df2, 0x0e52}, // Thai_leksong
|
||||
{0x0df3, 0x0e53}, // Thai_leksam
|
||||
{0x0df4, 0x0e54}, // Thai_leksi
|
||||
{0x0df5, 0x0e55}, // Thai_lekha
|
||||
{0x0df6, 0x0e56}, // Thai_lekhok
|
||||
{0x0df7, 0x0e57}, // Thai_lekchet
|
||||
{0x0df8, 0x0e58}, // Thai_lekpaet
|
||||
{0x0df9, 0x0e59}, // Thai_lekkao
|
||||
{0x0ea1, 0x3131}, // Hangul_Kiyeog
|
||||
{0x0ea2, 0x3132}, // Hangul_SsangKiyeog
|
||||
{0x0ea3, 0x3133}, // Hangul_KiyeogSios
|
||||
{0x0ea4, 0x3134}, // Hangul_Nieun
|
||||
{0x0ea5, 0x3135}, // Hangul_NieunJieuj
|
||||
{0x0ea6, 0x3136}, // Hangul_NieunHieuh
|
||||
{0x0ea7, 0x3137}, // Hangul_Dikeud
|
||||
{0x0ea8, 0x3138}, // Hangul_SsangDikeud
|
||||
{0x0ea9, 0x3139}, // Hangul_Rieul
|
||||
{0x0eaa, 0x313a}, // Hangul_RieulKiyeog
|
||||
{0x0eab, 0x313b}, // Hangul_RieulMieum
|
||||
{0x0eac, 0x313c}, // Hangul_RieulPieub
|
||||
{0x0ead, 0x313d}, // Hangul_RieulSios
|
||||
{0x0eae, 0x313e}, // Hangul_RieulTieut
|
||||
{0x0eaf, 0x313f}, // Hangul_RieulPhieuf
|
||||
{0x0eb0, 0x3140}, // Hangul_RieulHieuh
|
||||
{0x0eb1, 0x3141}, // Hangul_Mieum
|
||||
{0x0eb2, 0x3142}, // Hangul_Pieub
|
||||
{0x0eb3, 0x3143}, // Hangul_SsangPieub
|
||||
{0x0eb4, 0x3144}, // Hangul_PieubSios
|
||||
{0x0eb5, 0x3145}, // Hangul_Sios
|
||||
{0x0eb6, 0x3146}, // Hangul_SsangSios
|
||||
{0x0eb7, 0x3147}, // Hangul_Ieung
|
||||
{0x0eb8, 0x3148}, // Hangul_Jieuj
|
||||
{0x0eb9, 0x3149}, // Hangul_SsangJieuj
|
||||
{0x0eba, 0x314a}, // Hangul_Cieuc
|
||||
{0x0ebb, 0x314b}, // Hangul_Khieuq
|
||||
{0x0ebc, 0x314c}, // Hangul_Tieut
|
||||
{0x0ebd, 0x314d}, // Hangul_Phieuf
|
||||
{0x0ebe, 0x314e}, // Hangul_Hieuh
|
||||
{0x0ebf, 0x314f}, // Hangul_A
|
||||
{0x0ec0, 0x3150}, // Hangul_AE
|
||||
{0x0ec1, 0x3151}, // Hangul_YA
|
||||
{0x0ec2, 0x3152}, // Hangul_YAE
|
||||
{0x0ec3, 0x3153}, // Hangul_EO
|
||||
{0x0ec4, 0x3154}, // Hangul_E
|
||||
{0x0ec5, 0x3155}, // Hangul_YEO
|
||||
{0x0ec6, 0x3156}, // Hangul_YE
|
||||
{0x0ec7, 0x3157}, // Hangul_O
|
||||
{0x0ec8, 0x3158}, // Hangul_WA
|
||||
{0x0ec9, 0x3159}, // Hangul_WAE
|
||||
{0x0eca, 0x315a}, // Hangul_OE
|
||||
{0x0ecb, 0x315b}, // Hangul_YO
|
||||
{0x0ecc, 0x315c}, // Hangul_U
|
||||
{0x0ecd, 0x315d}, // Hangul_WEO
|
||||
{0x0ece, 0x315e}, // Hangul_WE
|
||||
{0x0ecf, 0x315f}, // Hangul_WI
|
||||
{0x0ed0, 0x3160}, // Hangul_YU
|
||||
{0x0ed1, 0x3161}, // Hangul_EU
|
||||
{0x0ed2, 0x3162}, // Hangul_YI
|
||||
{0x0ed3, 0x3163}, // Hangul_I
|
||||
{0x0ed4, 0x11a8}, // Hangul_J_Kiyeog
|
||||
{0x0ed5, 0x11a9}, // Hangul_J_SsangKiyeog
|
||||
{0x0ed6, 0x11aa}, // Hangul_J_KiyeogSios
|
||||
{0x0ed7, 0x11ab}, // Hangul_J_Nieun
|
||||
{0x0ed8, 0x11ac}, // Hangul_J_NieunJieuj
|
||||
{0x0ed9, 0x11ad}, // Hangul_J_NieunHieuh
|
||||
{0x0eda, 0x11ae}, // Hangul_J_Dikeud
|
||||
{0x0edb, 0x11af}, // Hangul_J_Rieul
|
||||
{0x0edc, 0x11b0}, // Hangul_J_RieulKiyeog
|
||||
{0x0edd, 0x11b1}, // Hangul_J_RieulMieum
|
||||
{0x0ede, 0x11b2}, // Hangul_J_RieulPieub
|
||||
{0x0edf, 0x11b3}, // Hangul_J_RieulSios
|
||||
{0x0ee0, 0x11b4}, // Hangul_J_RieulTieut
|
||||
{0x0ee1, 0x11b5}, // Hangul_J_RieulPhieuf
|
||||
{0x0ee2, 0x11b6}, // Hangul_J_RieulHieuh
|
||||
{0x0ee3, 0x11b7}, // Hangul_J_Mieum
|
||||
{0x0ee4, 0x11b8}, // Hangul_J_Pieub
|
||||
{0x0ee5, 0x11b9}, // Hangul_J_PieubSios
|
||||
{0x0ee6, 0x11ba}, // Hangul_J_Sios
|
||||
{0x0ee7, 0x11bb}, // Hangul_J_SsangSios
|
||||
{0x0ee8, 0x11bc}, // Hangul_J_Ieung
|
||||
{0x0ee9, 0x11bd}, // Hangul_J_Jieuj
|
||||
{0x0eea, 0x11be}, // Hangul_J_Cieuc
|
||||
{0x0eeb, 0x11bf}, // Hangul_J_Khieuq
|
||||
{0x0eec, 0x11c0}, // Hangul_J_Tieut
|
||||
{0x0eed, 0x11c1}, // Hangul_J_Phieuf
|
||||
{0x0eee, 0x11c2}, // Hangul_J_Hieuh
|
||||
{0x0eef, 0x316d}, // Hangul_RieulYeorinHieuh
|
||||
{0x0ef0, 0x3171}, // Hangul_SunkyeongeumMieum
|
||||
{0x0ef1, 0x3178}, // Hangul_SunkyeongeumPieub
|
||||
{0x0ef2, 0x317f}, // Hangul_PanSios
|
||||
{0x0ef3, 0x3181}, // Hangul_KkogjiDalrinIeung
|
||||
{0x0ef4, 0x3184}, // Hangul_SunkyeongeumPhieuf
|
||||
{0x0ef5, 0x3186}, // Hangul_YeorinHieuh
|
||||
{0x0ef6, 0x318d}, // Hangul_AraeA
|
||||
{0x0ef7, 0x318e}, // Hangul_AraeAE
|
||||
{0x0ef8, 0x11eb}, // Hangul_J_PanSios
|
||||
{0x0ef9, 0x11f0}, // Hangul_J_KkogjiDalrinIeung
|
||||
{0x0efa, 0x11f9}, // Hangul_J_YeorinHieuh
|
||||
{0x0eff, 0x20a9}, // Korean_Won
|
||||
{0x13a4, 0x20ac}, // Euro
|
||||
{0x13bc, 0x0152}, // OE
|
||||
{0x13bd, 0x0153}, // oe
|
||||
{0x13be, 0x0178}, // Ydiaeresis
|
||||
{0x20a0, 0x20a0}, // EcuSign
|
||||
{0x20a1, 0x20a1}, // ColonSign
|
||||
{0x20a2, 0x20a2}, // CruzeiroSign
|
||||
{0x20a3, 0x20a3}, // FFrancSign
|
||||
{0x20a4, 0x20a4}, // LiraSign
|
||||
{0x20a5, 0x20a5}, // MillSign
|
||||
{0x20a6, 0x20a6}, // NairaSign
|
||||
{0x20a7, 0x20a7}, // PesetaSign
|
||||
{0x20a8, 0x20a8}, // RupeeSign
|
||||
{0x20a9, 0x20a9}, // WonSign
|
||||
{0x20aa, 0x20aa}, // NewSheqelSign
|
||||
{0x20ab, 0x20ab}, // DongSign
|
||||
{0x20ac, 0x20ac}, // EuroSign
|
||||
{0xfe50, 0x0300}, // dead_grave
|
||||
{0xfe51, 0x0301}, // dead_acute
|
||||
{0xfe52, 0x0302}, // dead_circumflex
|
||||
{0xfe53, 0x0303}, // dead_tilde
|
||||
{0xfe54, 0x0304}, // dead_macron
|
||||
{0xfe55, 0x0306}, // dead_breve
|
||||
{0xfe56, 0x0307}, // dead_abovedot
|
||||
{0xfe57, 0x0308}, // dead_diaeresis
|
||||
{0xfe58, 0x030a}, // dead_abovering
|
||||
{0xfe59, 0x030b}, // dead_doubleacute
|
||||
{0xfe5a, 0x030c}, // dead_caron
|
||||
{0xfe5b, 0x0327}, // dead_cedilla
|
||||
{0xfe5c, 0x0328}, // dead_ogonek
|
||||
{0xfe5d, 0x0345}, // dead_iota
|
||||
{0xfe5e, 0x3099}, // dead_voiced_sound
|
||||
{0xfe5f, 0x309a}, // dead_semivoiced_sound
|
||||
{0xff08, 0x0008}, // BackSpace /* back space, back char */
|
||||
{0xff09, 0x0009}, // Tab
|
||||
{0xff0a, 0x000a}, // Linefeed /* Linefeed, LF */
|
||||
{0xff0b, 0x000b}, // Clear
|
||||
{0xff0d, 0x000d}, // Return /* Return, enter */
|
||||
{0xff13, 0x0013}, // Pause /* Pause, hold */
|
||||
{0xff14, 0x0014}, // Scroll_Lock
|
||||
{0xff15, 0x0015}, // Sys_Req
|
||||
{0xff1b, 0x001b}, // Escape
|
||||
{0xff80, 0x0032}, // KP_Space /* space */
|
||||
{0xff89, 0x0009}, // KP_Tab
|
||||
{0xff8d, 0x000d}, // KP_Enter /* enter */
|
||||
{0xffaa, 0x002a}, // KP_Multiply
|
||||
{0xffab, 0x002b}, // KP_Add
|
||||
{0xffac, 0x002c}, // KP_Separator /* separator, often comma */
|
||||
{0xffad, 0x002d}, // KP_Subtract
|
||||
{0xffae, 0x002e}, // KP_Decimal
|
||||
{0xffaf, 0x002f}, // KP_Divide
|
||||
{0xffb0, 0x0030}, // KP_0
|
||||
{0xffb1, 0x0031}, // KP_1
|
||||
{0xffb2, 0x0032}, // KP_2
|
||||
{0xffb3, 0x0033}, // KP_3
|
||||
{0xffb4, 0x0034}, // KP_4
|
||||
{0xffb5, 0x0035}, // KP_5
|
||||
{0xffb6, 0x0036}, // KP_6
|
||||
{0xffb7, 0x0037}, // KP_7
|
||||
{0xffb8, 0x0038}, // KP_8
|
||||
{0xffb9, 0x0039}, // KP_9
|
||||
{0xffbd, 0x003d}, // KP_Equal /* equals */
|
||||
}
|
||||
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 Eric 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 Eric 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 Eric 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 Eric 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
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
|
||||
// Copyright (c) 2018, Přemysl Eric Janouch <p@janouch.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted.
|
||||
@@ -61,7 +61,7 @@ func detectTLS(sysconn syscall.RawConn) (isTLS bool) {
|
||||
isTLS = buf[0]&0x80 != 0 && buf[2] == 1
|
||||
fallthrough
|
||||
case n == 2:
|
||||
isTLS = buf[0] == 22 && buf[1] == 3
|
||||
isTLS = isTLS || buf[0] == 22 && buf[1] == 3
|
||||
case n == 1:
|
||||
isTLS = buf[0] == 22
|
||||
case err == syscall.EAGAIN:
|
||||
@@ -74,21 +74,21 @@ func detectTLS(sysconn syscall.RawConn) (isTLS bool) {
|
||||
|
||||
// --- Declarations ------------------------------------------------------------
|
||||
|
||||
type connCloseWrite interface {
|
||||
type connCloseWriter interface {
|
||||
net.Conn
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
type client struct {
|
||||
transport net.Conn // underlying connection
|
||||
tls *tls.Conn // TLS, if detected
|
||||
conn connCloseWrite // high-level connection
|
||||
inQ []byte // unprocessed input
|
||||
outQ []byte // unprocessed output
|
||||
reading bool // whether a reading goroutine is running
|
||||
writing bool // whether a writing goroutine is running
|
||||
closing bool // whether we're closing the connection
|
||||
killTimer *time.Timer // timeout
|
||||
transport net.Conn // underlying connection
|
||||
tls *tls.Conn // TLS, if detected
|
||||
conn connCloseWriter // high-level connection
|
||||
inQ []byte // unprocessed input
|
||||
outQ []byte // unprocessed output
|
||||
reading bool // whether a reading goroutine is running
|
||||
writing bool // whether a writing goroutine is running
|
||||
closing bool // whether we're closing the connection
|
||||
killTimer *time.Timer // timeout
|
||||
}
|
||||
|
||||
type preparedEvent struct {
|
||||
@@ -210,15 +210,14 @@ func (c *client) destroy() {
|
||||
}
|
||||
|
||||
// Handle the results from initializing the client's connection.
|
||||
func (c *client) onPrepared(host string, isTLS bool) {
|
||||
func (c *client) onPrepared(isTLS bool) {
|
||||
if isTLS {
|
||||
c.tls = tls.Server(c.transport, tlsConf)
|
||||
c.conn = c.tls
|
||||
} else {
|
||||
c.conn = c.transport.(connCloseWrite)
|
||||
c.conn = c.transport.(connCloseWriter)
|
||||
}
|
||||
|
||||
// TODO: Save the host in the client structure.
|
||||
// TODO: If we've tried to send any data before now, we need to flushOutQ.
|
||||
go read(c)
|
||||
c.reading = true
|
||||
@@ -399,9 +398,9 @@ func processOneEvent() {
|
||||
go prepare(c)
|
||||
|
||||
case ev := <-prepared:
|
||||
log.Println("client is ready:", ev.host)
|
||||
log.Println("client is ready, resolved to", ev.host)
|
||||
if _, ok := clients[ev.client]; ok {
|
||||
ev.client.onPrepared(ev.host, ev.isTLS)
|
||||
ev.client.onPrepared(ev.isTLS)
|
||||
}
|
||||
|
||||
case ev := <-reads:
|
||||
|
||||
329
prototypes/xgb-draw.go
Normal file
329
prototypes/xgb-draw.go
Normal file
@@ -0,0 +1,329 @@
|
||||
// Network-friendly drawing application based on XRender.
|
||||
//
|
||||
// TODO: Maybe keep the pixmap as large as the window.
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/render"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
"log"
|
||||
)
|
||||
|
||||
func F64ToFixed(f float64) render.Fixed { return render.Fixed(f * 65536) }
|
||||
func FixedToF64(f render.Fixed) float64 { return float64(f) / 65536 }
|
||||
|
||||
func findPictureFormat(formats []render.Pictforminfo,
|
||||
depth byte, direct render.Directformat) render.Pictformat {
|
||||
for _, pf := range formats {
|
||||
if pf.Depth == depth && pf.Direct == direct {
|
||||
return pf.Id
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func createNewPicture(X *xgb.Conn, depth byte, drawable xproto.Drawable,
|
||||
width uint16, height uint16, format render.Pictformat) render.Picture {
|
||||
pixmapid, err := xproto.NewPixmapId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
_ = xproto.CreatePixmap(X, depth, pixmapid, drawable, width, height)
|
||||
|
||||
pictid, err := render.NewPictureId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
_ = render.CreatePicture(X, pictid, xproto.Drawable(pixmapid), format,
|
||||
0, []uint32{})
|
||||
return pictid
|
||||
}
|
||||
|
||||
func main() {
|
||||
X, err := xgb.NewConn()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err := render.Init(X); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
setup := xproto.Setup(X)
|
||||
screen := setup.DefaultScreen(X)
|
||||
|
||||
visual, depth := screen.RootVisual, screen.RootDepth
|
||||
if depth < 24 {
|
||||
log.Fatalln("need more colors")
|
||||
}
|
||||
|
||||
wid, err := xproto.NewWindowId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
_ = xproto.CreateWindow(X, depth, wid, screen.Root,
|
||||
0, 0, 500, 500, 0, xproto.WindowClassInputOutput,
|
||||
visual, xproto.CwBackPixel|xproto.CwEventMask,
|
||||
[]uint32{0xffffffff, xproto.EventMaskButtonPress |
|
||||
xproto.EventMaskButtonMotion | xproto.EventMaskButtonRelease |
|
||||
xproto.EventMaskStructureNotify | xproto.EventMaskExposure})
|
||||
|
||||
title := []byte("Draw")
|
||||
_ = xproto.ChangeProperty(X, xproto.PropModeReplace, wid, xproto.AtomWmName,
|
||||
xproto.AtomString, 8, uint32(len(title)), title)
|
||||
|
||||
_ = xproto.MapWindow(X, wid)
|
||||
|
||||
pformats, err := render.QueryPictFormats(X).Reply()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Find appropriate picture formats.
|
||||
var pformat, pformatAlpha, pformatRGB render.Pictformat
|
||||
for _, pd := range pformats.Screens[X.DefaultScreen].Depths {
|
||||
for _, pv := range pd.Visuals {
|
||||
if pv.Visual == visual {
|
||||
pformat = pv.Format
|
||||
}
|
||||
}
|
||||
}
|
||||
if pformatAlpha = findPictureFormat(pformats.Formats, 8,
|
||||
render.Directformat{
|
||||
AlphaShift: 0,
|
||||
AlphaMask: 0xff,
|
||||
}); pformat == 0 {
|
||||
log.Fatalln("required picture format not found")
|
||||
}
|
||||
if pformatRGB = findPictureFormat(pformats.Formats, 24,
|
||||
render.Directformat{
|
||||
RedShift: 16,
|
||||
RedMask: 0xff,
|
||||
GreenShift: 8,
|
||||
GreenMask: 0xff,
|
||||
BlueShift: 0,
|
||||
BlueMask: 0xff,
|
||||
}); pformatRGB == 0 {
|
||||
log.Fatalln("required picture format not found")
|
||||
}
|
||||
|
||||
// Picture for the window.
|
||||
pid, err := render.NewPictureId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
render.CreatePicture(X, pid, xproto.Drawable(wid), pformat, 0, []uint32{})
|
||||
|
||||
// Brush shape.
|
||||
const brushRadius = 5
|
||||
|
||||
brushid, err := render.NewPictureId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
cFull := render.Color{0xffff, 0xffff, 0xffff, 0xffff}
|
||||
cTrans := render.Color{0xffff, 0xffff, 0xffff, 0}
|
||||
_ = render.CreateRadialGradient(X, brushid,
|
||||
render.Pointfix{F64ToFixed(brushRadius), F64ToFixed(brushRadius)},
|
||||
render.Pointfix{F64ToFixed(brushRadius), F64ToFixed(brushRadius)},
|
||||
F64ToFixed(0),
|
||||
F64ToFixed(brushRadius),
|
||||
3, []render.Fixed{F64ToFixed(0), F64ToFixed(0.1), F64ToFixed(1)},
|
||||
[]render.Color{cFull, cFull, cTrans})
|
||||
|
||||
// Brush color.
|
||||
colorid, err := render.NewPictureId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
_ = render.CreateSolidFill(X, colorid, render.Color{
|
||||
Red: 0x4444,
|
||||
Green: 0x8888,
|
||||
Blue: 0xffff,
|
||||
Alpha: 0xffff,
|
||||
})
|
||||
|
||||
// Various pixmaps.
|
||||
const (
|
||||
pixWidth = 1000
|
||||
pixHeight = 1000
|
||||
)
|
||||
|
||||
canvasid := createNewPicture(X, 24,
|
||||
xproto.Drawable(screen.Root), pixWidth, pixHeight, pformatRGB)
|
||||
bufferid := createNewPicture(X, 24,
|
||||
xproto.Drawable(screen.Root), pixWidth, pixHeight, pformatRGB)
|
||||
maskid := createNewPicture(X, 8,
|
||||
xproto.Drawable(screen.Root), pixWidth, pixHeight, pformatAlpha)
|
||||
|
||||
// Smoothing by way of blur, apparently a misguided idea.
|
||||
/*
|
||||
_ = render.SetPictureFilter(X, maskid,
|
||||
uint16(len("convolution")), "convolution",
|
||||
[]render.Fixed{F64ToFixed(3), F64ToFixed(3),
|
||||
F64ToFixed(0), F64ToFixed(0.15), F64ToFixed(0),
|
||||
F64ToFixed(0.15), F64ToFixed(0.40), F64ToFixed(0.15),
|
||||
F64ToFixed(0), F64ToFixed(0.15), F64ToFixed(0)})
|
||||
*/
|
||||
|
||||
// Pixmaps come uninitialized.
|
||||
_ = render.FillRectangles(X,
|
||||
render.PictOpSrc, canvasid, render.Color{
|
||||
Red: 0xffff, Green: 0xffff, Blue: 0xffff, Alpha: 0xffff,
|
||||
}, []xproto.Rectangle{{Width: pixWidth, Height: pixHeight}})
|
||||
|
||||
// This is the only method we can use to render brush strokes without
|
||||
// alpha accumulation due to stamping. Though this also seems to be
|
||||
// misguided. Keeping it here for educational purposes.
|
||||
//
|
||||
// ConjointOver is defined as: A = Aa * 1 + Ab * max(1-Aa/Ab,0)
|
||||
// which basically resolves to: A = max(Aa, Ab)
|
||||
// which equals "lighten" with one channel only.
|
||||
//
|
||||
// Resources:
|
||||
// - https://www.cairographics.org/operators/
|
||||
// - http://ssp.impulsetrain.com/porterduff.html
|
||||
// - https://keithp.com/~keithp/talks/renderproblems/renderproblems/render-title.html
|
||||
// - https://keithp.com/~keithp/talks/cairo2003.pdf
|
||||
drawPointAt := func(x, y int16) {
|
||||
_ = render.Composite(X, render.PictOpConjointOver,
|
||||
brushid, render.PictureNone, maskid,
|
||||
0, 0, 0, 0, x-brushRadius, y-brushRadius,
|
||||
brushRadius*2, brushRadius*2)
|
||||
|
||||
_ = render.SetPictureClipRectangles(X, bufferid,
|
||||
x-brushRadius, y-brushRadius, []xproto.Rectangle{
|
||||
{Width: brushRadius * 2, Height: brushRadius * 2}})
|
||||
_ = render.Composite(X, render.PictOpSrc,
|
||||
canvasid, render.PictureNone, bufferid,
|
||||
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
|
||||
pixWidth, pixHeight)
|
||||
_ = render.Composite(X, render.PictOpOver,
|
||||
colorid, maskid, bufferid,
|
||||
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
|
||||
pixWidth, pixHeight)
|
||||
|
||||
// Composited, now blit to window without flicker.
|
||||
_ = render.SetPictureClipRectangles(X, pid,
|
||||
x-brushRadius, y-brushRadius, []xproto.Rectangle{
|
||||
{Width: brushRadius * 2, Height: brushRadius * 2}})
|
||||
_ = render.Composite(X, render.PictOpSrc,
|
||||
bufferid, render.PictureNone, pid,
|
||||
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
|
||||
pixWidth, pixHeight)
|
||||
}
|
||||
|
||||
// Integer version of Bresenham's line drawing algorithm
|
||||
drawLine := func(x0, y0, x1, y1 int16) {
|
||||
dx, dy := x1-x0, y1-y0
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
}
|
||||
if dy < 0 {
|
||||
dy = -dy
|
||||
}
|
||||
|
||||
steep := dx < dy
|
||||
if steep {
|
||||
// Flip the coordinate system on input
|
||||
x0, y0 = y0, x0
|
||||
x1, y1 = y1, x1
|
||||
dx, dy = dy, dx
|
||||
}
|
||||
|
||||
var stepX, stepY int16 = 1, 1
|
||||
if x0 > x1 {
|
||||
stepX = -1
|
||||
}
|
||||
if y0 > y1 {
|
||||
stepY = -1
|
||||
}
|
||||
|
||||
dpr := dy * 2
|
||||
delta := dpr - dx
|
||||
dpru := delta - dx
|
||||
|
||||
for ; dx > 0; dx-- {
|
||||
// Unflip the coordinate system on output
|
||||
if steep {
|
||||
drawPointAt(y0, x0)
|
||||
} else {
|
||||
drawPointAt(x0, y0)
|
||||
}
|
||||
|
||||
x0 += stepX
|
||||
if delta > 0 {
|
||||
y0 += stepY
|
||||
delta += dpru
|
||||
} else {
|
||||
delta += dpr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var startX, startY int16 = 0, 0
|
||||
drawing := false
|
||||
for {
|
||||
ev, xerr := X.WaitForEvent()
|
||||
if xerr != nil {
|
||||
log.Printf("Error: %s\n", xerr)
|
||||
return
|
||||
}
|
||||
if ev == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Event: %s\n", ev)
|
||||
switch e := ev.(type) {
|
||||
case xproto.UnmapNotifyEvent:
|
||||
return
|
||||
|
||||
case xproto.ExposeEvent:
|
||||
_ = render.SetPictureClipRectangles(X, pid, int16(e.X), int16(e.Y),
|
||||
[]xproto.Rectangle{{Width: e.Width, Height: e.Height}})
|
||||
|
||||
// Not bothering to deflicker here using the buffer pixmap,
|
||||
// with compositing this event is rare enough.
|
||||
_ = render.Composite(X, render.PictOpSrc,
|
||||
canvasid, render.PictureNone, pid,
|
||||
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
|
||||
pixWidth, pixHeight)
|
||||
|
||||
if drawing {
|
||||
_ = render.Composite(X, render.PictOpOver,
|
||||
colorid, maskid, pid,
|
||||
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
|
||||
pixWidth, pixHeight)
|
||||
}
|
||||
|
||||
case xproto.ButtonPressEvent:
|
||||
if e.Detail == xproto.ButtonIndex1 {
|
||||
render.FillRectangles(X,
|
||||
render.PictOpSrc, maskid, render.Color{},
|
||||
[]xproto.Rectangle{{Width: pixWidth, Height: pixHeight}})
|
||||
|
||||
drawing = true
|
||||
drawPointAt(e.EventX, e.EventY)
|
||||
startX, startY = e.EventX, e.EventY
|
||||
}
|
||||
|
||||
case xproto.MotionNotifyEvent:
|
||||
if drawing {
|
||||
drawLine(startX, startY, e.EventX, e.EventY)
|
||||
startX, startY = e.EventX, e.EventY
|
||||
}
|
||||
|
||||
case xproto.ButtonReleaseEvent:
|
||||
if e.Detail == xproto.ButtonIndex1 {
|
||||
_ = render.Composite(X, render.PictOpOver,
|
||||
colorid, maskid, canvasid,
|
||||
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
|
||||
pixWidth, pixHeight)
|
||||
|
||||
drawing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
361
prototypes/xgb-image.go
Normal file
361
prototypes/xgb-image.go
Normal file
@@ -0,0 +1,361 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/render"
|
||||
"github.com/BurntSushi/xgb/shm"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"image"
|
||||
"image/color"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
// #include <sys/ipc.h>
|
||||
// #include <sys/shm.h>
|
||||
import "C"
|
||||
|
||||
func F64ToFixed(f float64) render.Fixed { return render.Fixed(f * 65536) }
|
||||
func FixedToF64(f render.Fixed) float64 { return float64(f) / 65536 }
|
||||
|
||||
var formats = map[byte]struct {
|
||||
format render.Directformat
|
||||
transform func(color.Color) uint32
|
||||
}{
|
||||
32: {
|
||||
format: render.Directformat{
|
||||
RedShift: 16,
|
||||
RedMask: 0xff,
|
||||
GreenShift: 8,
|
||||
GreenMask: 0xff,
|
||||
BlueShift: 0,
|
||||
BlueMask: 0xff,
|
||||
AlphaShift: 24,
|
||||
AlphaMask: 0xff,
|
||||
},
|
||||
transform: func(color color.Color) uint32 {
|
||||
r, g, b, a := color.RGBA()
|
||||
return (a>>8)<<24 | (r>>8)<<16 | (g>>8)<<8 | (b >> 8)
|
||||
},
|
||||
},
|
||||
30: {
|
||||
/*
|
||||
// Alpha makes compositing unbearably slow.
|
||||
AlphaShift: 30,
|
||||
AlphaMask: 0x3,
|
||||
*/
|
||||
format: render.Directformat{
|
||||
RedShift: 20,
|
||||
RedMask: 0x3ff,
|
||||
GreenShift: 10,
|
||||
GreenMask: 0x3ff,
|
||||
BlueShift: 0,
|
||||
BlueMask: 0x3ff,
|
||||
},
|
||||
transform: func(color color.Color) uint32 {
|
||||
r, g, b, a := color.RGBA()
|
||||
return (a>>14)<<30 | (r>>6)<<20 | (g>>6)<<10 | (b >> 6)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
/*
|
||||
pf, err := os.Create("pprof.out")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
pprof.StartCPUProfile(pf)
|
||||
defer pprof.StopCPUProfile()
|
||||
*/
|
||||
|
||||
// Load a picture from the command line.
|
||||
f, err := os.Open(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
img, name, err := image.Decode(f)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Println("image type is", name)
|
||||
|
||||
// Miscellaneous X11 initialization.
|
||||
X, err := xgb.NewConn()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if err := render.Init(X); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
setup := xproto.Setup(X)
|
||||
screen := setup.DefaultScreen(X)
|
||||
|
||||
visual, depth := screen.RootVisual, screen.RootDepth
|
||||
|
||||
// Only go for 10-bit when the picture can make use of that range.
|
||||
prefer30 := false
|
||||
switch img.(type) {
|
||||
case *image.Gray16, *image.RGBA64, *image.NRGBA64:
|
||||
prefer30 = true
|
||||
}
|
||||
|
||||
// XXX: We don't /need/ alpha here, it's just a minor improvement--affects
|
||||
// the backpixel value. (And we reject it in 30-bit depth anyway.)
|
||||
Depths:
|
||||
for _, i := range screen.AllowedDepths {
|
||||
for _, v := range i.Visuals {
|
||||
// TODO: Could/should check other parameters, e.g., the RGB masks.
|
||||
if v.Class != xproto.VisualClassTrueColor {
|
||||
continue
|
||||
}
|
||||
if i.Depth == 32 || i.Depth == 30 && prefer30 {
|
||||
visual, depth = v.VisualId, i.Depth
|
||||
if !prefer30 || i.Depth == 30 {
|
||||
break Depths
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
format, ok := formats[depth]
|
||||
if !ok {
|
||||
log.Fatalln("unsupported bit depth")
|
||||
}
|
||||
|
||||
mid, err := xproto.NewColormapId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
_ = xproto.CreateColormap(
|
||||
X, xproto.ColormapAllocNone, mid, screen.Root, visual)
|
||||
|
||||
wid, err := xproto.NewWindowId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Border pixel and colormap are required when depth differs from parent.
|
||||
_ = xproto.CreateWindow(X, depth, wid, screen.Root,
|
||||
0, 0, 500, 500, 0, xproto.WindowClassInputOutput,
|
||||
visual, xproto.CwBackPixel|xproto.CwBorderPixel|xproto.CwEventMask|
|
||||
xproto.CwColormap, []uint32{format.transform(color.Alpha{0x80}), 0,
|
||||
xproto.EventMaskStructureNotify | xproto.EventMaskExposure,
|
||||
uint32(mid)})
|
||||
|
||||
title := []byte("Image")
|
||||
_ = xproto.ChangeProperty(X, xproto.PropModeReplace, wid, xproto.AtomWmName,
|
||||
xproto.AtomString, 8, uint32(len(title)), title)
|
||||
|
||||
_ = xproto.MapWindow(X, wid)
|
||||
|
||||
pformats, err := render.QueryPictFormats(X).Reply()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Similar to XRenderFindVisualFormat.
|
||||
// The DefaultScreen is almost certain to be zero.
|
||||
var pformat render.Pictformat
|
||||
for _, pd := range pformats.Screens[X.DefaultScreen].Depths {
|
||||
// This check seems to be slightly extraneous.
|
||||
if pd.Depth != depth {
|
||||
continue
|
||||
}
|
||||
for _, pv := range pd.Visuals {
|
||||
if pv.Visual == visual {
|
||||
pformat = pv.Format
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the window's surface in a picture.
|
||||
pid, err := render.NewPictureId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
render.CreatePicture(X, pid, xproto.Drawable(wid), pformat, 0, []uint32{})
|
||||
|
||||
// setup.BitmapFormatScanline{Pad,Unit} and setup.BitmapFormatBitOrder
|
||||
// don't interest us here since we're only using Z format pixmaps.
|
||||
for _, pf := range setup.PixmapFormats {
|
||||
if pf.Depth == depth {
|
||||
if pf.BitsPerPixel != 32 || pf.ScanlinePad != 32 {
|
||||
log.Fatalln("unsuported X server")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pixid, err := xproto.NewPixmapId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
_ = xproto.CreatePixmap(X, depth, pixid, xproto.Drawable(screen.Root),
|
||||
uint16(img.Bounds().Dx()), uint16(img.Bounds().Dy()))
|
||||
|
||||
var bgraFormat render.Pictformat
|
||||
for _, pf := range pformats.Formats {
|
||||
if pf.Depth == depth && pf.Direct == format.format {
|
||||
bgraFormat = pf.Id
|
||||
break
|
||||
}
|
||||
}
|
||||
if bgraFormat == 0 {
|
||||
log.Fatalln("picture format not found")
|
||||
}
|
||||
|
||||
// We could also look for the inverse pictformat.
|
||||
var encoding binary.ByteOrder
|
||||
if setup.ImageByteOrder == xproto.ImageOrderMSBFirst {
|
||||
encoding = binary.BigEndian
|
||||
} else {
|
||||
encoding = binary.LittleEndian
|
||||
}
|
||||
|
||||
pixpicid, err := render.NewPictureId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
render.CreatePicture(X, pixpicid, xproto.Drawable(pixid), bgraFormat,
|
||||
0, []uint32{})
|
||||
|
||||
// Do we really need this? :/
|
||||
cid, err := xproto.NewGcontextId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
_ = xproto.CreateGC(X, cid, xproto.Drawable(pixid),
|
||||
xproto.GcGraphicsExposures, []uint32{0})
|
||||
|
||||
bounds := img.Bounds()
|
||||
Lstart := time.Now()
|
||||
|
||||
if err := shm.Init(X); err != nil {
|
||||
log.Println("MIT-SHM unavailable")
|
||||
|
||||
// We're being lazy and resolve the 1<<16 limit of requests by sending
|
||||
// a row at a time. The encoding is also done inefficiently.
|
||||
// Also see xgbutil/xgraphics/xsurface.go.
|
||||
row := make([]byte, bounds.Dx()*4)
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
encoding.PutUint32(row[x*4:], format.transform(img.At(x, y)))
|
||||
}
|
||||
_ = xproto.PutImage(X, xproto.ImageFormatZPixmap,
|
||||
xproto.Drawable(pixid), cid, uint16(bounds.Dx()), 1,
|
||||
0, int16(y),
|
||||
0, depth, row)
|
||||
}
|
||||
} else {
|
||||
rep, err := shm.QueryVersion(X).Reply()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if rep.PixmapFormat != xproto.ImageFormatZPixmap ||
|
||||
!rep.SharedPixmaps {
|
||||
log.Fatalln("MIT-SHM configuration unfit")
|
||||
}
|
||||
|
||||
shmSize := bounds.Dx() * bounds.Dy() * 4
|
||||
|
||||
// As a side note, to clean up unreferenced segments (orphans):
|
||||
// ipcs -m | awk '$6 == "0" { print $2 }' | xargs ipcrm shm
|
||||
shmID := int(C.shmget(C.IPC_PRIVATE,
|
||||
C.size_t(shmSize), C.IPC_CREAT|0777))
|
||||
if shmID == -1 {
|
||||
// TODO: We should handle this case by falling back to PutImage,
|
||||
// if only because the allocation may hit a system limit.
|
||||
log.Fatalln("memory allocation failed")
|
||||
}
|
||||
|
||||
dataRaw := C.shmat(C.int(shmID), nil, 0)
|
||||
defer C.shmdt(dataRaw)
|
||||
defer C.shmctl(C.int(shmID), C.IPC_RMID, nil)
|
||||
|
||||
data := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(dataRaw), Len: shmSize, Cap: shmSize}))
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
row := data[y*bounds.Dx()*4:]
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
encoding.PutUint32(row[x*4:], format.transform(img.At(x, y)))
|
||||
}
|
||||
}
|
||||
|
||||
segid, err := shm.NewSegId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Need to have it attached on the server before we unload the segment.
|
||||
c := shm.AttachChecked(X, segid, uint32(shmID), true /* RO */)
|
||||
if err := c.Check(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
_ = shm.PutImage(X, xproto.Drawable(pixid), cid,
|
||||
uint16(bounds.Dx()), uint16(bounds.Dy()), 0, 0,
|
||||
uint16(bounds.Dx()), uint16(bounds.Dy()), 0, 0,
|
||||
depth, xproto.ImageFormatZPixmap,
|
||||
0 /* SendEvent */, segid, 0 /* Offset */)
|
||||
}
|
||||
|
||||
log.Println("uploading took", time.Now().Sub(Lstart))
|
||||
|
||||
var scale float64 = 1
|
||||
for {
|
||||
ev, xerr := X.WaitForEvent()
|
||||
if xerr != nil {
|
||||
log.Printf("Error: %s\n", xerr)
|
||||
return
|
||||
}
|
||||
if ev == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Event: %s\n", ev)
|
||||
switch e := ev.(type) {
|
||||
case xproto.UnmapNotifyEvent:
|
||||
return
|
||||
|
||||
case xproto.ConfigureNotifyEvent:
|
||||
w, h := e.Width, e.Height
|
||||
|
||||
scaleX := float64(bounds.Dx()) / float64(w)
|
||||
scaleY := float64(bounds.Dy()) / float64(h)
|
||||
|
||||
if scaleX < scaleY {
|
||||
scale = scaleY
|
||||
} else {
|
||||
scale = scaleX
|
||||
}
|
||||
|
||||
_ = render.SetPictureTransform(X, pixpicid, render.Transform{
|
||||
F64ToFixed(scale), F64ToFixed(0), F64ToFixed(0),
|
||||
F64ToFixed(0), F64ToFixed(scale), F64ToFixed(0),
|
||||
F64ToFixed(0), F64ToFixed(0), F64ToFixed(1),
|
||||
})
|
||||
_ = render.SetPictureFilter(X, pixpicid, 8, "bilinear", nil)
|
||||
|
||||
case xproto.ExposeEvent:
|
||||
_ = render.Composite(X, render.PictOpSrc,
|
||||
pixpicid, render.PictureNone, pid,
|
||||
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
|
||||
uint16(float64(img.Bounds().Dx())/scale),
|
||||
uint16(float64(img.Bounds().Dy())/scale))
|
||||
}
|
||||
}
|
||||
}
|
||||
213
prototypes/xgb-keys.go
Normal file
213
prototypes/xgb-keys.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
//"github.com/BurntSushi/xgb/xkb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
X, err := xgb.NewConn()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
/*
|
||||
// Use the extension if available, makes better use of state bits.
|
||||
if err := xkb.Init(X); err == nil {
|
||||
if _, err := xkb.UseExtension(X, 1, 0).Reply(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
setup := xproto.Setup(X)
|
||||
screen := setup.DefaultScreen(X)
|
||||
|
||||
visual, depth := screen.RootVisual, screen.RootDepth
|
||||
// TODO: We should check that we find it, though we don't /need/ alpha here,
|
||||
// it's just a minor improvement--affects the backpixel value.
|
||||
for _, i := range screen.AllowedDepths {
|
||||
for _, v := range i.Visuals {
|
||||
// TODO: Could/should check other parameters.
|
||||
if i.Depth == 32 && v.Class == xproto.VisualClassTrueColor {
|
||||
visual, depth = v.VisualId, i.Depth
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mid, err := xproto.NewColormapId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
_ = xproto.CreateColormap(
|
||||
X, xproto.ColormapAllocNone, mid, screen.Root, visual)
|
||||
|
||||
wid, err := xproto.NewWindowId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Border pixel and colormap are required when depth differs from parent.
|
||||
_ = xproto.CreateWindow(X, depth, wid, screen.Root,
|
||||
0, 0, 500, 500, 0, xproto.WindowClassInputOutput,
|
||||
visual, xproto.CwBackPixel|xproto.CwBorderPixel|xproto.CwEventMask|
|
||||
xproto.CwColormap, []uint32{0x80808080, 0,
|
||||
xproto.EventMaskStructureNotify | xproto.EventMaskKeyPress |
|
||||
/* KeymapNotify */ xproto.EventMaskKeymapState, uint32(mid)})
|
||||
|
||||
title := []byte("Keys")
|
||||
_ = xproto.ChangeProperty(X, xproto.PropModeReplace, wid, xproto.AtomWmName,
|
||||
xproto.AtomString, 8, uint32(len(title)), title)
|
||||
|
||||
_ = xproto.MapWindow(X, wid)
|
||||
|
||||
mapping, err := xproto.GetKeyboardMapping(X, setup.MinKeycode,
|
||||
byte(setup.MaxKeycode-setup.MinKeycode+1)).Reply()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// The order is "Shift, Lock, Control, Mod1, Mod2, Mod3, Mod4, and Mod5."
|
||||
mm, err := xproto.GetModifierMapping(X).Reply()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// XXX: This seems pointless, the key will just end up switching groups
|
||||
// instead of levels without full XKB handling. Though perhaps it might
|
||||
// at least work as intended when there's only one XKB group.
|
||||
const MODE_SWITCH = 0xff7e
|
||||
|
||||
var modeSwitchMask uint16
|
||||
for mod := 0; mod < 8; mod++ {
|
||||
perMod := int(mm.KeycodesPerModifier)
|
||||
for _, kc := range mm.Keycodes[mod*perMod : (mod+1)*perMod] {
|
||||
if kc == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
perKc := int(mapping.KeysymsPerKeycode)
|
||||
k := int(kc - setup.MinKeycode)
|
||||
for _, ks := range mapping.Keysyms[k*perKc : (k+1)*perKc] {
|
||||
if ks == MODE_SWITCH {
|
||||
modeSwitchMask |= 1 << uint(mod)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
ev, xerr := X.WaitForEvent()
|
||||
if xerr != nil {
|
||||
log.Printf("Error: %s\n", xerr)
|
||||
return
|
||||
}
|
||||
if ev == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Event: %s\n", ev)
|
||||
switch e := ev.(type) {
|
||||
case xproto.UnmapNotifyEvent:
|
||||
return
|
||||
|
||||
case xproto.KeymapNotifyEvent:
|
||||
// e.Keys is a 32 * 8 large bitmap indicating which keys are
|
||||
// currently pressed down. This is sent "after every EnterNotify
|
||||
// and FocusIn" but it also seems to fire when the keyboard layout
|
||||
// changes. aixterm manual even speaks of that explicitly.
|
||||
//
|
||||
// But since changing the effective group involves no changes to
|
||||
// the compatibility mapping, there's nothing for us to do.
|
||||
|
||||
case xproto.MappingNotifyEvent:
|
||||
// e.FirstKeyCode .. e.Count changes have happened but rereading
|
||||
// everything is the simpler thing to do.
|
||||
mapping, err = xproto.GetKeyboardMapping(X, setup.MinKeycode,
|
||||
byte(setup.MaxKeycode-setup.MinKeycode+1)).Reply()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// TODO: We should also repeat the search for MODE SWITCH.
|
||||
|
||||
case xproto.KeyPressEvent:
|
||||
step := int(mapping.KeysymsPerKeycode)
|
||||
from := int(e.Detail-setup.MinKeycode) * step
|
||||
ks := mapping.Keysyms[from : from+step]
|
||||
|
||||
// Strip trailing NoSymbol entries.
|
||||
for len(ks) > 0 && ks[len(ks)-1] == 0 {
|
||||
ks = ks[:len(ks)-1]
|
||||
}
|
||||
|
||||
// Expand back to at least 4.
|
||||
switch {
|
||||
case len(ks) == 1:
|
||||
ks = append(ks, 0, ks[0], 0)
|
||||
case len(ks) == 2:
|
||||
ks = append(ks, ks[0], ks[1])
|
||||
case len(ks) == 3:
|
||||
ks = append(ks, 0)
|
||||
}
|
||||
|
||||
// Other silly expansion rules, only applied to basic ASCII.
|
||||
if ks[1] == 0 {
|
||||
ks[1] = ks[0]
|
||||
if ks[0] >= 'A' && ks[0] <= 'Z' ||
|
||||
ks[0] >= 'a' && ks[0] <= 'z' {
|
||||
ks[0] = ks[0] | 32
|
||||
ks[1] = ks[0] &^ 32
|
||||
}
|
||||
}
|
||||
|
||||
if ks[3] == 0 {
|
||||
ks[3] = ks[2]
|
||||
if ks[2] >= 'A' && ks[2] <= 'Z' ||
|
||||
ks[2] >= 'a' && ks[2] <= 'z' {
|
||||
ks[2] = ks[2] | 32
|
||||
ks[3] = ks[2] &^ 32
|
||||
}
|
||||
}
|
||||
|
||||
// We only have enough information to switch between two groups.
|
||||
offset := 0
|
||||
if e.State&modeSwitchMask != 0 {
|
||||
offset += 2
|
||||
}
|
||||
|
||||
var result xproto.Keysym
|
||||
|
||||
shift := e.State&xproto.ModMaskShift != 0
|
||||
lock := e.State&xproto.ModMaskLock != 0
|
||||
switch {
|
||||
case !shift && !lock:
|
||||
result = ks[offset+0]
|
||||
case !shift && lock:
|
||||
if ks[offset+0] >= 'a' && ks[offset+0] <= 'z' {
|
||||
result = ks[offset+1]
|
||||
} else {
|
||||
result = ks[offset+0]
|
||||
}
|
||||
case shift && lock:
|
||||
if ks[offset+1] >= 'a' && ks[offset+1] <= 'z' {
|
||||
result = ks[offset+1] &^ 32
|
||||
} else {
|
||||
result = ks[offset+1]
|
||||
}
|
||||
case shift:
|
||||
result = ks[offset+1]
|
||||
}
|
||||
|
||||
if result <= 0xff {
|
||||
log.Printf("%c (Latin-1)\n", rune(result))
|
||||
} else {
|
||||
log.Println(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
prototypes/xgb-monitors.go
Normal file
40
prototypes/xgb-monitors.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
"log"
|
||||
|
||||
// Needs a patched local version with xcb-proto 1.12 and this fix:
|
||||
// -size := xgb.Pad((8 + (24 + xgb.Pad((int(NOutput) * 4)))))
|
||||
// +size := xgb.Pad((8 + (24 + xgb.Pad((int(Monitorinfo.NOutput) * 4)))))
|
||||
"github.com/BurntSushi/xgb/randr"
|
||||
)
|
||||
|
||||
func main() {
|
||||
X, err := xgb.NewConn()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if err := randr.Init(X); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
setup := xproto.Setup(X)
|
||||
screen := setup.DefaultScreen(X)
|
||||
|
||||
ms, err := randr.GetMonitors(X, screen.Root, true /* GetActive */).Reply()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for _, m := range ms.Monitors {
|
||||
reply, err := xproto.GetAtomName(X, m.Name).Reply()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
log.Printf("Monitor %s %+v\n", reply.Name, m)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
538
prototypes/xgb-text-viewer.go
Normal file
538
prototypes/xgb-text-viewer.go
Normal file
@@ -0,0 +1,538 @@
|
||||
// This is an amalgamation of xgb-xrender.go and xgb-keys.go and more of a demo,
|
||||
// some comments have been stripped.
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/render"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
"github.com/golang/freetype"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/gofont/goregular"
|
||||
"golang.org/x/image/math/fixed"
|
||||
"image"
|
||||
"image/draw"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func glyphListBytes(buf []byte, runes []rune, size int) int {
|
||||
b := 0
|
||||
for _, r := range runes {
|
||||
switch size {
|
||||
default:
|
||||
buf[b] = byte(r)
|
||||
b += 1
|
||||
case 2:
|
||||
xgb.Put16(buf[b:], uint16(r))
|
||||
b += 2
|
||||
case 4:
|
||||
xgb.Put32(buf[b:], uint32(r))
|
||||
b += 4
|
||||
}
|
||||
}
|
||||
return xgb.Pad(b)
|
||||
}
|
||||
|
||||
// When the len is 255, a GLYPHABLE follows, otherwise a list of CARD8/16/32.
|
||||
func glyphEltHeaderBytes(buf []byte, len byte, deltaX, deltaY int16) int {
|
||||
b := 0
|
||||
buf[b] = len
|
||||
b += 4
|
||||
xgb.Put16(buf[b:], uint16(deltaX))
|
||||
b += 2
|
||||
xgb.Put16(buf[b:], uint16(deltaY))
|
||||
b += 2
|
||||
return xgb.Pad(b)
|
||||
}
|
||||
|
||||
type xgbCookie interface{ Check() error }
|
||||
|
||||
// compositeString makes an appropriate render.CompositeGlyphs request,
|
||||
// assuming that glyphs equal Unicode codepoints.
|
||||
func compositeString(c *xgb.Conn, op byte, src, dst render.Picture,
|
||||
maskFormat render.Pictformat, glyphset render.Glyphset, srcX, srcY int16,
|
||||
destX, destY int16, text string) xgbCookie {
|
||||
runes := []rune(text)
|
||||
|
||||
var highest rune
|
||||
for _, r := range runes {
|
||||
if r > highest {
|
||||
highest = r
|
||||
}
|
||||
}
|
||||
|
||||
size := 1
|
||||
switch {
|
||||
case highest > 1<<16:
|
||||
size = 4
|
||||
case highest > 1<<8:
|
||||
size = 2
|
||||
}
|
||||
|
||||
// They gave up on the XCB protocol API and we need to serialize explicitly.
|
||||
|
||||
// To spare us from caring about the padding, use the largest number lesser
|
||||
// than 255 that is divisible by 4 (for size 2 and 4 the requirements are
|
||||
// less strict but this works in the general case).
|
||||
const maxPerChunk = 252
|
||||
|
||||
buf := make([]byte, (len(runes)+maxPerChunk-1)/maxPerChunk*8+len(runes)*size)
|
||||
b := 0
|
||||
|
||||
for len(runes) > maxPerChunk {
|
||||
b += glyphEltHeaderBytes(buf[b:], maxPerChunk, 0, 0)
|
||||
b += glyphListBytes(buf[b:], runes[:maxPerChunk], size)
|
||||
runes = runes[maxPerChunk:]
|
||||
}
|
||||
if len(runes) > 0 {
|
||||
b += glyphEltHeaderBytes(buf[b:], byte(len(runes)), destX, destY)
|
||||
b += glyphListBytes(buf[b:], runes, size)
|
||||
}
|
||||
|
||||
switch size {
|
||||
default:
|
||||
return render.CompositeGlyphs8(c, op, src, dst, maskFormat, glyphset,
|
||||
srcX, srcY, buf)
|
||||
case 2:
|
||||
return render.CompositeGlyphs16(c, op, src, dst, maskFormat, glyphset,
|
||||
srcX, srcY, buf)
|
||||
case 4:
|
||||
return render.CompositeGlyphs32(c, op, src, dst, maskFormat, glyphset,
|
||||
srcX, srcY, buf)
|
||||
}
|
||||
}
|
||||
|
||||
type textRenderer struct {
|
||||
f *truetype.Font
|
||||
opts *truetype.Options
|
||||
face font.Face
|
||||
|
||||
bounds fixed.Rectangle26_6 // outer bounds for all the font's glyph
|
||||
buf *image.RGBA // rendering buffer
|
||||
|
||||
X *xgb.Conn
|
||||
gsid render.Glyphset
|
||||
loaded map[rune]bool
|
||||
}
|
||||
|
||||
func newTextRenderer(X *xgb.Conn, ttf []byte, opts *truetype.Options) (
|
||||
*textRenderer, error) {
|
||||
pformats, err := render.QueryPictFormats(X).Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We use RGBA here just so that lines are padded to 32 bits.
|
||||
// Since there's no subpixel antialiasing and alpha is premultiplied,
|
||||
// it doesn't even mater that RGBA is interpreted as ARGB or BGRA.
|
||||
var rgbFormat render.Pictformat
|
||||
for _, pf := range pformats.Formats {
|
||||
if pf.Depth == 32 && pf.Direct.AlphaMask != 0 {
|
||||
rgbFormat = pf.Id
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
tr := &textRenderer{opts: opts, X: X, loaded: make(map[rune]bool)}
|
||||
if tr.f, err = freetype.ParseFont(goregular.TTF); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tr.face = truetype.NewFace(tr.f, opts)
|
||||
tr.bounds = tr.f.Bounds(fixed.Int26_6(opts.Size * float64(opts.DPI) *
|
||||
(64.0 / 72.0)))
|
||||
|
||||
if tr.gsid, err = render.NewGlyphsetId(X); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := render.CreateGlyphSetChecked(X, tr.gsid, rgbFormat).
|
||||
Check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tr.buf = image.NewRGBA(image.Rect(
|
||||
+tr.bounds.Min.X.Floor(),
|
||||
-tr.bounds.Min.Y.Floor(),
|
||||
+tr.bounds.Max.X.Ceil(),
|
||||
-tr.bounds.Max.Y.Ceil(),
|
||||
))
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
func (tr *textRenderer) addRune(r rune) bool {
|
||||
dr, mask, maskp, advance, ok := tr.face.Glyph(
|
||||
fixed.P(0, 0) /* subpixel destination location */, r)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(tr.buf.Pix); i++ {
|
||||
tr.buf.Pix[i] = 0
|
||||
}
|
||||
|
||||
// Copying, since there are absolutely no guarantees.
|
||||
draw.Draw(tr.buf, dr, mask, maskp, draw.Src)
|
||||
|
||||
_ = render.AddGlyphs(tr.X, tr.gsid, 1, []uint32{uint32(r)},
|
||||
[]render.Glyphinfo{{
|
||||
Width: uint16(tr.buf.Rect.Size().X),
|
||||
Height: uint16(tr.buf.Rect.Size().Y),
|
||||
X: int16(-tr.bounds.Min.X.Floor()),
|
||||
Y: int16(+tr.bounds.Max.Y.Ceil()),
|
||||
XOff: int16(advance.Ceil()),
|
||||
YOff: int16(0),
|
||||
}}, []byte(tr.buf.Pix))
|
||||
return true
|
||||
}
|
||||
|
||||
func (tr *textRenderer) render(src, dst render.Picture,
|
||||
srcX, srcY, destX, destY int16, text string) xgbCookie {
|
||||
// XXX: You're really supposed to handle tabs differently from this.
|
||||
text = strings.Replace(text, "\t", " ", -1)
|
||||
|
||||
for _, r := range text {
|
||||
if !tr.loaded[r] {
|
||||
tr.addRune(r)
|
||||
tr.loaded[r] = true
|
||||
}
|
||||
}
|
||||
|
||||
return compositeString(tr.X, render.PictOpOver, src, dst,
|
||||
0 /* TODO: mask Pictureformat? */, tr.gsid,
|
||||
srcX, srcY, destX, destY, text)
|
||||
}
|
||||
|
||||
const (
|
||||
ksEscape = 0xff1b
|
||||
ksUp = 0xff52
|
||||
ksDown = 0xff54
|
||||
ksPageUp = 0xff55
|
||||
ksPageDown = 0xff56
|
||||
ksModeSwitch = 0xff7e
|
||||
)
|
||||
|
||||
type keyMapper struct {
|
||||
X *xgb.Conn
|
||||
setup *xproto.SetupInfo
|
||||
mapping *xproto.GetKeyboardMappingReply
|
||||
|
||||
modeSwitchMask uint16
|
||||
}
|
||||
|
||||
func newKeyMapper(X *xgb.Conn) (*keyMapper, error) {
|
||||
m := &keyMapper{X: X, setup: xproto.Setup(X)}
|
||||
if err := m.update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (km *keyMapper) update() error {
|
||||
var err error
|
||||
km.mapping, err = xproto.GetKeyboardMapping(km.X, km.setup.MinKeycode,
|
||||
byte(km.setup.MaxKeycode-km.setup.MinKeycode+1)).Reply()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
km.modeSwitchMask = 0
|
||||
|
||||
// The order is "Shift, Lock, Control, Mod1, Mod2, Mod3, Mod4, and Mod5."
|
||||
mm, err := xproto.GetModifierMapping(km.X).Reply()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
perMod := int(mm.KeycodesPerModifier)
|
||||
perKc := int(km.mapping.KeysymsPerKeycode)
|
||||
for mod := 0; mod < 8; mod++ {
|
||||
for _, kc := range mm.Keycodes[mod*perMod : (mod+1)*perMod] {
|
||||
if kc == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
k := int(kc - km.setup.MinKeycode)
|
||||
for _, ks := range km.mapping.Keysyms[k*perKc : (k+1)*perKc] {
|
||||
if ks == ksModeSwitch {
|
||||
km.modeSwitchMask |= 1 << uint(mod)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (km *keyMapper) decode(e xproto.KeyPressEvent) (result xproto.Keysym) {
|
||||
step := int(km.mapping.KeysymsPerKeycode)
|
||||
from := int(e.Detail-km.setup.MinKeycode) * step
|
||||
ks := km.mapping.Keysyms[from : from+step]
|
||||
|
||||
// Strip trailing NoSymbol entries.
|
||||
for len(ks) > 0 && ks[len(ks)-1] == 0 {
|
||||
ks = ks[:len(ks)-1]
|
||||
}
|
||||
|
||||
// Expand back to at least 4.
|
||||
switch {
|
||||
case len(ks) == 1:
|
||||
ks = append(ks, 0, ks[0], 0)
|
||||
case len(ks) == 2:
|
||||
ks = append(ks, ks[0], ks[1])
|
||||
case len(ks) == 3:
|
||||
ks = append(ks, 0)
|
||||
}
|
||||
|
||||
// Other silly expansion rules, only applied to basic ASCII since we
|
||||
// don't have translation tables to Unicode here for brevity.
|
||||
if ks[1] == 0 {
|
||||
ks[1] = ks[0]
|
||||
if ks[0] >= 'A' && ks[0] <= 'Z' ||
|
||||
ks[0] >= 'a' && ks[0] <= 'z' {
|
||||
ks[0] = ks[0] | 32
|
||||
ks[1] = ks[0] &^ 32
|
||||
}
|
||||
}
|
||||
|
||||
if ks[3] == 0 {
|
||||
ks[3] = ks[2]
|
||||
if ks[2] >= 'A' && ks[2] <= 'Z' ||
|
||||
ks[2] >= 'a' && ks[2] <= 'z' {
|
||||
ks[2] = ks[2] | 32
|
||||
ks[3] = ks[2] &^ 32
|
||||
}
|
||||
}
|
||||
|
||||
offset := 0
|
||||
if e.State&km.modeSwitchMask != 0 {
|
||||
offset += 2
|
||||
}
|
||||
|
||||
shift := e.State&xproto.ModMaskShift != 0
|
||||
lock := e.State&xproto.ModMaskLock != 0
|
||||
switch {
|
||||
case !shift && !lock:
|
||||
result = ks[offset+0]
|
||||
case !shift && lock:
|
||||
if ks[offset+0] >= 'a' && ks[offset+0] <= 'z' {
|
||||
result = ks[offset+1]
|
||||
} else {
|
||||
result = ks[offset+0]
|
||||
}
|
||||
case shift && lock:
|
||||
if ks[offset+1] >= 'a' && ks[offset+1] <= 'z' {
|
||||
result = ks[offset+1] &^ 32
|
||||
} else {
|
||||
result = ks[offset+1]
|
||||
}
|
||||
case shift:
|
||||
result = ks[offset+1]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatalln("no filename given")
|
||||
}
|
||||
|
||||
text, err := ioutil.ReadFile(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
lines := strings.Split(string(text), "\n")
|
||||
|
||||
X, err := xgb.NewConn()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err := render.Init(X); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
setup := xproto.Setup(X)
|
||||
screen := setup.DefaultScreen(X)
|
||||
|
||||
visual, depth := screen.RootVisual, screen.RootDepth
|
||||
// TODO: We should check that we find it, though we don't /need/ alpha here,
|
||||
// it's just a minor improvement--affects the backpixel value.
|
||||
for _, i := range screen.AllowedDepths {
|
||||
// TODO: Could/should check other parameters.
|
||||
for _, v := range i.Visuals {
|
||||
if i.Depth == 32 && v.Class == xproto.VisualClassTrueColor {
|
||||
visual, depth = v.VisualId, i.Depth
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mid, err := xproto.NewColormapId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
_ = xproto.CreateColormap(
|
||||
X, xproto.ColormapAllocNone, mid, screen.Root, visual)
|
||||
|
||||
wid, err := xproto.NewWindowId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
// Border pixel and colormap are required when depth differs from parent.
|
||||
_ = xproto.CreateWindow(X, depth, wid, screen.Root,
|
||||
0, 0, 500, 500, 0, xproto.WindowClassInputOutput,
|
||||
visual, xproto.CwBackPixel|xproto.CwBorderPixel|xproto.CwEventMask|
|
||||
xproto.CwColormap, []uint32{0xf0f0f0f0, 0,
|
||||
xproto.EventMaskStructureNotify | xproto.EventMaskKeyPress |
|
||||
/* KeymapNotify */ xproto.EventMaskKeymapState |
|
||||
xproto.EventMaskExposure | xproto.EventMaskButtonPress,
|
||||
uint32(mid)})
|
||||
|
||||
title := []byte("Viewer")
|
||||
_ = xproto.ChangeProperty(X, xproto.PropModeReplace, wid, xproto.AtomWmName,
|
||||
xproto.AtomString, 8, uint32(len(title)), title)
|
||||
|
||||
_ = xproto.MapWindow(X, wid)
|
||||
|
||||
pformats, err := render.QueryPictFormats(X).Reply()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Similar to XRenderFindVisualFormat.
|
||||
// The DefaultScreen is almost certain to be zero.
|
||||
var pformat render.Pictformat
|
||||
for _, pd := range pformats.Screens[X.DefaultScreen].Depths {
|
||||
// This check seems to be slightly extraneous.
|
||||
if pd.Depth != depth {
|
||||
continue
|
||||
}
|
||||
for _, pv := range pd.Visuals {
|
||||
if pv.Visual == visual {
|
||||
pformat = pv.Format
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pid, err := render.NewPictureId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
render.CreatePicture(X, pid, xproto.Drawable(wid), pformat, 0, []uint32{})
|
||||
|
||||
blackid, err := render.NewPictureId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
_ = render.CreateSolidFill(X, blackid, render.Color{Alpha: 0xffff})
|
||||
|
||||
tr, err := newTextRenderer(X, goregular.TTF, &truetype.Options{
|
||||
Size: 10,
|
||||
DPI: float64(screen.WidthInPixels) /
|
||||
float64(screen.WidthInMillimeters) * 25.4,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
scroll := 0 // index of the top line
|
||||
|
||||
var w, h uint16
|
||||
redraw := func() {
|
||||
y, ascent, step := 5, tr.bounds.Max.Y.Ceil(),
|
||||
tr.bounds.Max.Y.Ceil()-tr.bounds.Min.Y.Floor()
|
||||
for _, line := range lines[scroll:] {
|
||||
if uint16(y) >= h {
|
||||
break
|
||||
}
|
||||
_ = tr.render(blackid, pid, 0, 0, 5, int16(y+ascent), line)
|
||||
y += step
|
||||
}
|
||||
|
||||
vis := float64(h-10) / float64(step)
|
||||
if vis < float64(len(lines)) {
|
||||
length := float64(step) * (vis + 1) * vis / float64(len(lines))
|
||||
start := float64(step) * float64(scroll) * vis / float64(len(lines))
|
||||
|
||||
_ = render.FillRectangles(X, render.PictOpSrc, pid,
|
||||
render.Color{Alpha: 0xffff}, []xproto.Rectangle{{
|
||||
X: int16(w - 15), Y: int16(start),
|
||||
Width: 15, Height: uint16(length + 10)}})
|
||||
}
|
||||
}
|
||||
|
||||
km, err := newKeyMapper(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for {
|
||||
ev, xerr := X.WaitForEvent()
|
||||
if xerr != nil {
|
||||
log.Printf("Error: %s\n", xerr)
|
||||
return
|
||||
}
|
||||
if ev == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch e := ev.(type) {
|
||||
case xproto.UnmapNotifyEvent:
|
||||
return
|
||||
|
||||
case xproto.ConfigureNotifyEvent:
|
||||
w, h = e.Width, e.Height
|
||||
|
||||
case xproto.MappingNotifyEvent:
|
||||
_ = km.update()
|
||||
|
||||
case xproto.KeyPressEvent:
|
||||
_ = xproto.ClearArea(X, true /* ExposeEvent */, wid, 0, 0, w, h)
|
||||
|
||||
const pageJump = 40
|
||||
switch km.decode(e) {
|
||||
case ksEscape:
|
||||
return
|
||||
case ksUp:
|
||||
if scroll >= 1 {
|
||||
scroll--
|
||||
}
|
||||
case ksDown:
|
||||
if scroll+1 < len(lines) {
|
||||
scroll++
|
||||
}
|
||||
case ksPageUp:
|
||||
if scroll >= pageJump {
|
||||
scroll -= pageJump
|
||||
}
|
||||
case ksPageDown:
|
||||
if scroll+pageJump < len(lines) {
|
||||
scroll += pageJump
|
||||
}
|
||||
}
|
||||
|
||||
case xproto.ButtonPressEvent:
|
||||
_ = xproto.ClearArea(X, true /* ExposeEvent */, wid, 0, 0, w, h)
|
||||
|
||||
switch e.Detail {
|
||||
case xproto.ButtonIndex4:
|
||||
if scroll > 0 {
|
||||
scroll--
|
||||
}
|
||||
case xproto.ButtonIndex5:
|
||||
if scroll+1 < len(lines) {
|
||||
scroll++
|
||||
}
|
||||
}
|
||||
|
||||
case xproto.ExposeEvent:
|
||||
// FIXME: The window's context haven't necessarily been destroyed.
|
||||
if e.Count == 0 {
|
||||
redraw()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
156
prototypes/xgb-window.go
Normal file
156
prototypes/xgb-window.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func main() {
|
||||
X, err := xgb.NewConn()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
setup := xproto.Setup(X)
|
||||
screen := setup.DefaultScreen(X)
|
||||
|
||||
var visual xproto.Visualid
|
||||
var depth byte
|
||||
for _, i := range screen.AllowedDepths {
|
||||
if i.Depth == 32 {
|
||||
// TODO: Could/should check other parameters.
|
||||
for _, v := range i.Visuals {
|
||||
if v.Class == xproto.VisualClassTrueColor {
|
||||
visual = v.VisualId
|
||||
depth = i.Depth
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if visual == 0 {
|
||||
log.Fatalln("cannot find an RGBA TrueColor visual")
|
||||
}
|
||||
|
||||
mid, err := xproto.NewColormapId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
_ = xproto.CreateColormap(
|
||||
X, xproto.ColormapAllocNone, mid, screen.Root, visual)
|
||||
|
||||
wid, err := xproto.NewWindowId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Border pixel and colormap are required when depth differs from parent.
|
||||
_ = xproto.CreateWindow(X, depth, wid, screen.Root,
|
||||
0, 0, 500, 500, 0, xproto.WindowClassInputOutput,
|
||||
visual, xproto.CwBorderPixel|xproto.CwColormap,
|
||||
[]uint32{0, uint32(mid)})
|
||||
|
||||
// This could be included in CreateWindow parameters.
|
||||
_ = xproto.ChangeWindowAttributes(X, wid,
|
||||
xproto.CwBackPixel|xproto.CwEventMask, []uint32{0x80808080,
|
||||
xproto.EventMaskStructureNotify | xproto.EventMaskKeyPress |
|
||||
xproto.EventMaskExposure})
|
||||
|
||||
title := "Gradient"
|
||||
_ = xproto.ChangeProperty(X, xproto.PropModeReplace, wid, xproto.AtomWmName,
|
||||
xproto.AtomString, 8, uint32(len(title)), []byte(title))
|
||||
|
||||
_ = xproto.MapWindow(X, wid)
|
||||
|
||||
cid, err := xproto.NewGcontextId(X)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
_ = xproto.CreateGC(X, cid, xproto.Drawable(wid),
|
||||
xproto.GcGraphicsExposures, []uint32{0})
|
||||
|
||||
blend := func(a, b uint32, ratio, gamma float64) uint32 {
|
||||
iratio := 1 - ratio
|
||||
|
||||
fa := math.Pow(float64(a)/255, gamma)
|
||||
fb := math.Pow(float64(b)/255, gamma)
|
||||
|
||||
return uint32(math.Pow(ratio*fa+iratio*fb, 1/gamma)*255) & 0xff
|
||||
}
|
||||
|
||||
// TODO: We could show some text just like we intend to with xgb-xrender.go.
|
||||
|
||||
var w, h uint16
|
||||
var start, end uint32 = 0xabcdef, 0x32ab54
|
||||
gradient := func() {
|
||||
ra, ga, ba := (start>>16)&0xff, (start>>8)&0xff, start&0xff
|
||||
rb, gb, bb := (end>>16)&0xff, (end>>8)&0xff, end&0xff
|
||||
|
||||
var low, high uint16 = 50, h - 50
|
||||
if high > h {
|
||||
return
|
||||
}
|
||||
|
||||
for y := low; y < high; y++ {
|
||||
ratio := float64(y-low) / (float64(high) - float64(low))
|
||||
|
||||
rR := blend(ra, rb, ratio, 2.2)
|
||||
gG := blend(ga, gb, ratio, 2.2)
|
||||
bB := blend(ba, bb, ratio, 2.2)
|
||||
|
||||
_ = xproto.ChangeGC(X, cid, xproto.GcForeground,
|
||||
[]uint32{0xff000000 | rR<<16 | gG<<8 | bB})
|
||||
_ = xproto.PolyLine(X, xproto.CoordModeOrigin, xproto.Drawable(wid),
|
||||
cid, []xproto.Point{
|
||||
{X: 50, Y: int16(y)},
|
||||
{X: int16(w / 2), Y: int16(y)},
|
||||
})
|
||||
|
||||
rR = blend(ra, rb, ratio, 1)
|
||||
gG = blend(ga, gb, ratio, 1)
|
||||
bB = blend(ba, bb, ratio, 1)
|
||||
|
||||
_ = xproto.ChangeGC(X, cid, xproto.GcForeground,
|
||||
[]uint32{0xff000000 | rR<<16 | gG<<8 | bB})
|
||||
_ = xproto.PolyLine(X, xproto.CoordModeOrigin, xproto.Drawable(wid),
|
||||
cid, []xproto.Point{
|
||||
{X: int16(w / 2), Y: int16(y)},
|
||||
{X: int16(w - 50), Y: int16(y)},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
ev, xerr := X.WaitForEvent()
|
||||
if xerr != nil {
|
||||
log.Printf("Error: %s\n", xerr)
|
||||
return
|
||||
}
|
||||
if ev == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Event: %s\n", ev)
|
||||
switch e := ev.(type) {
|
||||
case xproto.UnmapNotifyEvent:
|
||||
return
|
||||
|
||||
case xproto.ConfigureNotifyEvent:
|
||||
w, h = e.Width, e.Height
|
||||
|
||||
case xproto.KeyPressEvent:
|
||||
start = rand.Uint32() & 0xffffff
|
||||
end = rand.Uint32() & 0xffffff
|
||||
gradient()
|
||||
|
||||
case xproto.ExposeEvent:
|
||||
gradient()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user