Compare commits
226 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
115b612e47
|
|||
|
821b3f6ab9
|
|||
|
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
|
|||
|
4fa84cc877
|
|||
|
4fc4426587
|
|||
|
9b6c4e7028
|
|||
|
2e427c2cea
|
|||
|
a4239548d3
|
|||
|
a966d8489a
|
|||
|
e0b46a8cff
|
|||
|
a6e6c3aaff
|
|||
|
33969ac86e
|
|||
|
b103d5e2eb
|
|||
|
c75299e1c3
|
|||
|
24f1c4413a
|
|||
|
40370702d4
|
|||
|
5429c0b09d
|
|||
|
b28c20a250
|
|||
|
2dfb4e45d1
|
|||
|
f5def2e579
|
|||
|
b2cd8b46c9
|
|||
|
e8602ee718
|
|||
|
a8f02e0d37
|
|||
|
525734eeb3
|
|||
|
6caa4ab928
|
|||
|
728fa4e548
|
|||
|
b5b64db075
|
|||
| 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 |
+15
-2
@@ -1,2 +1,15 @@
|
||||
*.html
|
||||
gen.sh
|
||||
/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
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
Bad things
|
||||
==========
|
||||
|
||||
Web browsers
|
||||
------------
|
||||
https://drewdevault.com/2020/08/13/Web-browsers-need-to-stop.html +
|
||||
https://macwright.com/2020/08/22/clean-starts-for-the-web.html
|
||||
(but link:GoodThings.html#_asciidoc[fuck Markdown])
|
||||
|
||||
Scrap it all, start anew. Content goes first, presentation second. Don't allow
|
||||
for the inverse to happen. I'm not interested in anyone's animations and gray
|
||||
font.
|
||||
|
||||
GNOME 3
|
||||
-------
|
||||
Removing features, worsening accessibility, making the UI incomprehensible,
|
||||
piling up laughable levels of complexity, politically driven development,
|
||||
everything is wrong here.
|
||||
|
||||
KDE
|
||||
---
|
||||
KDE will always be hideously ugly, it's the one constant in the universe. See a
|
||||
https://www.abclinuxu.cz/images/screenshots/3/7/141073-chyby-v-gui-v-kde-4-3-31785.png[case study].
|
||||
|
||||
Pop-ups
|
||||
-------
|
||||
Anything that shows up and changes the consequences of the user's actions, if it
|
||||
doesn't appear as an _immediate result_ of the user doing something, is pure
|
||||
evil. Sadly, this is sometimes hard to avoid, or the cost is too high.
|
||||
|
||||
Major culprits that make me want to hurt the computer: Firefox, Thunderbird
|
||||
(e.g. certificate problems when your network MITMs you and you haven't run
|
||||
sshuttle yet), Qt Creator (files changed externally).
|
||||
|
||||
Unix terminals
|
||||
--------------
|
||||
Barely tolerable with graphical displays. The character grid must go, not only
|
||||
because variable pitch fonts are infinitely more pleasant to read. See also:
|
||||
https://p.janouch.name/article-tui.html[So you want to make a TUI…]
|
||||
|
||||
The only redeeming quality is how well these work over slow links.
|
||||
|
||||
ASCII
|
||||
-----
|
||||
Outdated, made for teletypes. Unicode mostly just adds more complexity (and
|
||||
characters). For sanity reasons, I'll put up with it and try to avoid the parts
|
||||
I don't like, perhaps even banning certain characters where possible.
|
||||
|
||||
Centralisation
|
||||
--------------
|
||||
Try to build things so that they survive outages--resilient, robust, and even
|
||||
https://en.wikipedia.org/wiki/Antifragility[antifragile]. Admittedly, this
|
||||
principle is hard for humans to follow.
|
||||
|
||||
Speaking of which, project Haven has no public mirrors yet. Not that it matters
|
||||
thus far... but eventually we should at least set up mirrorring to repo.or.cz,
|
||||
which is local.
|
||||
-123
@@ -1,123 +0,0 @@
|
||||
Good things
|
||||
===========
|
||||
|
||||
Windows 95
|
||||
----------
|
||||
Most applications followed
|
||||
https://web.archive.org/web/20200303155748/https://www.ics.uci.edu/~kobsa/courses/ICS104/course-notes/Microsoft_WindowsGuidelines.pdf[design guidelines]
|
||||
or just used the default controls, giving a
|
||||
https://guidebookgallery.org/screenshots/win95[unified and simple UI]
|
||||
that could be reasonably well controlled from the keyboard.
|
||||
|
||||
SerenityOS
|
||||
----------
|
||||
https://github.com/SerenityOS/serenity
|
||||
|
||||
// TODO: Include a screenshot.
|
||||
|
||||
A project with a much larger scope than Haven but with somewhat similar goals,
|
||||
to create a nice new environment. Gives proof that such a project can be
|
||||
written in few people, given proper experience.
|
||||
|
||||
ToaruOS
|
||||
-------
|
||||
https://github.com/klange/toaruos
|
||||
|
||||
Similarly to SerenityOS, this proves that one can write an entire operating
|
||||
system from scratch. Somewhat sadly, it just replicates the Linux desktop
|
||||
without trying to make any fundamental changes whatsoever. Still, it makes for
|
||||
a very interesting data point.
|
||||
|
||||
Haiku
|
||||
-----
|
||||
Pretty. Desktop-first. Just the window manager sucks, and C++ is no longer
|
||||
a reasonable choice for a high-level programming language, despite all the
|
||||
effort to improve it--it's the foundations that are rotten.
|
||||
|
||||
VIM
|
||||
---
|
||||
Customizable modal text editor allowing for rather efficient editing. While the
|
||||
internals might not be so great, the user interface is hard to beat once you get
|
||||
used to it.
|
||||
|
||||
With https://github.com/prabirshrestha/vim-lsp/[vim-lsp] it even shines as an
|
||||
IDE, which is a wonderful development.
|
||||
|
||||
Qt Creator
|
||||
----------
|
||||
// TODO: Include a screenshot.
|
||||
|
||||
Has a very good VIM emulator (rare!), excellent code navigation on top of that,
|
||||
clean UI.
|
||||
|
||||
Primarily a C++ IDE, yet slowly the Language Server Protocol implementation
|
||||
makes it transcend into a general-purpose one.
|
||||
|
||||
Vimperator/Pentadactyl/qutebrowser
|
||||
----------------------------------
|
||||
// TODO: Include a screenshot.
|
||||
|
||||
Vimperator/Pentadactyl created a new standard in web browser usability. The
|
||||
most important innovations being the addition of modality to the web browser and
|
||||
the "follow link" feature, which is what I feel all mouse-first UIs deserve.
|
||||
|
||||
Too bad it can no longer work in current versions of Firefox as they cut off
|
||||
all support for XUL addons. The good times are already behind us. I certainly
|
||||
enjoyed them.
|
||||
|
||||
qutebrowser went somewhat beyond in terms of how neat the integration of a VIM
|
||||
interface and a web browser engine can be, rebuilding the concept on top of
|
||||
Webkit, but to this day it lacks a normal AdBlock, and thus remains a toy.
|
||||
|
||||
SourceHut
|
||||
---------
|
||||
https://sourcehut.org/
|
||||
|
||||
Drew might be an opinionated asshole but that's not necessarily a bad quality,
|
||||
especially if it helps with diversity. His creations are compatible with my
|
||||
values. Except that I hate Python. :)
|
||||
|
||||
WikiWikiWeb
|
||||
-----------
|
||||
http://wiki.c2.com/
|
||||
|
||||
An interesting knowledge base. Simple formatting, automatic links. I've
|
||||
borrowed the general concept for this very wiki (yes, technically you can send
|
||||
in pull requests).
|
||||
|
||||
It's a shame that it's been frozen in time.
|
||||
|
||||
AsciiDoc
|
||||
--------
|
||||
I utterly despise Markdown for its choices, and I don't want to suffer while
|
||||
writing:
|
||||
|
||||
- you have to escape all underscores in words,
|
||||
- the link syntax is hard to remember,
|
||||
- emphasis is screwed-up entirely, asterisks are supposed to mean '`bold`'.
|
||||
|
||||
Granted, I can see some faults in AsciiDoc as well, notably with how hard it is
|
||||
to parse and process, but I haven't found anything better yet, not to mention
|
||||
powerful, and standardisation seems to finally be
|
||||
https://groups.google.com/g/asciidoc/c/EKx-Hfx-nMM[on its way]. One day I might
|
||||
create a processor for a slightly modified subset of it. Until then,
|
||||
https://github.com/bytesparadise/libasciidoc[libasciidoc] is what I'll be using
|
||||
for all markup, and I'm immensely thankful to its authors for their work.
|
||||
|
||||
Plan 9, Go
|
||||
----------
|
||||
Pervasive simplicity. Sometimes it's annoying (one could say '`misplaced`')
|
||||
but overall you get the feeling you have the entire platform under your control.
|
||||
|
||||
Oberon OS
|
||||
---------
|
||||
An exercise in vertical integration, going as deep down as the CPU.
|
||||
Also something completely different from what you can find today, exploring
|
||||
a new way of controlling the computer.
|
||||
|
||||
Smalltalk
|
||||
---------
|
||||
The application is a live system, powered by a simple programming language.
|
||||
You can change anything you want, and see the results instantly. See also the
|
||||
Self programming language, for which my friend is trying to make an
|
||||
https://github.com/Bystroushaak/tinySelf[alternative implementation].
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
Haven
|
||||
=====
|
||||
|
||||
// This is a wiki of sorts, although, since no one else can edit it,
|
||||
// it's just a personal wiki, or maybe just a collection of articles.
|
||||
// Might name it like "writings" or even simply "db".
|
||||
|
||||
As computer power user, I often get unhappy, especially as a result of
|
||||
the changes that have happened over the last decade:
|
||||
|
||||
- almost every application I use looks and feels different,
|
||||
- senseless animations vie for my attention and add delays,
|
||||
- many applications are hard to control from the keyboard,
|
||||
- visual clutter distracts me from tasks at hand,
|
||||
- lack of customizability sets all those problems in stone,
|
||||
- and unbelievable hardware requirements, stemming from accidental complexity
|
||||
and a general lack of care, push me to buy unnecessarily expensive equipment
|
||||
just so that I don't feel like being _used by the computer_.
|
||||
|
||||
*haven* is an umbrella project for a range of mostly desktop
|
||||
applications. 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.
|
||||
|
||||
I'm not trying to create anything novel but to achieve something
|
||||
*elegant* and *pleasing*.
|
||||
|
||||
.Related articles:
|
||||
- https://lobste.rs/s/ksbcw5/12_principles_for_diverging_desktop +
|
||||
While I don't fully agree with (or care about) the vision,
|
||||
we have similar concerns.
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
To get a better sense of the direction this project is going in, you can take
|
||||
a look at a list of GoodThings and BadThings. If you're of the shallower kind,
|
||||
have a look at Identity, and if you're interested in the overall design,
|
||||
consult Technicalities.
|
||||
|
||||
Status
|
||||
------
|
||||
Trying to gain momentum, there's an astounding amount of work to be done. Hop
|
||||
on if you know Go well, my contact is on the link:/[front page]. Top priority
|
||||
tasks:
|
||||
|
||||
- [x] start writing a detailed wiki for propagation purposes
|
||||
- [ ] create a basic terminal emulator for `TERM=dumb` (exercise)
|
||||
- [ ] create a basic CSV viewer (simple, useful)
|
||||
- [ ] create an IRC client (lots of know-how, trivial UI)
|
||||
- [ ] create a basic vi-inspired text editor (required component for later)
|
||||
- [ ] create a personal wiki/organiser/PKB (urgent)
|
||||
|
||||
Mockups
|
||||
-------
|
||||
// TODO: Mockups can be just included from different places.
|
||||
|
||||
// SVG is too precise, must use HTML/CSS.
|
||||
|
||||
.ht -- terminal emulator
|
||||
+++
|
||||
<div style="border: 1px solid black; padding: 2px; background: white;">
|
||||
<pre style="margin: 0; padding: 0;">
|
||||
This is just supposed to be a simple terminal emulator application.
|
||||
It ought to be a rather simple exercise.
|
||||
</pre>
|
||||
</div>
|
||||
+++
|
||||
|
||||
While I despise the Unix terminal, I can't just stop using Unix tools, and so
|
||||
I'll need a means to interact with them. One day it might evolve into
|
||||
a graphical terminal with a tty compatibility layer, which would need
|
||||
integrating a custom POSIX shell such as https://github.com/mvdan/sh[].
|
||||
@@ -1,33 +0,0 @@
|
||||
Identity
|
||||
========
|
||||
|
||||
The name "haven" 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 for personal amusement.)
|
||||
|
||||
A logo covering the entire project is not needed and it seems hard to figure out
|
||||
anything meaningful anyway, though we might pick a specific font to use for the
|
||||
project's name <1>.
|
||||
|
||||
__
|
||||
<2> _/ /_ <1> | _ _ _ _
|
||||
\ _ \ |/ \ / \| \ / /_\ |/ \
|
||||
/ / / / | | | | \ / | | |
|
||||
/_/ /_/ | | \_/| \/ \_/ | |
|
||||
|
||||
That being said, if any symbol is deeply desired, a simple "h" <2> or "hvn"
|
||||
could work. 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.
|
||||
|
||||
Mascots
|
||||
-------
|
||||
I can think of two kinds of mascots that would bring me joy: an anime-style
|
||||
character, or a My Little Pony OC. The color scheme would be mostly grayscale,
|
||||
with something like shades of orange thrown in.
|
||||
|
||||
Either of the two choices is hard to draw for me if I don't want to infringe on
|
||||
someone else's copyright, so I might have to find someone else to do it.
|
||||
Eventually. Priorities lie elsewhere.
|
||||
@@ -0,0 +1,12 @@
|
||||
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.
|
||||
|
||||
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.
|
||||
+155
@@ -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.
|
||||
@@ -1,77 +0,0 @@
|
||||
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
|
||||
and 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. Wayland can wait until it
|
||||
stabilizes--it should not be a major issue switching the backends.
|
||||
Vector graphics can be handled by draw2d.
|
||||
|
||||
- https://rosettacode.org/wiki/Window_creation/X11#Go
|
||||
- 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 use 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.
|
||||
|
||||
.Resources:
|
||||
- https://github.com/golang/exp/tree/master/shiny is a GUI library
|
||||
- https://github.com/as/shiny is a fork of it
|
||||
- http://man.cat-v.org/plan_9/1/rio has a particular, unusual model
|
||||
|
||||
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 they're 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.
|
||||
|
||||
Licensing
|
||||
---------
|
||||
All will be released into the public domain. The preferred way of doing so is
|
||||
the 0BSD license.
|
||||
|
||||
Other people's libraries may be used or even imported if it makes sense, so long
|
||||
as they're not licensed under something either viral like the GPL, or annoying
|
||||
such as the original 4-clause BSD license.
|
||||
@@ -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
|
||||
)
|
||||
@@ -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=
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
<!DOCTYPE html><html lang=en-GB>
|
||||
<meta http-equiv=content-type content="text/html; charset=utf-8">
|
||||
<!-- make mobile browsers happy -->
|
||||
<meta name=viewport content="width=device-width, initial-scale=1">
|
||||
<link rel=stylesheet href=../style.css>
|
||||
<style>
|
||||
.sect1 { margin: .5rem 0; }
|
||||
.sect1 h2 { margin: 0; }
|
||||
.paragraph { margin: 1rem 0; }
|
||||
.paragraph p { margin: 0; }
|
||||
.literalblock { margin: 1rem 0; }
|
||||
.literalblock pre { margin: 0; }
|
||||
.ulist { margin: 1rem 0; }
|
||||
.ulist .ulist { margin: 0; }
|
||||
.ulist ul { margin: 0; }
|
||||
.ulist li p { margin: 0; }
|
||||
.imageblock img { max-width: 100%; }
|
||||
.paragraph svg { max-width: 100%; }
|
||||
#links, #footer { font-size: small; color: gray; }
|
||||
</style>
|
||||
<ul class=nav>
|
||||
<li><a href=../index.html>Me</a></li>
|
||||
<li><a href=../haven/Haven.html><b>Haven</b></a></li>
|
||||
<li><a href=https://git.janouch.name/p>Projects</a></li>
|
||||
<li><a href=../articles.html>Articles</a></li>
|
||||
</ul>
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
// hnc is a netcat-alike that shuts down properly.
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
// #include <unistd.h>
|
||||
import "C"
|
||||
|
||||
func isatty(fd uintptr) bool { return C.isatty(C.int(fd)) != 0 }
|
||||
|
||||
func log(format string, args ...interface{}) {
|
||||
msg := fmt.Sprintf(format+"\n", args...)
|
||||
if isatty(os.Stderr.Fd()) {
|
||||
msg = "\x1b[0;1;31m" + msg + "\x1b[m"
|
||||
}
|
||||
os.Stderr.WriteString(msg)
|
||||
}
|
||||
|
||||
var (
|
||||
flagTLS = flag.Bool("tls", false, "connect using TLS")
|
||||
flagCRLF = flag.Bool("crlf", false, "translate LF into CRLF")
|
||||
)
|
||||
|
||||
// Network connection that can shut down the write end.
|
||||
type connCloseWriter interface {
|
||||
net.Conn
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
func dial(address string) (connCloseWriter, error) {
|
||||
if *flagTLS {
|
||||
return tls.Dial("tcp", address, &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
}
|
||||
transport, err := net.Dial("tcp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transport.(connCloseWriter), nil
|
||||
}
|
||||
|
||||
func expand(raw []byte) []byte {
|
||||
if !*flagCRLF {
|
||||
return raw
|
||||
}
|
||||
var res []byte
|
||||
for _, b := range raw {
|
||||
if b == '\n' {
|
||||
res = append(res, '\r')
|
||||
}
|
||||
res = append(res, b)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Asynchronously delivered result of io.Reader.
|
||||
type readResult struct {
|
||||
b []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func read(r io.Reader, ch chan<- readResult) {
|
||||
defer close(ch)
|
||||
for {
|
||||
var buf [8192]byte
|
||||
n, err := r.Read(buf[:])
|
||||
ch <- readResult{buf[:n], err}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(flag.CommandLine.Output(),
|
||||
"Usage: %s [OPTION]... HOST PORT\n"+
|
||||
"Connect to a remote host over TCP/IP.\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
if flag.NArg() != 2 {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
conn, err := dial(net.JoinHostPort(flag.Arg(0), flag.Arg(1)))
|
||||
if err != nil {
|
||||
log("dial: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fromUser := make(chan readResult)
|
||||
go read(os.Stdin, fromUser)
|
||||
|
||||
fromConn := make(chan readResult)
|
||||
go read(conn, fromConn)
|
||||
|
||||
for fromUser != nil || fromConn != nil {
|
||||
select {
|
||||
case result := <-fromUser:
|
||||
if len(result.b) > 0 {
|
||||
if _, err := conn.Write(expand(result.b)); err != nil {
|
||||
log("remote: %s", err)
|
||||
}
|
||||
}
|
||||
if result.err != nil {
|
||||
log("stdin: %s", result.err)
|
||||
fromUser = nil
|
||||
if err := conn.CloseWrite(); err != nil {
|
||||
log("remote: %s", err)
|
||||
}
|
||||
}
|
||||
case result := <-fromConn:
|
||||
if len(result.b) > 0 {
|
||||
if _, err := os.Stdout.Write(result.b); err != nil {
|
||||
log("stdout: %s", err)
|
||||
}
|
||||
}
|
||||
if result.err != nil {
|
||||
log("remote: %s", result.err)
|
||||
fromConn = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+367
@@ -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
@@ -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 text == "" || 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))
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
}
|
||||
+533
@@ -0,0 +1,533 @@
|
||||
// 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
|
||||
}
|
||||
|
||||
categorised := map[string][]*Entry{}
|
||||
for _, entry := range ordered {
|
||||
category := entry.Attr("category")
|
||||
categorised[category] = append(categorised[category], entry)
|
||||
}
|
||||
return t.Execute(f, categorised)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+49
@@ -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
|
||||
Executable
+84
@@ -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
|
||||
Executable
+49
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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},
|
||||
}
|
||||
@@ -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 */
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
xgbgen/xgbgen
|
||||
.*.swp
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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'
|
||||
|
||||
| ||||