Compare commits

226 Commits

Author SHA1 Message Date
p 115b612e47 hswg: add categories 2026-06-24 21:15:31 +02:00
p 821b3f6ab9 hswc: ignore bots 2026-06-24 21:15:26 +02:00
p 9bbe70de85 hpcu: respond to CurrentTime as well
A fresh problem with VIM.

X11 documentation appears to claim that this is valid,
as is using CurrentTime in the response.
2025-11-30 19:01:46 +01:00
p 486b58525f Add a basic tool for collecting web comments 2024-11-03 06:12:31 +01:00
p c3cc608570 hswg: bump libasciidoc to master 2022-10-08 17:00:55 +02:00
p d643187333 hswg: preset some attributes 2022-10-05 20:38:27 +02:00
p 103704b183 Punt hid back where it came from 2022-09-26 13:37:51 +02:00
p 18e8e11ad4 Further reduce scope
The idea of hic now lives as xP within the xK project,
and the idea of hib has been implemented within xC.
2022-09-25 23:57:19 +02:00
p 1528ed2db0 Reduce scope
I've already built an X11-capable MPD client, and getting the right
kind of FFT for spectrum analysis would be difficult without Cgo.
Native Go PulseAudio interfacing would similarly cause excess work.

The image viewer would be a lot worse in this regard, seeing as
the amount of preexisting native Go code is tiny, and it's slow.

TL;DR: These problems have already been resolved in nncmpp and fiv,
and there's no need for inferior alternatives.
2022-08-23 03:33:27 +02:00
p 5f0d5bca70 hid: bump the FD limit 2022-08-02 22:10:31 +02:00
p 442fa5d660 Bump libasciidoc 2022-03-20 23:28:45 +01:00
p 8b376694d3 hid: make note of a deprecation 2022-03-16 12:57:00 +01:00
p 22e3861851 hswg: use a buffered channel for signals
Found by go vet.
2022-03-16 12:33:25 +01:00
p 9603456cd6 hid: add WebIRC support
Such clients can only be identified through STATS L.

It's a bit weird to abuse the "port" field this way,
but right now, it serves its purpose.
2022-03-15 19:57:31 +01:00
p b832a38ca6 hswg: parallelize rendering 2022-03-03 12:57:38 +01:00
p 6353dd156a hswg: log the error message on 📅 parse fail 2022-02-28 10:44:32 +01:00
p 7a2ea02c8d hswg: make AttrList return nil for missing attrs
And update copyright years.
2022-02-28 05:05:52 +01:00
p bafd5ef221 hid: implement WALLOPS 2022-02-05 00:31:34 +01:00
p ca245e4aca xgb-image: fix visual searching 2021-11-15 12:12:23 +01:00
p c3905349b0 hswg: don't eat libasciidoc rendering errors
An unfortunate consequence of the := operator's behaviour.

Also, update copyright years.
2021-09-23 20:58:59 +02:00
p f9e1f9a244 Bump Go modules to 1.17 2021-08-19 05:35:01 +02:00
p 4aab0b22ae hid: reflect the original project's new name
Better keep all schizophreny in my own head, rather than all projects.
2021-08-06 17:31:32 +02:00
p 5ab2977548 xgb-image: seek 32-bit visuals along with 30-bit
We only tried to upgrade to 32-bit depth when we weren't looking
for 30-bit visuals.  Probably of no practical consequence.
2021-08-01 03:36:23 +02:00
p a927713a81 xgb-image: support 10-bit colours
Apparently, a DefaultDepth 30 Xserver won't do translucency.
2021-07-15 01:49:37 +02:00
p 5ae8c24b8d xgb-image: gofmt 2021-07-15 01:49:34 +02:00
p ef24d7980c hswg: improve inotify processing
Make sure to read the whole record before checking flags.
2021-07-06 01:15:44 +02:00
p 6228693b22 Update .gitignore 2021-06-30 06:24:59 +02:00
p be27f00685 hswg: add .Attr, .AttrList, contains
Trying to figure out a sensible way of handle tags.
2021-06-29 04:14:47 +02:00
p 61083027a3 hswg: split out asciidoc.go 2021-06-29 04:14:47 +02:00
p 5b432fcc0b hswg: add a minimal README.adoc
Moving the example script from somewhere else.
2021-06-27 00:01:22 +02:00
p 04e19f5186 hswg: use inotify to watch for changed documents
Now we force the glob to be *.adoc, as well as *.asciidoc,
and there can only be one document directory.

The previous single-run mode is no longer supported.
2021-06-22 23:25:02 +02:00
p 49a685c32e hswg: take output path for the index as argument 2021-06-22 23:25:02 +02:00
p d763ce619d hswg: separate rendering from link expansion 2021-06-22 23:25:02 +02:00
p 8276f6bcb9 hswg: an initial attempt at refactoring main() 2021-06-22 23:25:02 +02:00
p dd5c583e8b hswg: store backlinks in a map 2021-06-22 23:25:01 +02:00
p 63d18d068d hswg: actually use templates for output files 2021-06-22 01:01:43 +02:00
p d47a8d2237 Bump libasciidoc dependency 2021-06-02 22:56:49 +02:00
p a979edf5b7 Bump libasciidoc dependency
Now at a usable release version again.
2020-10-04 15:31:24 +02:00
p 9db34a955b Bump libasciidoc dependency 2020-09-30 03:00:00 +02:00
p 35974efe21 hswg: try to order entries by date, reverse order 2020-09-21 22:44:12 +02:00
p 8f542c7120 hswg: execute a template given on standard input
So that information can be extracted from documents easily.
2020-09-21 19:26:55 +02:00
p ea8c59961c hswg: don't link to drafts 2020-09-21 19:26:54 +02:00
p 750f2139f5 hswg: extract attributes from documents 2020-09-21 19:26:54 +02:00
p 3d002bc540 Bump libasciidoc dependency 2020-09-21 19:26:54 +02:00
p 084a0a94b0 hswg: avoid self-referential links 2020-08-18 06:35:32 +02:00
p 91b1120c4a hswg: bump libasciidoc to a dev version
We want:
 - curved quotation marks
 - image alignment/float setting
2020-08-17 04:40:20 +02:00
p f7f892fb59 hswg: deduplicate backlinks 2020-08-15 07:20:04 +02:00
p d0ce3e3e66 Update README.adoc 2020-08-15 06:10:05 +02:00
p 3a5cc216bb hswg: merge in hasp as a mode
No need to have the two-line header processor in two places.
2020-08-15 04:26:50 +02:00
p a049249d81 hswg: add a static website generator 2020-08-15 04:26:50 +02:00
p d4eb9cde39 Update README.adoc 2020-08-12 01:52:09 +02:00
p cb1c8f8563 Render the README as AsciiDoc
The little issues aren't worth the monospace font in Gitea.
2020-08-12 01:42:31 +02:00
p 13275f1dd3 Update .gitignore 2020-08-06 19:04:11 +02:00
p 030a23c1a2 hasp: bump libasciidoc and other dependencies 2020-08-01 14:02:03 +02:00
p 0d37e5bc8a Name change 2020-08-01 14:01:58 +02:00
p eae39b13c2 hid: mention Go 1.12 alternative to TLS autodetection 2019-02-27 02:36:04 +01:00
p 95f183aa48 hpcu: eliminate infinite looping 2019-02-04 09:27:10 +01:00
p e7ea35f304 Use Go modules 2018-12-01 22:52:17 +01:00
p 056391eeca hpcu: the slightest cleanup 2018-11-01 11:38:17 +01:00
p f99615c850 Make README more AsciiDoc-compatible
It seems to almost work, except for callouts without blocks.
2018-10-08 03:54:57 +02:00
p d04c140a69 ht: add generated wcwidth tables and algorithm 2018-10-07 18:09:09 +02:00
p cd6c9e4d8c ht: add generated X11 keysym maps
Now we can make sense of keysyms and translate them to text.
2018-10-07 18:09:09 +02:00
p bf14fd5e04 Update README
Some reprioritization was in order.  Added some resources for hss.
2018-10-07 18:09:08 +02:00
p 228c3f3914 hasp: add a libasciidoc preprocessor 2018-10-07 18:09:08 +02:00
p 7d51aaa9a4 hpcu: add a selection unifier
So far not supporting large selections.
2018-09-30 18:45:29 +02:00
p f198f9f6ac xgb-selection: add a demo to track X11 selections 2018-09-30 18:45:29 +02:00
p 106e9b82b8 nexgb: update README 2018-09-30 17:34:27 +02:00
p 139c50b748 nexgb: update to xcb-proto 1.13 2018-09-30 17:34:27 +02:00
p e6e4e94436 nexgb: regenerate from xcb-proto 1.12
To refresh documentation.
2018-09-30 17:34:27 +02:00
p 7051829581 xgbgen: make request function signatures shorter 2018-09-30 17:34:26 +02:00
p 3e9ed4eac6 xgbgen: process <doc> elements
Most of XCB documentation now ends up in Go sources,
although the end result is of mixed quality.
2018-09-30 17:34:26 +02:00
p 0056720d05 xgbgen: generate proper sentences with full stops 2018-09-30 17:34:26 +02:00
p 28a5112532 nexgb: export {Major,Minor}Version for extensions 2018-09-30 17:34:25 +02:00
p 1a53c005e9 nexgb: gofmt 2018-09-30 17:34:25 +02:00
p 07bf9881cc nexgb: relicense xgbgen to 0BSD
Doing what the fuck I want to.

Included BurntSushi as a copyright holder because
under the Berne convention he totally is.
2018-09-30 17:34:25 +02:00
p 7aa2601e66 nexgb: rewrite import paths 2018-09-21 08:37:59 +02:00
p 48fb710f35 nexgb: post-merge fixups and cleanups
AUTHORS, CONTRIBUTORS: still useful, add people from git log, clean up.

LICENSE: Google doesn't really grant you shit anymore.  The EU doesn't
have software patents either, so it doesn't affect me directly.

README, doc.go: erase mentions of unmaintained xgbutil.
2018-09-08 19:51:53 +02:00
p 3173202cc1 Merge aarzilli/xgb, branch xcb1.12 as nexgb
History has been linearized and rewritten to stay under the new
subdirectory.  I want to make changes incompatible to BurntSushi/xgb.

The history begs for being thrown away entirely because of its quality
and because it doesn't cover the Google period but it is still useful
for copyright tracking.
2018-09-08 16:54:17 +02:00
aarzilli 3906399e7c Regenerated from xcb-proto 1.12 2018-09-08 16:49:27 +02:00
aarzilli 1c01d79ba1 Changed xgbgen to support xcb-proto 1.12
* Added minimal support for switch fields.
* Changed the way Size is calculated to accomodate for lists inside structs
  (added to randr)
* Removed heuristic to place alignment gaps, they are now explicitly
  described in xml
2018-09-08 16:49:26 +02:00
aarzilli a102c4056f Makefile: do not redefine $XPROTO if it's already defined. 2018-09-08 16:49:26 +02:00
Rabin Vincent 1f83ea75a2 Remove stray format char in randr example 2018-09-08 16:49:26 +02:00
Andrew Gallant 1614b58c42 fix structs with field name of 'Bytes'
(it conflict with a method of the same name that is generated for
all such structs)
2018-09-08 16:49:25 +02:00
aarzilli 8d343cfd3a Handle wildcard values in Xauthority file
Some field values in the Xauthority file have special meanings:
- a value of 65535 in the 'family' field means that the entry will
match a connection of any family on any address
- an empty string in the 'display number' field means that the entry
will match a connection on any display number

This behaviour is documented at:
https://cgit.freedesktop.org/xorg/lib/libXau/tree/AuGetBest.c#n109
2018-09-08 16:49:25 +02:00
aarzilli becaf43dcb Read/Write mutex for Extensions map 2018-09-08 16:49:24 +02:00
aarzilli baff8c1906 sendRequest reads req.buf after closing req.seq
NewRequest says you can avoid reallocating a new buffer for each request by calling it directly.
This is not true if req.seq is closed before req.buf is read.
2018-09-08 16:49:24 +02:00
Andrew Gallant 5451e59f88 Remove panics/fatal errors.
Fixes #9.

This makes shutdown a little more graceful, but there's more
work to be done here. Namely, all outstanding cookies need to
be given the error, otherwise they will block forever.
2018-09-08 16:49:24 +02:00
Bryan Matsuo dd00568d44 assign a sequence id to the cookie before returning from Conn.NewRequest 2018-09-08 16:49:23 +02:00
fangyuanziti a1d1151017 add a new api: NewConnNet 2018-09-08 16:49:23 +02:00
Andrew Gallant ef8155bf17 Formatting and 80 cols. 2018-09-08 16:49:23 +02:00
aarzilli a548d9d0f7 Fix Issue #21: automatic calculation of alignment padding after lists 2018-09-08 16:49:22 +02:00
snyh 1f8bd79abe examples:randr use info.Connection to detect whether there has any Randr.Modes 2018-09-08 16:49:22 +02:00
snyh 64c6e6170d example/randr: check the GetOutputInfo's mode length (virtual head can have zero mode) 2018-09-08 16:49:22 +02:00
Andrew Gallant 76f9adb599 Use consistent extension names. Close #6. 2018-09-08 16:49:21 +02:00
Andrew Gallant 33509dbeb0 It appears that the "Str" type (which is built into the core X protocol)
doesn't specify any padding. So it has to be treated as a special case.

Close #12.
2018-09-08 16:49:21 +02:00
Andrew Gallant ad9c35a02f shortcuts for the lazy 2018-09-08 16:49:20 +02:00
Andrew Gallant 2dc9914b5e When writing, don't pad the length of bytes produced from inner
structs/unions. Each type should take care of its own padding.

Close #14.
2018-09-08 16:49:20 +02:00
Andrew Gallant f0385db3a7 Regenerate xgb with latest XML descriptions. 2018-09-08 16:49:20 +02:00
Andrew Gallant 5a07ac7108 Fix fmt'd output for union list writing. 2018-09-08 16:49:19 +02:00
Andrew Gallant efe87cb908 Ignore the "fd" field for the time being (for the shm extension only). 2018-09-08 16:49:19 +02:00
Andrew Gallant 3b4adabee1 The sync and xinput extensions now use the switch field, which
XGB does not currently support. Therefore, I'm removing the sync and
xinput extensions.

This affects issues #11 and #13.
2018-09-08 16:49:19 +02:00
Axel Wagner 2104b8fcdf Export the logger (again)
Just enabling or disabling logging falls short of the power of
interfaces of go. A user is forced to either accept the logging to
stderr in the format defined by xgb or disable logging alltogether. By
exporting the logger, we can actually let the user decide where to log
in what format.
2018-09-08 16:49:18 +02:00
Andrew Gallant 38b293e74d Padding on a list is on the length of the list.
There was a bug where padding was being computed on each element of the
list. Close #5.
2018-09-08 16:49:18 +02:00
Andrew Gallant b06a8ca976 Don't needlessly change source files every time. 2018-09-08 16:49:18 +02:00
Andrew Gallant 0685fb57e1 Update to latest xproto XML. 2018-09-08 16:49:17 +02:00
Andrew Gallant 4b20ffaf4f Updated to work with new xproto XML files.
Namely, the "doc" element is ignored. Also, I've sorted everything
before output so that diff isn't completely useless.
2018-09-08 16:49:17 +02:00
Andrew Gallant 5d96993ee1 Fixed a nasty bug where closing could cause ReadFull to crash
the program. Close #4.
2018-09-08 16:49:16 +02:00
Andrew Gallant 3658686aee gofmt 2018-09-08 16:49:16 +02:00
Andrew Gallant e635de5e1d Provide access to the X display number in the XGB Conn. 2018-09-08 16:49:16 +02:00
Andrew Gallant (Ocelot) 269a7b9cc6 The hack continues. I've increased the event channel buffer. I know I'm
goofing here.

What I'd personally like to do is just use an "infinite" channel. That
is, push the limit of how many events can be processed to the machine
and not set an artificial limit in XGB. Some day...
2018-09-08 16:49:15 +02:00
Andrew Gallant (Ocelot) 13eff4bec3 Tests were move to xproto package a long time ago. Update README. 2018-09-08 16:49:15 +02:00
Andrew Gallant (Ocelot) 08275ebda8 Doc fixes and stop exporting ReplyChecked and ReplyUnchecked 2018-09-08 16:49:15 +02:00
Andrew Gallant (Ocelot) a9eae45cb3 Refresh build. Eh. 2018-09-08 16:49:14 +02:00
Andrew Gallant (Ocelot) 22ceab8074 Add rules for installing all packages. 2018-09-08 16:49:14 +02:00
Andrew Gallant (Ocelot) e9dc18b4f9 Doc fix. 2018-09-08 16:49:14 +02:00
Andrew Gallant (Ocelot) 744c9688cc Benchmark with gomaxprocs=3 too 2018-09-08 16:49:13 +02:00
Andrew Gallant (Ocelot) 8ee0ea9899 A pathological example for profiling purposes. Use 'make test'. 2018-09-08 16:49:13 +02:00
Andrew Gallant (Ocelot) e960f4d34e Add some style guidelines. 2018-09-08 16:49:07 +02:00
p 632b3ae494 xgb-draw: double buffer, change color, cleanup 2018-09-06 18:17:30 +02:00
p e8381d86ce xgb-draw: point interpolation 2018-09-05 12:31:23 +02:00
p 254ceb810c xgb-draw: proper brush stroke render 2018-09-05 12:30:38 +02:00
p df082e1dee xgb-draw: add a demo drawing application
Just trying to see how fast XRender can be and such.
2018-09-03 16:51:01 +02:00
p 3e42402e2b xgb-text-viewer: add a demo text viewer
More of a real application and just needs pictures in order to bring
the parts I have so far all together.
2018-09-02 18:25:38 +02:00
p c8fd1068d1 xgb-image: add support for the MIT-SHM extension 2018-09-02 18:25:37 +02:00
p cea1792913 xgb-image: add a demo that shows a scaled picture 2018-09-02 18:25:37 +02:00
p ff7518c74d xgb-keys: minimal example of reading keys 2018-09-02 18:25:37 +02:00
p 9e070e9648 xgb-monitors: add an experimental dumper 2018-09-02 18:25:36 +02:00
p 0c2853a8ae xgb-render: update comments 2018-09-02 18:25:36 +02:00
p 41e04fdc9f xgb-render: go back to RGBA because of alignment
Size 9 just happened to have the buffer 16 bytes wide.
2018-09-02 18:25:36 +02:00
p 23586eae01 xgb-render: give up on composite alpha 2018-09-02 18:25:35 +02:00
p 9424579c75 xgb-render: cleanup, tolerable glyph placement 2018-09-02 18:25:35 +02:00
p 32beda3c90 xgb-render: slightly simplify 2018-09-02 18:25:35 +02:00
p 30f2366f9a xgb-render: preliminary text rendering
I have finally got it working at all, now let's fix bounds etc.
2018-09-02 18:25:34 +02:00
p 215e3e8630 xgb-render: add some comments
Some containing code to list out potentially interesting information
from the X server.
2018-09-02 18:25:34 +02:00
p 44b01ccb17 xgb-window: add comparison with correct blending 2018-09-02 18:25:28 +02:00
p 0f7fcca7ce xgb-xrender: add a basic demo for XRender
So far just a conversion of xgb-window.go.
2018-09-02 18:24:14 +02:00
p 1fdf14f351 xgb-window: add a basic xgb demo
Demonstrating RGBA visuals and direct pixel values.
2018-09-02 18:24:14 +02:00
p 0ef66c7282 Update README 2018-09-02 18:24:14 +02:00
p 68d7e34b03 hnc: cleanup 2018-08-06 21:58:32 +02:00
p cc08b5457c tls-autodetect: updates, now that hid is ported
- fix SSL 2.0 detection
 - give up on using the resolved hostname later
 - rename connCloseWrite to connCloseWriter
2018-08-06 21:41:49 +02:00
p f8bcfe447c hid: clean up/finalize logging 2018-08-06 20:47:33 +02:00
p fb648c37be hid: move off of the log package
We don't spam with useless messages without -debug any longer.
2018-08-06 19:52:39 +02:00
p bb0113021a hid: port logging facilities
Though the regular mode now has timestamps and a new mode for systemd
has been added.
2018-08-06 19:49:06 +02:00
p 5a40d7c2ed hid: cleanups
No functional changes.
2018-08-06 12:31:31 +02:00
p f32e2f1483 hid: port IRC tests from liberty, fix tag parsing 2018-08-06 12:09:18 +02:00
p 62418ebb54 hid: rename connCloseWrite to connCloseWriter 2018-08-06 12:06:42 +02:00
p 6b45865e3d hid: add the first tests
This has actually revealed a problem in the SSL 2.0 detection.
2018-08-06 12:06:20 +02:00
p 198cb87036 README: fix typos, add some references 2018-08-06 11:10:35 +02:00
p 23f637dd47 hid: fix SSL 2.0 autodetection 2018-08-04 21:13:28 +02:00
p 4cd460886e hid: add support for customized replies 2018-08-03 21:45:53 +02:00
p 4d8376fd3c hid: unify exit codes with the flag package 2018-08-03 21:45:53 +02:00
p 01c3933a07 hid: cleanups 2018-08-03 21:45:52 +02:00
p 4fa84cc877 Add a README 2018-08-03 19:21:43 +02:00
p 4fc4426587 hnc: add a custom netcat-alike 2018-08-03 19:07:12 +02:00
p 9b6c4e7028 hid: fix listener shutdown 2018-08-03 10:55:22 +02:00
p 2e427c2cea hid: fix nickname verification in the user MODE message 2018-08-02 18:42:32 +02:00
p a4239548d3 hid: port default formatting strings to fmt 2018-08-02 12:51:22 +02:00
p a966d8489a hid: ircSendToRoommates -> ircNotifyRoommates
Should be clearer.
2018-08-01 20:39:37 +02:00
p e0b46a8cff hid: figured out how to port timeouts 2018-08-01 20:39:37 +02:00
p a6e6c3aaff hid: another round of general code cleanups 2018-08-01 17:45:56 +02:00
p 33969ac86e hid: bringup of what we have this far 2018-07-31 23:11:54 +02:00
p b103d5e2eb hid: port configuration and initialization
All the basic elements should be there now, we just need to port PING
timers and fix some remaining issues and we're basically done.
2018-07-31 20:53:23 +02:00
p c75299e1c3 hid: port IRC 3.2 message tag parsing, unused 2018-07-30 17:50:27 +02:00
p 24f1c4413a hid: use time.Time and time.Duration
It improves the code significantly over explicit int64 conversions.

Despite carrying unnecessary timezone information, time.Time also
carries a monotonic reading of time, which allows for more precise
measurement of time differences.
2018-07-30 10:07:02 +02:00
p 40370702d4 hid: port MODE, STATS, LINKS, KILL
Now all the commands have been ported but we desperately need to parse
a configuration file for additional settings yet.
2018-07-30 09:46:59 +02:00
p 5429c0b09d hid: port PART, KICK, INVITE, JOIN, AWAY, ISON, ADMIN, DIE 2018-07-29 17:49:57 +02:00
p b28c20a250 hid: port PRIVMSG, NOTICE, NAMES, WHO, WHOIS/WAS, TOPIC, SUMMON, USERS 2018-07-29 15:57:39 +02:00
p 2dfb4e45d1 hid: first round of mixed fixes and cleanups 2018-07-29 08:14:07 +02:00
p f5def2e579 hid: add a work in progress IRC daemon
The port is more than viable but it's also sort of all-or-nothing
and versioning needs have come before I've had a chance to finish it.
2018-07-28 16:21:34 +02:00
p b2cd8b46c9 tls-autodetect: mark issues, fix initialization 2018-07-24 13:58:57 +02:00
p e8602ee718 tls-autodetect: avoid a goroutine leak on timeout 2018-07-23 05:33:50 +02:00
p a8f02e0d37 tls-autodetect: finish inQ overrun handling 2018-07-22 14:55:15 +02:00
p 525734eeb3 tls-autodetect: fix client-initiated shutdown 2018-07-22 13:46:28 +02:00
p 6caa4ab928 Rename client.kill to client.closeLink 2018-07-21 00:27:26 +02:00
p 728fa4e548 tls-autodetect: put most of the server code in place
So far we act up when it is the client who initializes the shutdown.
2018-07-15 12:51:06 +02:00
p b5b64db075 Initial commit
Add a work-in-progress demo chat server with TLS autodetection.

It needs to be perfected before I move on to writing an IRCd on
the foundations.  If history is of any indication, this might lead
towards a nice set of applications.
2018-07-15 05:58:59 +02:00
Paul Sbarra cd22f99b20 auth: use encoding.binary 2012-05-28 17:58:32 -05:00
Andrew Gallant (Ocelot) 4ea94ca0fe Bug fix in the generator that was outputting %(MISSING) crud. 2012-05-26 18:24:52 -04:00
Andrew Gallant (Ocelot) 58bb2572c5 Doc touchups. 2012-05-26 18:22:25 -04:00
Andrew Gallant (Ocelot) acb84171e5 Add new logger type so that it can be shut off. 2012-05-16 23:57:26 -04:00
Andrew Gallant (Ocelot) 424f293671 export logger so it can be disabled 2012-05-16 23:26:19 -04:00
Andrew Gallant (Ocelot) 45a4ee92eb close channels. 2012-05-12 22:17:10 -04:00
Andrew Gallant (Ocelot) 6bdfd1d1b1 A more idiomatic way of trying a non-blocking send on a buffered channel
and falling back to a blocking send inside a goroutine.

This really needs to be fixed. The situation only arises when events are
sent and aren't pulled off the channel using {Wait,Poll}ForEvent.
Namely, if the event send blocks, the entire program will deadlock.

Using a goroutine is not ideal because we lose a guarantee of order:
that events are processed in the order of their arrival. However, it
seems OK as a temporary band-aide for a situation that probably doesn't
arise too often.

What I need to do is implement a dynamic queue. Here is a reference
implementation: http://play.golang.org/p/AiHBsxTFpj
2012-05-12 21:55:57 -04:00
Andrew Gallant (Ocelot) 7abc9c6455 added some docs and removed some extraneous code 2012-05-12 21:44:53 -04:00
Andrew Gallant (Ocelot) 24fef4062a docs 2012-05-12 21:36:31 -04:00
Andrew Gallant (Ocelot) f77feff864 some docs in the Makefile and removing a prefix that isn't needed. 2012-05-12 21:27:47 -04:00
Andrew Gallant (Ocelot) aa95801b2d panic when an extension request is issued before an extension has been initialized. but give a nice error message for the happy people. 2012-05-11 23:59:38 -04:00
Andrew Gallant (Ocelot) 29942bf078 panic when an extension request is issued before an extension has been initialized. but give a nice error message for the happy people. 2012-05-11 23:58:52 -04:00
Andrew Gallant (Ocelot) fb3128ed2a doc updates and a quick usage 2012-05-11 02:01:29 -04:00
Andrew Gallant (Ocelot) 3e6b354493 add a little more docs for errors 2012-05-11 01:58:52 -04:00
Andrew Gallant (Ocelot) c00652934e better docs 2012-05-10 23:57:34 -04:00
Andrew Gallant (Ocelot) a3363755cd adding package header comments 2012-05-10 20:06:22 -04:00
Andrew Gallant (Ocelot) 0c50dc6241 a huge commit. splitting extensions into their own sub-packages. 2012-05-10 17:01:42 -04:00
Andrew Gallant (Ocelot) e239bb3c68 make resource ids their own individual types. last commit before overhaul to sub-packages 2012-05-10 12:47:19 -04:00
Andrew Gallant (Ocelot) 00c6217ca9 update 2012-05-08 23:03:55 -04:00
Andrew Gallant (Ocelot) 5d64f69030 fixed nasty bug that made XGB not thread safe 2012-05-08 23:03:45 -04:00
Andrew Gallant (Ocelot) 62b293c937 use a custom logger so we don't stomp all over the global log configuration 2012-05-08 00:27:00 -04:00
Andrew Gallant (Ocelot) e256da00b1 gofmt 2012-05-07 21:58:43 -04:00
Andrew Gallant (Ocelot) 13d598e5e7 more clean up. use log instead of fmt.Print to stderr. bug fix for event blocking (a hack fix for now). 2012-05-07 21:58:33 -04:00
Andrew Gallant (Ocelot) daad54a5e1 important stuff first please 2012-05-07 04:17:11 -04:00
Andrew Gallant (Ocelot) eed777ebfd more info in readme. link to docs. 2012-05-07 04:13:41 -04:00
Andrew Gallant (Ocelot) dc48249e1a lots of docs and examples 2012-05-07 04:09:19 -04:00
Andrew Gallant (Ocelot) 3bf376bd66 80 cols 2012-05-07 01:11:41 -04:00
Andrew Gallant (Ocelot) fd30f1512a added tests 2012-05-07 01:00:45 -04:00
Andrew Gallant (Ocelot) 6d545e723a add more extension cruft. make extension checking more uniform. 2012-05-06 17:48:40 -04:00
Andrew Gallant (Ocelot) 135cee5761 auto-generated Go code ftw. ~65,000 lines. woooheee 2012-05-06 03:06:48 -04:00
Andrew Gallant (Ocelot) 70ebcf5178 build comman for converting XML to Go 2012-05-06 03:06:27 -04:00
Andrew Gallant (Ocelot) ea30f1a0a7 more bug fixes for the rest of the extensions 2012-05-06 03:06:02 -04:00
Andrew Gallant (Ocelot) 014a0598bf status update 2012-05-06 02:28:32 -04:00
Andrew Gallant (Ocelot) 18b2d420b0 added documentation and did some slight restructuring. it's party time. 2012-05-06 02:21:31 -04:00
Andrew Gallant (Ocelot) 99bc76de54 examples. some should be tests 2012-05-05 18:22:40 -04:00
Andrew Gallant (Ocelot) 369ad0d33e extensions are working! extensions are working! 2012-05-05 18:22:24 -04:00
Andrew Gallant (Ocelot) b6715f376f fixing bugs related mostly to extension handling 2012-05-05 18:21:48 -04:00
Andrew Gallant (Ocelot) 4a7b05be36 oh momma. a lot of modifications and it appears to be working. w00t. 2012-05-05 02:56:15 -04:00
Andrew Gallant (Ocelot) c222d406b0 converting to new reply/cookie scheme 2012-05-05 02:55:38 -04:00
Andrew Gallant (Ocelot) a5d4ad6c9d reworking xgb. cleaned up connection stuff a little. making new xid generation cleaner and use goroutines for it. 2012-05-03 22:47:50 -04:00
Andrew Gallant (Ocelot) 5cdae5950c holy toldeo... things might actually be working 2012-05-03 01:00:01 -04:00
Andrew Gallant (Ocelot) 39507f86ab finally starting on the crescendo: requests and replies. 2012-05-02 01:46:30 -04:00
Andrew Gallant (Ocelot) f48b6fafc6 float my boat 2012-05-01 01:09:45 -04:00
Andrew Gallant (Ocelot) 83a71d4648 unions, events and errors... oh my 2012-05-01 01:08:03 -04:00
Andrew Gallant (Ocelot) 73154769b3 splitting up go specific code. too much for one file IMO. more progress. almost done with structs. 2012-04-30 16:18:17 -04:00
Andrew Gallant (Ocelot) 2a2d8653b3 gofmt 2012-04-30 02:44:31 -04:00
Andrew Gallant (Ocelot) 05d8ec6a16 complete and total overhaul like i promised. things are much easier to reason about. still not working yet though. 2012-04-30 02:40:55 -04:00
Andrew Gallant (Ocelot) 3115c13e88 last commit before i tear everything down 2012-04-29 14:09:03 -04:00
Andrew Gallant (Ocelot) 6bf0191fb0 progress. still not working. this is incredibly difficult. 2012-04-29 03:38:29 -04:00
Andrew Gallant (Ocelot) 52a21b415a initial commit. not currently in a working state. 2012-04-28 23:25:57 -04:00
103 changed files with 82754 additions and 391 deletions
+15 -2
View File
@@ -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
-57
View File
@@ -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
View File
@@ -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
View File
@@ -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[].
-33
View File
@@ -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.
+12
View File
@@ -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
View File
@@ -0,0 +1,155 @@
Project haven
=============
haven is an umbrella project for a range of mostly desktop applications.
This README is being converted into a wiki on the wiki branch, also publicly
available at https://p.janouch.name/haven/Haven.html[]. We should only document
here that which has been created, as the extent of the rationale and plans is
gigantic and seems to have a life of its own.
Goal
----
The greater goal is to create a fresh computing environment for daily work and
play--easily controllable, reasonably complex, both visually and internally
unified and last but not least responsive. One should be able to use it
comfortably with a 60% keyboard and no pointing device.
haven serves as a testing ground, leaning on the side of compromises. It aims
to use today's Linux desktop as a support, relying on X11/Wayland, existing
window managers and web browsers.
The focus is therefore on going breadth-first, not depth-first. Applications
only need to be good enough to be able to replace their older siblings at all.
I.e. for me personally.
Scope
-----
Subproject names aim to have the minimum viable, reasonably identifiable name.
To group them together, a common prefix of "h" is used. The second column is
what should be used as the name in .desktop files, just like the GNOME project
figured out it would make sense:
- hbe - bitmap editor
- hbfe - bitmap font editor
- he - text editor
- hfm - file manager
- hm - mail client
- hnc - netcat-alike
- ho - all-powerful organizer
- hsm - system monitor
- hss - spreadsheets
- htd - translation dictionary
- ht - terminal emulator
- htk - GUI toolkit library
See Projects for more information about the individual projects.
Some taken names in Debian: hd (important), hte, hy.
Projects
--------
These are sorted in the order in which they should be created in order to gain
the best possible momentum. The htk GUI toolkit is implied as a side product
permeating the entire list.
Some information is omitted from these descriptions and lies either in my head
or in my other notes.
hnc -- netcat-alike
~~~~~~~~~~~~~~~~~~~
The result of testing xK/xS with telnet, OpenSSL s_client, OpenBSD nc, GNU nc,
and Ncat is that neither of them can properly shutdown the connection.
We need a good implementation with TLS support.
hpcu -- PRIMARY-CLIPBOARD unifier
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An improved replacement for autocutsel in selection synchronization "mode":
- using only one OS process;
- not polling selections twice a second unnecessarily;
- calling SetSelectionOwner on change even when it already owns the selection,
so that XFIXES SelectionNotify events are delivered;
- not using cut buffers for anything.
Only UTF8_STRING-convertible selections are synchronized.
hswg -- static website generator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
link:hswg/README.adoc[See hswg's README for details.]
ht -- terminal emulator
~~~~~~~~~~~~~~~~~~~~~~~
Similar scope to st(1). Clever display of internal padding for better looks.
he -- text editor
~~~~~~~~~~~~~~~~~
VIM controls, no scripting, no syntax highlight, single-file, made for
variable-width/proportional fonts. Initially done primarily to produce a text
editing widget, which is going to be an interesting challenge, arguably better
solved by whole program composition. Scintilla may provide some inspiration.
In the second stage, support for the Language Server Protocol will be added so
that the project can be edited using its own tools. Some scripting, perhaps
a tiny subset of VimL, might be desirable. Or other means of configuration.
Visual block mode or the color column may still be implemented.
The real model for the editor is Qt Creator with FakeVIM, though this is not to
be a clone of it, e.g. the various "Output" lists could be just special buffers,
which may be have names starting on "// ".
.Resources:
- http://doc.cat-v.org/plan_9/4th_edition/papers/sam/
ho -- all-powerful organizer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Zettelkasten with fulltext search, arbitrary reciprocal links, arbitrary tags.
Flat storage. Should be able to use translation dictionaries for search hints.
Indexing and search may be based on a common database, no need to get all fancy:
- http://rachbelaid.com/postgres-full-text-search-is-good-enough/
- https://www.sqlite.org/fts3.html#full_text_index_queries (FTS4 seems better)
htd -- translation dictionary
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This specific kind of application doesn't need a lot of user interface either,
just a tab bar, text entry and two columns of text with simple formatting.
For simplicity we will establish a custom dictionary format based on either
simple compress/gzip with separate files in StarDict style or, since we don't
really strive for random access and memory-efficiency (those 120M that sdtui
takes with my 10 dictionaries isn't particularly bad), pack everything with
archive/zip.
Instead of ICU we may use x/text/collate and that's about everything we need.
Since we have our own format, we may expect the index to be ordered by the
locale's rules, assuming they don't change between versions.
hfm -- file manager
~~~~~~~~~~~~~~~~~~~
All we need to achieve here is replace Midnight Commander, which besides the
most basic features includes a VFS for archives. The editing widget in
read-only mode could be used for F3. The shell is going to work very simply,
creating a PTY device and running things under TERM=dumb while decoding SGR,
or one could decide to run a new terminal emulator with a different shortcut.
ht could probably also be integrated.
Eventually the number of panels should be arbitrary with proper shortcuts for
working with them. We might also integrate a special view for picture previews,
which might or might not deserve its own program.
hss -- spreadsheets
~~~~~~~~~~~~~~~~~~~
The first version doesn't need to be able to reference other cells, and can more
or less be a CSV editor.
We can take inspiration from Excel:
- https://docs.microsoft.com/en-us/office/client-developer/excel/excel-recalculation
- https://www.microsoft.com/en-us/research/uploads/prod/2018/03/build-systems.pdf
The rest
~~~~~~~~
Currently there are no significant, specific plans about the other applications.
-77
View File
@@ -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.
+26
View File
@@ -0,0 +1,26 @@
module janouch.name/haven
go 1.17
require (
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802
github.com/bytesparadise/libasciidoc v0.7.1-0.20221008082129-967103fe8df6
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
golang.org/x/image v0.0.0-20190802002840-cff245a6509b
)
require github.com/sirupsen/logrus v1.8.1 // indirect
require (
github.com/alecthomas/chroma/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.9 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
+418
View File
@@ -0,0 +1,418 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/chroma/v2 v2.3.0 h1:83xfxrnjv8eK+Cf8qZDzNo3PPF9IbTWHs7z28GY6D0U=
github.com/alecthomas/chroma/v2 v2.3.0/go.mod h1:mZxeWZlxP2Dy+/8cBob2PYd8O2DwNAzave5AY7A2eQw=
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bytesparadise/libasciidoc v0.7.1-0.20221008082129-967103fe8df6 h1:H6OENzfxMVi4XJvFZWpbnttN5ncGnhC2qS15slyDdbw=
github.com/bytesparadise/libasciidoc v0.7.1-0.20221008082129-967103fe8df6/go.mod h1:Q2ZeBQ1fko5+NTUTs8rGu9gjTtbVaD6Qxg37GOPYdN4=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/felixge/fgtrace v0.1.0/go.mod h1:VYPh/jE5zczuRiQge0AtcpNmcLhV/epE/wpfVYQALlU=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mna/pigeon v1.1.0 h1:EjlvVbkGnNGemf8OrjeJX0nH8orujY/HkJgzJtd7kxc=
github.com/mna/pigeon v1.1.0/go.mod h1:rkFeDZ0gc+YbnrXPw0q2RlI0QRuKBBPu67fgYIyGRNg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190830223141-573d9926052a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-27
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,367 @@
// hpcu unifies the PRIMARY and CLIPBOARD X11 selections for text contents.
package main
import (
"errors"
"log"
"janouch.name/haven/nexgb"
"janouch.name/haven/nexgb/xfixes"
"janouch.name/haven/nexgb/xproto"
)
type selectionState struct {
name string // name of the selection
inProgress xproto.Timestamp // timestamp of retrieved selection
buffer []byte // UTF-8 text buffer
incr bool // INCR running
incrFailed bool // INCR failure indicator
owning xproto.Timestamp // since when we own the selection
}
var (
X *nexgb.Conn
setup *xproto.SetupInfo
screen *xproto.ScreenInfo
atomCLIPBOARD xproto.Atom // X11 atom for CLIPBOARD
atomUTF8String xproto.Atom // X11 atom for UTF8_STRING
atomINCR xproto.Atom // X11 atom for INCR
atomTARGETS xproto.Atom // X11 atom for TARGETS
atomTIMESTAMP xproto.Atom // X11 atom for TIMESTAMP
wid xproto.Window // auxiliary window
selections map[xproto.Atom]*selectionState
contents string // current shared selection contents
)
// resolveAtoms resolves a few required atoms that are not in the core protocol.
func resolveAtoms() error {
for _, i := range []struct {
placement *xproto.Atom
name string
}{
{&atomCLIPBOARD, "CLIPBOARD"},
{&atomUTF8String, "UTF8_STRING"},
{&atomINCR, "INCR"},
{&atomTARGETS, "TARGETS"},
{&atomTIMESTAMP, "TIMESTAMP"},
} {
if reply, err := xproto.InternAtom(X,
false, uint16(len(i.name)), i.name).Reply(); err != nil {
return err
} else {
*i.placement = reply.Atom
}
}
return nil
}
// setupAuxiliaryWindow creates a window that receives notifications about
// changed selection contents, and serves
func setupAuxiliaryWindow() error {
var err error
if wid, err = xproto.NewWindowId(X); err != nil {
return err
}
_ = xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, 0, 0, 1, 1,
0, xproto.WindowClassInputOutput, screen.RootVisual, xproto.CwEventMask,
[]uint32{xproto.EventMaskPropertyChange})
for _, selection := range []xproto.Atom{xproto.AtomPrimary, atomCLIPBOARD} {
_ = xfixes.SelectSelectionInput(X, wid, selection,
xfixes.SelectionEventMaskSetSelectionOwner|
xfixes.SelectionEventMaskSelectionWindowDestroy|
xfixes.SelectionEventMaskSelectionClientClose)
}
return nil
}
// getProperty reads a window property in a memory-efficient manner.
func getProperty(window xproto.Window, property xproto.Atom) (
*xproto.GetPropertyReply, error) {
// xorg-xserver doesn't seem to limit the length of replies or even
// the length of properties in the first place. It only has a huge
// (0xffffffff - sizeof(xChangePropertyReq))/4 limit for ChangeProperty
// requests, even though I can't XChangeProperty more than 0xffffe0
// bytes at a time.
//
// Since the XGB API doesn't let us provide our own buffer for
// value data, let us avoid multiplying the amount of consumed memory in
// pathological cases where properties are several gigabytes in size by
// chunking the requests. This has a cost of losing atomicity, although
// it shouldn't pose a problem except for timeout-caused INCR races.
var result xproto.GetPropertyReply
for (result.Sequence == 0 && result.Length == 0) || result.BytesAfter > 0 {
reply, err := xproto.GetProperty(X, false, /* delete */
window, property, xproto.GetPropertyTypeAny,
uint32(len(result.Value))/4,
uint32(setup.MaximumRequestLength)).Reply()
if err != nil {
return nil, err
}
if result.Length != 0 &&
(reply.Format != result.Format || reply.Type != result.Type) {
return nil, errors.New("property type changed during read")
}
reply.Value = append(result.Value, reply.Value...)
reply.ValueLen += result.ValueLen
result = *reply
}
return &result, nil
}
// appendText tries to append UTF-8 text to the selection state buffer.
func appendText(state *selectionState, prop *xproto.GetPropertyReply) bool {
if prop.Type == atomUTF8String && prop.Format == 8 {
state.buffer = append(state.buffer, prop.Value...)
return true
}
return false
}
func requestOwnership(origin *selectionState, time xproto.Timestamp) {
contents = string(origin.buffer)
for selection, state := range selections {
// We might want to replace the originator as well but it might have
// undesirable effects, mainly with PRIMARY.
if state != origin {
// No need to GetSelectionOwner, XFIXES is more reliable.
_ = xproto.SetSelectionOwner(X, wid, selection, time)
}
}
}
func handleXfixesSelectionNotify(e xfixes.SelectionNotifyEvent) {
state, ok := selections[e.Selection]
if !ok {
return
}
// Ownership request has been granted, don't ask ourselves for data.
if e.Owner == wid {
state.owning = e.SelectionTimestamp
return
}
// This should always be true.
if state.owning < e.SelectionTimestamp {
state.owning = 0
}
// Not checking whether we should give up when our current retrieval
// attempt is interrupted--the timeout mostly solves this.
if e.Owner == xproto.WindowNone {
return
}
// Don't try to process two things at once. Each request gets a few
// seconds to finish, then we move on, hoping that a property race
// doesn't commence. Ideally we'd set up a separate queue for these
// skipped requests and process them later.
if state.inProgress != 0 && e.Timestamp-state.inProgress < 5000 {
return
}
// ICCCM says we should ensure the named property doesn't exist.
_ = xproto.DeleteProperty(X, e.Window, e.Selection)
_ = xproto.ConvertSelection(X, e.Window, e.Selection,
atomUTF8String, e.Selection, e.Timestamp)
state.inProgress = e.Timestamp
state.incr = false
}
func handleSelectionNotify(e xproto.SelectionNotifyEvent) {
state, ok := selections[e.Selection]
if e.Requestor != wid || !ok || e.Time != state.inProgress {
return
}
state.inProgress = 0
if e.Property == xproto.AtomNone {
return
}
state.buffer = nil
reply, err := getProperty(e.Requestor, e.Property)
if err != nil {
return
}
// When you select a lot of text in VIM, it starts the ICCCM
// INCR mechanism, from which there is no opt-out.
if reply.Type == atomINCR {
state.inProgress = e.Time
state.incr = true
state.incrFailed = false
} else if appendText(state, reply) {
requestOwnership(state, e.Time)
}
_ = xproto.DeleteProperty(X, e.Requestor, e.Property)
}
func handlePropertyNotify(e xproto.PropertyNotifyEvent) {
state, ok := selections[e.Atom]
if e.Window != wid || e.State != xproto.PropertyNewValue ||
!ok || !state.incr {
return
}
reply, err := getProperty(e.Window, e.Atom)
if err != nil {
state.incrFailed = true
return
}
if !appendText(state, reply) {
// We need to keep deleting the property.
state.incrFailed = true
}
if reply.ValueLen == 0 {
if !state.incrFailed {
requestOwnership(state, e.Time)
}
state.inProgress = 0
state.incr = false
}
_ = xproto.DeleteProperty(X, e.Window, e.Atom)
}
func handleSelectionRequest(e xproto.SelectionRequestEvent) {
property := e.Property
if property == xproto.AtomNone {
property = e.Target
}
state, ok := selections[e.Selection]
if e.Owner != wid || !ok {
return
}
var (
typ xproto.Atom
format byte
data []byte
)
// XXX: We should also support the MULTIPLE target but it seems to be
// unimportant and largely abandoned today.
targets := []xproto.Atom{atomTARGETS, atomTIMESTAMP, atomUTF8String}
switch e.Target {
case atomTARGETS:
typ = xproto.AtomAtom
format = 32
data = make([]byte, len(targets)*4)
for i, atom := range targets {
nexgb.Put32(data[i*4:], uint32(atom))
}
case atomTIMESTAMP:
typ = xproto.AtomInteger
format = 32
data = make([]byte, 4)
nexgb.Put32(data, uint32(state.owning))
case atomUTF8String:
typ = atomUTF8String
format = 8
data = []byte(contents)
}
response := xproto.SelectionNotifyEvent{
Time: e.Time,
Requestor: e.Requestor,
Selection: e.Selection,
Target: e.Target,
Property: xproto.AtomNone,
}
if typ == 0 || len(data) > int(setup.MaximumRequestLength)*4-64 ||
state.owning == 0 ||
(e.Time != xproto.TimeCurrentTime && e.Time < state.owning) {
// TODO: Use the INCR mechanism for large data transfers instead
// of refusing the request, or at least use PropModeAppend.
//
// According to the ICCCM we need to set up a queue for concurrent
// (requestor, selection, target, timestamp) requests that differ
// only in the target property, and process them in order. The ICCCM
// has a nice rationale. It seems to only concern INCR. The queue
// might be a map[(who, what, how, when)][](where, data, offset).
//
// NOTE: Even with BigRequests support, it may technically be
// missing on the particular X server, and XGB copies buffers to yet
// another buffer, making very large transfers a very bad idea.
} else if xproto.ChangePropertyChecked(X, xproto.PropModeReplace,
e.Requestor, property, typ, format,
uint32(len(data)/int(format/8)), data).Check() == nil {
response.Property = property
}
_ = xproto.SendEvent(X, false /* propagate */, e.Requestor,
0 /* event mask */, string(response.Bytes()))
}
func handleXEvent(ev nexgb.Event) {
switch e := ev.(type) {
case xfixes.SelectionNotifyEvent:
handleXfixesSelectionNotify(e)
case xproto.SelectionNotifyEvent:
handleSelectionNotify(e)
case xproto.PropertyNotifyEvent:
handlePropertyNotify(e)
case xproto.SelectionRequestEvent:
handleSelectionRequest(e)
}
}
func main() {
var err error
if X, err = nexgb.NewConn(); err != nil {
log.Fatalln(err)
}
if err = xfixes.Init(X); err != nil {
log.Fatalln(err)
}
// Enable the extension.
_ = xfixes.QueryVersion(X, xfixes.MajorVersion, xfixes.MinorVersion)
setup = xproto.Setup(X)
screen = setup.DefaultScreen(X)
if err = resolveAtoms(); err != nil {
log.Fatalln(err)
}
if err = setupAuxiliaryWindow(); err != nil {
log.Fatalln(err)
}
// Now that we have our atoms, we can initialize state.
selections = map[xproto.Atom]*selectionState{
xproto.AtomPrimary: {name: "PRIMARY"},
atomCLIPBOARD: {name: "CLIPBOARD"},
}
for {
ev, xerr := X.WaitForEvent()
if xerr != nil {
log.Printf("Error: %s\n", xerr)
return
}
if ev != nil {
handleXEvent(ev)
}
}
}
+101
View File
@@ -0,0 +1,101 @@
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"os"
"regexp"
"strings"
"sync"
"time"
)
var (
long = regexp.MustCompile(`(.{72}\S*)\s+`)
file, requests *os.File
m sync.Mutex
)
func wrap(s string) string {
return strings.ReplaceAll(long.ReplaceAllString(
strings.ReplaceAll(s, "\r", ""), "$1\n"), "\n", "\n ")
}
func handler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
if err := r.ParseForm(); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
text := r.FormValue("text")
if 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))
}
+62
View File
@@ -0,0 +1,62 @@
hswg: a static website generator
================================
hswg wraps libasciidoc to make it understand more syntax, namely
two-line/underlined titles, and can be run either as a filter, or as a simple
wiki-like site generator.
Gitea/cgit AsciiDoc processor
-----------------------------
Wrap hswg in the following script to give it a few superpowers:
```
#!/bin/sh
# Make this also work for cgit which, strangely enough, is willing to render
# /anything/ via the /about route, only passing through image/* unchanged.
if [ -z "$GITEA_PREFIX_SRC" ]; then
test "${1%.adoc}" != "$1" || exit 1
cgit_fixups='s/<div class="content">/<div>/'
export GITEA_PREFIX_SRC=. GITEA_PREFIX_RAW=.
fi
# libasciidoc can't be helped in other ways so far, adding support for:
# - the original 'italics' syntax
# - double-line headings (part of haven's hswg which invokes libasciidoc)
# - make links to other documents work, normally an attribute could be used
perl -pe "s|'([-~/\\.\\w]+)'|_\$1_|g;" | hswg 2>/dev/null | \
perl -pe 's|(href=")([^/][^:]*?")|$1$ENV{GITEA_PREFIX_SRC}/$2|;' \
-e 's|(src=")([^/][^:]*?")|$1$ENV{GITEA_PREFIX_RAW}/$2|;' \
-e "$cgit_fixups"
```
Then, to set it up in Gitea, include the following snippet in your _app.ini_:
```
[markup.asciidoc]
ENABLED = true
FILE_EXTENSIONS = .adoc,.asciidoc
RENDER_COMMAND = /usr/local/bin/hswg-gitea
IS_INPUT_FILE = false
```
Similarly for cgit, the following _cgitrc_ snippet might do the job:
```
about-filter=/usr/local/bin/hswg-gitea
readme=:README.adoc
```
If parsing fails for some reason, the contents will be wrapped in HTML verbatim
as plain text.
Wiki mode
---------
The program will read a Go template for the index page from its standard input,
and another template for rendered pages from the path given as its first
argument. The second argument specifies the output filename for the index page,
and the last one is the document directory.
Consult the source code for a list template variables.
All pages will be initially rerendered on startup, and then the directory will
be watched for changes in real time using the inotify API.
+54
View File
@@ -0,0 +1,54 @@
package main
import (
"bytes"
"io"
"strings"
"unicode"
"unicode/utf8"
)
// isTitle returns the title level if the lines seem to form a title,
// zero otherwise. Input lines may inclide trailing newlines.
func isTitle(line1, line2 []byte) int {
// This is a very naïve method, we should target graphemes (thus at least
// NFC normalize the lines first) and account for wide characters.
diff := utf8.RuneCount(line1) - utf8.RuneCount(line2)
if len(line2) < 2 || diff < -1 || diff > 1 {
return 0
}
// "Don't be fooled by back-to-back delimited blocks."
// Still gets fooled by other things, though.
if bytes.IndexFunc(line1, func(r rune) bool {
return unicode.IsLetter(r) || unicode.IsNumber(r)
}) < 0 {
return 0
}
// The underline must be homogenous.
for _, r := range bytes.TrimRight(line2, "\r\n") {
if r != line2[0] {
return 0
}
}
return 1 + strings.IndexByte("=-~^+", line2[0])
}
func writeLine(w *io.PipeWriter, cur, next []byte) []byte {
if level := isTitle(cur, next); level > 0 {
w.Write(append(bytes.Repeat([]byte{'='}, level), ' '))
next = nil
}
w.Write(cur)
return next
}
// ConvertTitles converts AsciiDoc two-line (underlined) titles to single-line.
func ConvertTitles(w *io.PipeWriter, input []byte) {
var last []byte
for _, cur := range bytes.SplitAfter(input, []byte{'\n'}) {
last = writeLine(w, last, cur)
}
writeLine(w, last, nil)
}
+533
View File
@@ -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")
}
}
}
+49
View File
@@ -0,0 +1,49 @@
#!/bin/sh
gofmt <<EOF | sed 's, *//$,,'
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package $GOPACKAGE
import "janouch.name/haven/nexgb/xproto"
$(curl --silent --show-error \
https://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h \
https://cgit.freedesktop.org/xorg/proto/x11proto/plain/XF86keysym.h \
| perl -lne '
next unless /^\#define\s+
(XF86)?(XK_)([a-zA-Z_0-9]+)\s+
0x([0-9a-fA-F]+)\s*
(?:\/\*\s*(.*?)\s*\*\/)?\s*
$/x;
my ($name, $ident, $hex, $comment) =
(($1 // "") . $3, ($1 // "") . $2 . $3, lc $4, ($5 // ""));
# They are already somewhat sorted in the source file.
push @a, { hex => $hex, ident => $ident, comment => $comment };
$nametokeysym{$name} = $ident;
# All but the first name listed should be considered deprecated.
$keysymtoname{$ident} = $name unless exists $seen{$hex};
$seen{$hex}++;
END {
print "const (";
print "$_->{ident} = 0x$_->{hex} // $_->{comment}" for @a;
print ")";
# Very large tables, should be an on-demand package :(
print "// KeysymToName maps X11 keysym constants to their names";
print "var KeysymToName = map[xproto.Keysym]string{";
print "$_: \"$keysymtoname{$_}\"," for sort keys %keysymtoname;
print "}";
print "// NameToKeysym maps X11 keysym names to their constants";
print "var NameToKeysym = map[string]xproto.Keysym{";
print "\"$_\": $nametokeysym{$_}," for sort keys %nametokeysym;
print "}";
}
')
EOF
+84
View File
@@ -0,0 +1,84 @@
#!/bin/sh
u=https://www.unicode.org/Public/
# Download and filter Unicode data files with the given category expression,
# producing a list of possibly duplicitous codepoints in decimal format
retrieve() {
curl --silent --show-error --location "$2" | perl -lne 's/#.*//; s/ //g;
next unless /^([0-9A-F]+)(?:\.\.([0-9A-F]+))?;('"$1"')$/;
print for hex $1 .. hex ($2 // $1);'
}
togo() {
sort -nu | perl -lne '
sub flush { printf "{0x%04x, 0x%04x},\n", $first, $last }
BEGIN { $first = $last = <> }
if ($_ != $last + 1) { flush; $first = $_; }
$last = $_;
END { flush if defined $first }' | column -xc 72
}
gofmt <<EOF
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package $GOPACKAGE
// RuneWidth returns the column width of Go runes, using an algorithm and tables
// derived from https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c:
// - The null character (U+0000) has a column width of 0.
// - Other C0/C1 control characters and DEL will lead to a return value of -1.
// - Non-spacing and enclosing combining characters (general category code
// Mn or Me in the Unicode database) have a column width of 0.
// - SOFT HYPHEN (U+00AD) has a column width of 1.
// - Other format characters (general category code Cf in the Unicode database)
// and ZERO WIDTH SPACE (U+200B) have a column width of 0.
// - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) have
// a column width of 0.
// - Spacing characters in the East Asian Wide (W) or East Asian Full-width (F)
// category as defined in Unicode UAX #11 have a column width of 2.
// - All remaining characters (including all printable ISO 8859-1 and WGL4
// characters, Unicode control characters, etc.) have a column width of 1.
//
// Local changes:
// - Tables are generated from the latest available version of Unicode.
func RuneWidth(r rune) int {
switch {
case r == 0:
return 0
case r < 32 || r >= 0x7f && r < 0xa0:
return -1
case zeroWidthRunes.contains(r):
return 0
case fullWidthRunes.contains(r):
return 2
}
return 1
}
type runeRange struct{ first, last rune }
type runeRangeTable []runeRange
func (table runeRangeTable) contains(r rune) bool {
min, max := 0, len(table)-1
for max >= min {
mid := (min + max) / 2
if table[mid].last < r {
min = mid + 1
} else if table[mid].first > r {
max = mid - 1
} else {
return true
}
}
return false
}
var zeroWidthRunes = runeRangeTable{
$({ retrieve 'Me|Mn|Cf' $u/UCD/latest/ucd/extracted/DerivedGeneralCategory.txt;
seq 0x1160 0x11ff; echo $((0x200B)); } | grep -xv $((0x00AD)) | togo)
}
var fullWidthRunes = runeRangeTable{
$(retrieve 'W|F' $u/UCD/latest/ucd/EastAsianWidth.txt | togo)
}
EOF
+49
View File
@@ -0,0 +1,49 @@
#!/bin/sh
gofmt <<EOF
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package $GOPACKAGE
import "janouch.name/haven/nexgb/xproto"
// KeysymToRune tries to translate an X11 keysym to an appropriate rune.
// Returns -1 when no match is found.
func KeysymToRune(ks xproto.Keysym) rune {
// Visible Latin-1 is mapped 1:1
if (ks >= 0x20 && ks <= 0x7e) || (ks >= 0xa0 && ks <= 0xff) {
return rune(ks)
}
// Directly encoded 24-bit Unicode (going even above plane 16)
if (ks & 0xff000000) == 0x01000000 {
return rune(ks & 0x00ffffff)
}
min, max := 0, len(keysymToRuneTable)-1
for max >= min {
mid := (min + max) / 2
if keysymToRuneTable[mid].keysym < ks {
min = mid + 1
} else if keysymToRuneTable[mid].keysym > ks {
max = mid - 1
} else {
return keysymToRuneTable[mid].unicode
}
}
return -1
}
var keysymToRuneTable = []struct{
keysym xproto.Keysym
unicode rune
}{
$(curl --silent --show-error --location \
https://invisible-island.net/datafiles/release/xterm.tar.gz | \
tar --wildcards -xzOf - 'xterm-*/unicode/keysym.map' | \
sort | perl -lne '
next unless /^(0x([0-9a-f]{4}))\s+U([0-9a-f]{4})\s*(?:\#\s*(.*))?$/;
my ($keysym, $ks, $unicode, $comment) = ($1, hex($2), $3, ($4 // ""));
print "{$keysym, 0x$unicode}, // $comment"
if $ks >= 0x100 && $unicode ne "0000";
')
}
EOF
+6761
View File
File diff suppressed because it is too large Load Diff
+204
View File
@@ -0,0 +1,204 @@
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package main
// RuneWidth returns the column width of Go runes, using an algorithm and tables
// derived from https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c:
// - The null character (U+0000) has a column width of 0.
// - Other C0/C1 control characters and DEL will lead to a return value of -1.
// - Non-spacing and enclosing combining characters (general category code
// Mn or Me in the Unicode database) have a column width of 0.
// - SOFT HYPHEN (U+00AD) has a column width of 1.
// - Other format characters (general category code Cf in the Unicode database)
// and ZERO WIDTH SPACE (U+200B) have a column width of 0.
// - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) have
// a column width of 0.
// - Spacing characters in the East Asian Wide (W) or East Asian Full-width (F)
// category as defined in Unicode UAX #11 have a column width of 2.
// - All remaining characters (including all printable ISO 8859-1 and WGL4
// characters, Unicode control characters, etc.) have a column width of 1.
//
// Local changes:
// - Tables are generated from the latest available version of Unicode.
func RuneWidth(r rune) int {
switch {
case r == 0:
return 0
case r < 32 || r >= 0x7f && r < 0xa0:
return -1
case zeroWidthRunes.contains(r):
return 0
case fullWidthRunes.contains(r):
return 2
}
return 1
}
type runeRange struct{ first, last rune }
type runeRangeTable []runeRange
func (table runeRangeTable) contains(r rune) bool {
min, max := 0, len(table)-1
for max >= min {
mid := (min + max) / 2
if table[mid].last < r {
min = mid + 1
} else if table[mid].first > r {
max = mid - 1
} else {
return true
}
}
return false
}
var zeroWidthRunes = runeRangeTable{
{0x0300, 0x036f}, {0x0483, 0x0489}, {0x0591, 0x05bd},
{0x05bf, 0x05bf}, {0x05c1, 0x05c2}, {0x05c4, 0x05c5},
{0x05c7, 0x05c7}, {0x0600, 0x0605}, {0x0610, 0x061a},
{0x061c, 0x061c}, {0x064b, 0x065f}, {0x0670, 0x0670},
{0x06d6, 0x06dd}, {0x06df, 0x06e4}, {0x06e7, 0x06e8},
{0x06ea, 0x06ed}, {0x070f, 0x070f}, {0x0711, 0x0711},
{0x0730, 0x074a}, {0x07a6, 0x07b0}, {0x07eb, 0x07f3},
{0x07fd, 0x07fd}, {0x0816, 0x0819}, {0x081b, 0x0823},
{0x0825, 0x0827}, {0x0829, 0x082d}, {0x0859, 0x085b},
{0x08d3, 0x0902}, {0x093a, 0x093a}, {0x093c, 0x093c},
{0x0941, 0x0948}, {0x094d, 0x094d}, {0x0951, 0x0957},
{0x0962, 0x0963}, {0x0981, 0x0981}, {0x09bc, 0x09bc},
{0x09c1, 0x09c4}, {0x09cd, 0x09cd}, {0x09e2, 0x09e3},
{0x09fe, 0x09fe}, {0x0a01, 0x0a02}, {0x0a3c, 0x0a3c},
{0x0a41, 0x0a42}, {0x0a47, 0x0a48}, {0x0a4b, 0x0a4d},
{0x0a51, 0x0a51}, {0x0a70, 0x0a71}, {0x0a75, 0x0a75},
{0x0a81, 0x0a82}, {0x0abc, 0x0abc}, {0x0ac1, 0x0ac5},
{0x0ac7, 0x0ac8}, {0x0acd, 0x0acd}, {0x0ae2, 0x0ae3},
{0x0afa, 0x0aff}, {0x0b01, 0x0b01}, {0x0b3c, 0x0b3c},
{0x0b3f, 0x0b3f}, {0x0b41, 0x0b44}, {0x0b4d, 0x0b4d},
{0x0b56, 0x0b56}, {0x0b62, 0x0b63}, {0x0b82, 0x0b82},
{0x0bc0, 0x0bc0}, {0x0bcd, 0x0bcd}, {0x0c00, 0x0c00},
{0x0c04, 0x0c04}, {0x0c3e, 0x0c40}, {0x0c46, 0x0c48},
{0x0c4a, 0x0c4d}, {0x0c55, 0x0c56}, {0x0c62, 0x0c63},
{0x0c81, 0x0c81}, {0x0cbc, 0x0cbc}, {0x0cbf, 0x0cbf},
{0x0cc6, 0x0cc6}, {0x0ccc, 0x0ccd}, {0x0ce2, 0x0ce3},
{0x0d00, 0x0d01}, {0x0d3b, 0x0d3c}, {0x0d41, 0x0d44},
{0x0d4d, 0x0d4d}, {0x0d62, 0x0d63}, {0x0dca, 0x0dca},
{0x0dd2, 0x0dd4}, {0x0dd6, 0x0dd6}, {0x0e31, 0x0e31},
{0x0e34, 0x0e3a}, {0x0e47, 0x0e4e}, {0x0eb1, 0x0eb1},
{0x0eb4, 0x0eb9}, {0x0ebb, 0x0ebc}, {0x0ec8, 0x0ecd},
{0x0f18, 0x0f19}, {0x0f35, 0x0f35}, {0x0f37, 0x0f37},
{0x0f39, 0x0f39}, {0x0f71, 0x0f7e}, {0x0f80, 0x0f84},
{0x0f86, 0x0f87}, {0x0f8d, 0x0f97}, {0x0f99, 0x0fbc},
{0x0fc6, 0x0fc6}, {0x102d, 0x1030}, {0x1032, 0x1037},
{0x1039, 0x103a}, {0x103d, 0x103e}, {0x1058, 0x1059},
{0x105e, 0x1060}, {0x1071, 0x1074}, {0x1082, 0x1082},
{0x1085, 0x1086}, {0x108d, 0x108d}, {0x109d, 0x109d},
{0x1160, 0x11ff}, {0x135d, 0x135f}, {0x1712, 0x1714},
{0x1732, 0x1734}, {0x1752, 0x1753}, {0x1772, 0x1773},
{0x17b4, 0x17b5}, {0x17b7, 0x17bd}, {0x17c6, 0x17c6},
{0x17c9, 0x17d3}, {0x17dd, 0x17dd}, {0x180b, 0x180e},
{0x1885, 0x1886}, {0x18a9, 0x18a9}, {0x1920, 0x1922},
{0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193b},
{0x1a17, 0x1a18}, {0x1a1b, 0x1a1b}, {0x1a56, 0x1a56},
{0x1a58, 0x1a5e}, {0x1a60, 0x1a60}, {0x1a62, 0x1a62},
{0x1a65, 0x1a6c}, {0x1a73, 0x1a7c}, {0x1a7f, 0x1a7f},
{0x1ab0, 0x1abe}, {0x1b00, 0x1b03}, {0x1b34, 0x1b34},
{0x1b36, 0x1b3a}, {0x1b3c, 0x1b3c}, {0x1b42, 0x1b42},
{0x1b6b, 0x1b73}, {0x1b80, 0x1b81}, {0x1ba2, 0x1ba5},
{0x1ba8, 0x1ba9}, {0x1bab, 0x1bad}, {0x1be6, 0x1be6},
{0x1be8, 0x1be9}, {0x1bed, 0x1bed}, {0x1bef, 0x1bf1},
{0x1c2c, 0x1c33}, {0x1c36, 0x1c37}, {0x1cd0, 0x1cd2},
{0x1cd4, 0x1ce0}, {0x1ce2, 0x1ce8}, {0x1ced, 0x1ced},
{0x1cf4, 0x1cf4}, {0x1cf8, 0x1cf9}, {0x1dc0, 0x1df9},
{0x1dfb, 0x1dff}, {0x200b, 0x200f}, {0x202a, 0x202e},
{0x2060, 0x2064}, {0x2066, 0x206f}, {0x20d0, 0x20f0},
{0x2cef, 0x2cf1}, {0x2d7f, 0x2d7f}, {0x2de0, 0x2dff},
{0x302a, 0x302d}, {0x3099, 0x309a}, {0xa66f, 0xa672},
{0xa674, 0xa67d}, {0xa69e, 0xa69f}, {0xa6f0, 0xa6f1},
{0xa802, 0xa802}, {0xa806, 0xa806}, {0xa80b, 0xa80b},
{0xa825, 0xa826}, {0xa8c4, 0xa8c5}, {0xa8e0, 0xa8f1},
{0xa8ff, 0xa8ff}, {0xa926, 0xa92d}, {0xa947, 0xa951},
{0xa980, 0xa982}, {0xa9b3, 0xa9b3}, {0xa9b6, 0xa9b9},
{0xa9bc, 0xa9bc}, {0xa9e5, 0xa9e5}, {0xaa29, 0xaa2e},
{0xaa31, 0xaa32}, {0xaa35, 0xaa36}, {0xaa43, 0xaa43},
{0xaa4c, 0xaa4c}, {0xaa7c, 0xaa7c}, {0xaab0, 0xaab0},
{0xaab2, 0xaab4}, {0xaab7, 0xaab8}, {0xaabe, 0xaabf},
{0xaac1, 0xaac1}, {0xaaec, 0xaaed}, {0xaaf6, 0xaaf6},
{0xabe5, 0xabe5}, {0xabe8, 0xabe8}, {0xabed, 0xabed},
{0xfb1e, 0xfb1e}, {0xfe00, 0xfe0f}, {0xfe20, 0xfe2f},
{0xfeff, 0xfeff}, {0xfff9, 0xfffb}, {0x101fd, 0x101fd},
{0x102e0, 0x102e0}, {0x10376, 0x1037a}, {0x10a01, 0x10a03},
{0x10a05, 0x10a06}, {0x10a0c, 0x10a0f}, {0x10a38, 0x10a3a},
{0x10a3f, 0x10a3f}, {0x10ae5, 0x10ae6}, {0x10d24, 0x10d27},
{0x10f46, 0x10f50}, {0x11001, 0x11001}, {0x11038, 0x11046},
{0x1107f, 0x11081}, {0x110b3, 0x110b6}, {0x110b9, 0x110ba},
{0x110bd, 0x110bd}, {0x110cd, 0x110cd}, {0x11100, 0x11102},
{0x11127, 0x1112b}, {0x1112d, 0x11134}, {0x11173, 0x11173},
{0x11180, 0x11181}, {0x111b6, 0x111be}, {0x111c9, 0x111cc},
{0x1122f, 0x11231}, {0x11234, 0x11234}, {0x11236, 0x11237},
{0x1123e, 0x1123e}, {0x112df, 0x112df}, {0x112e3, 0x112ea},
{0x11300, 0x11301}, {0x1133b, 0x1133c}, {0x11340, 0x11340},
{0x11366, 0x1136c}, {0x11370, 0x11374}, {0x11438, 0x1143f},
{0x11442, 0x11444}, {0x11446, 0x11446}, {0x1145e, 0x1145e},
{0x114b3, 0x114b8}, {0x114ba, 0x114ba}, {0x114bf, 0x114c0},
{0x114c2, 0x114c3}, {0x115b2, 0x115b5}, {0x115bc, 0x115bd},
{0x115bf, 0x115c0}, {0x115dc, 0x115dd}, {0x11633, 0x1163a},
{0x1163d, 0x1163d}, {0x1163f, 0x11640}, {0x116ab, 0x116ab},
{0x116ad, 0x116ad}, {0x116b0, 0x116b5}, {0x116b7, 0x116b7},
{0x1171d, 0x1171f}, {0x11722, 0x11725}, {0x11727, 0x1172b},
{0x1182f, 0x11837}, {0x11839, 0x1183a}, {0x11a01, 0x11a0a},
{0x11a33, 0x11a38}, {0x11a3b, 0x11a3e}, {0x11a47, 0x11a47},
{0x11a51, 0x11a56}, {0x11a59, 0x11a5b}, {0x11a8a, 0x11a96},
{0x11a98, 0x11a99}, {0x11c30, 0x11c36}, {0x11c38, 0x11c3d},
{0x11c3f, 0x11c3f}, {0x11c92, 0x11ca7}, {0x11caa, 0x11cb0},
{0x11cb2, 0x11cb3}, {0x11cb5, 0x11cb6}, {0x11d31, 0x11d36},
{0x11d3a, 0x11d3a}, {0x11d3c, 0x11d3d}, {0x11d3f, 0x11d45},
{0x11d47, 0x11d47}, {0x11d90, 0x11d91}, {0x11d95, 0x11d95},
{0x11d97, 0x11d97}, {0x11ef3, 0x11ef4}, {0x16af0, 0x16af4},
{0x16b30, 0x16b36}, {0x16f8f, 0x16f92}, {0x1bc9d, 0x1bc9e},
{0x1bca0, 0x1bca3}, {0x1d167, 0x1d169}, {0x1d173, 0x1d182},
{0x1d185, 0x1d18b}, {0x1d1aa, 0x1d1ad}, {0x1d242, 0x1d244},
{0x1da00, 0x1da36}, {0x1da3b, 0x1da6c}, {0x1da75, 0x1da75},
{0x1da84, 0x1da84}, {0x1da9b, 0x1da9f}, {0x1daa1, 0x1daaf},
{0x1e000, 0x1e006}, {0x1e008, 0x1e018}, {0x1e01b, 0x1e021},
{0x1e023, 0x1e024}, {0x1e026, 0x1e02a}, {0x1e8d0, 0x1e8d6},
{0x1e944, 0x1e94a}, {0xe0001, 0xe0001}, {0xe0020, 0xe007f},
{0xe0100, 0xe01ef},
}
var fullWidthRunes = runeRangeTable{
{0x1100, 0x115f}, {0x231a, 0x231b}, {0x2329, 0x232a},
{0x23e9, 0x23ec}, {0x23f0, 0x23f0}, {0x23f3, 0x23f3},
{0x25fd, 0x25fe}, {0x2614, 0x2615}, {0x2648, 0x2653},
{0x267f, 0x267f}, {0x2693, 0x2693}, {0x26a1, 0x26a1},
{0x26aa, 0x26ab}, {0x26bd, 0x26be}, {0x26c4, 0x26c5},
{0x26ce, 0x26ce}, {0x26d4, 0x26d4}, {0x26ea, 0x26ea},
{0x26f2, 0x26f3}, {0x26f5, 0x26f5}, {0x26fa, 0x26fa},
{0x26fd, 0x26fd}, {0x2705, 0x2705}, {0x270a, 0x270b},
{0x2728, 0x2728}, {0x274c, 0x274c}, {0x274e, 0x274e},
{0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797},
{0x27b0, 0x27b0}, {0x27bf, 0x27bf}, {0x2b1b, 0x2b1c},
{0x2b50, 0x2b50}, {0x2b55, 0x2b55}, {0x2e80, 0x2e99},
{0x2e9b, 0x2ef3}, {0x2f00, 0x2fd5}, {0x2ff0, 0x2ffb},
{0x3000, 0x303e}, {0x3041, 0x3096}, {0x3099, 0x30ff},
{0x3105, 0x312f}, {0x3131, 0x318e}, {0x3190, 0x31ba},
{0x31c0, 0x31e3}, {0x31f0, 0x321e}, {0x3220, 0x3247},
{0x3250, 0x32fe}, {0x3300, 0x4dbf}, {0x4e00, 0xa48c},
{0xa490, 0xa4c6}, {0xa960, 0xa97c}, {0xac00, 0xd7a3},
{0xf900, 0xfaff}, {0xfe10, 0xfe19}, {0xfe30, 0xfe52},
{0xfe54, 0xfe66}, {0xfe68, 0xfe6b}, {0xff01, 0xff60},
{0xffe0, 0xffe6}, {0x16fe0, 0x16fe1}, {0x17000, 0x187f1},
{0x18800, 0x18af2}, {0x1b000, 0x1b11e}, {0x1b170, 0x1b2fb},
{0x1f004, 0x1f004}, {0x1f0cf, 0x1f0cf}, {0x1f18e, 0x1f18e},
{0x1f191, 0x1f19a}, {0x1f200, 0x1f202}, {0x1f210, 0x1f23b},
{0x1f240, 0x1f248}, {0x1f250, 0x1f251}, {0x1f260, 0x1f265},
{0x1f300, 0x1f320}, {0x1f32d, 0x1f335}, {0x1f337, 0x1f37c},
{0x1f37e, 0x1f393}, {0x1f3a0, 0x1f3ca}, {0x1f3cf, 0x1f3d3},
{0x1f3e0, 0x1f3f0}, {0x1f3f4, 0x1f3f4}, {0x1f3f8, 0x1f43e},
{0x1f440, 0x1f440}, {0x1f442, 0x1f4fc}, {0x1f4ff, 0x1f53d},
{0x1f54b, 0x1f54e}, {0x1f550, 0x1f567}, {0x1f57a, 0x1f57a},
{0x1f595, 0x1f596}, {0x1f5a4, 0x1f5a4}, {0x1f5fb, 0x1f64f},
{0x1f680, 0x1f6c5}, {0x1f6cc, 0x1f6cc}, {0x1f6d0, 0x1f6d2},
{0x1f6eb, 0x1f6ec}, {0x1f6f4, 0x1f6f9}, {0x1f910, 0x1f93e},
{0x1f940, 0x1f970}, {0x1f973, 0x1f976}, {0x1f97a, 0x1f97a},
{0x1f97c, 0x1f9a2}, {0x1f9b0, 0x1f9b9}, {0x1f9c0, 0x1f9c2},
{0x1f9d0, 0x1f9ff}, {0x20000, 0x2fffd}, {0x30000, 0x3fffd},
}
+859
View File
@@ -0,0 +1,859 @@
// Code generated by running "go generate" in janouch.name/haven. DO NOT EDIT.
package main
import "janouch.name/haven/nexgb/xproto"
// KeysymToRune tries to translate an X11 keysym to an appropriate rune.
// Returns -1 when no match is found.
func KeysymToRune(ks xproto.Keysym) rune {
// Visible Latin-1 is mapped 1:1
if (ks >= 0x20 && ks <= 0x7e) || (ks >= 0xa0 && ks <= 0xff) {
return rune(ks)
}
// Directly encoded 24-bit Unicode (going even above plane 16)
if (ks & 0xff000000) == 0x01000000 {
return rune(ks & 0x00ffffff)
}
min, max := 0, len(keysymToRuneTable)-1
for max >= min {
mid := (min + max) / 2
if keysymToRuneTable[mid].keysym < ks {
min = mid + 1
} else if keysymToRuneTable[mid].keysym > ks {
max = mid - 1
} else {
return keysymToRuneTable[mid].unicode
}
}
return -1
}
var keysymToRuneTable = []struct {
keysym xproto.Keysym
unicode rune
}{
{0x01a1, 0x0104}, // Aogonek
{0x01a2, 0x02d8}, // breve
{0x01a3, 0x0141}, // Lstroke
{0x01a5, 0x013d}, // Lcaron
{0x01a6, 0x015a}, // Sacute
{0x01a9, 0x0160}, // Scaron
{0x01aa, 0x015e}, // Scedilla
{0x01ab, 0x0164}, // Tcaron
{0x01ac, 0x0179}, // Zacute
{0x01ae, 0x017d}, // Zcaron
{0x01af, 0x017b}, // Zabovedot
{0x01b1, 0x0105}, // aogonek
{0x01b2, 0x02db}, // ogonek
{0x01b3, 0x0142}, // lstroke
{0x01b5, 0x013e}, // lcaron
{0x01b6, 0x015b}, // sacute
{0x01b7, 0x02c7}, // caron
{0x01b9, 0x0161}, // scaron
{0x01ba, 0x015f}, // scedilla
{0x01bb, 0x0165}, // tcaron
{0x01bc, 0x017a}, // zacute
{0x01bd, 0x02dd}, // doubleacute
{0x01be, 0x017e}, // zcaron
{0x01bf, 0x017c}, // zabovedot
{0x01c0, 0x0154}, // Racute
{0x01c3, 0x0102}, // Abreve
{0x01c5, 0x0139}, // Lacute
{0x01c6, 0x0106}, // Cacute
{0x01c8, 0x010c}, // Ccaron
{0x01ca, 0x0118}, // Eogonek
{0x01cc, 0x011a}, // Ecaron
{0x01cf, 0x010e}, // Dcaron
{0x01d0, 0x0110}, // Dstroke
{0x01d1, 0x0143}, // Nacute
{0x01d2, 0x0147}, // Ncaron
{0x01d5, 0x0150}, // Odoubleacute
{0x01d8, 0x0158}, // Rcaron
{0x01d9, 0x016e}, // Uring
{0x01db, 0x0170}, // Udoubleacute
{0x01de, 0x0162}, // Tcedilla
{0x01e0, 0x0155}, // racute
{0x01e3, 0x0103}, // abreve
{0x01e5, 0x013a}, // lacute
{0x01e6, 0x0107}, // cacute
{0x01e8, 0x010d}, // ccaron
{0x01ea, 0x0119}, // eogonek
{0x01ec, 0x011b}, // ecaron
{0x01ef, 0x010f}, // dcaron
{0x01f0, 0x0111}, // dstroke
{0x01f1, 0x0144}, // nacute
{0x01f2, 0x0148}, // ncaron
{0x01f5, 0x0151}, // odoubleacute
{0x01f8, 0x0159}, // rcaron
{0x01f9, 0x016f}, // uring
{0x01fb, 0x0171}, // udoubleacute
{0x01fe, 0x0163}, // tcedilla
{0x01ff, 0x02d9}, // abovedot
{0x02a1, 0x0126}, // Hstroke
{0x02a6, 0x0124}, // Hcircumflex
{0x02a9, 0x0130}, // Iabovedot
{0x02ab, 0x011e}, // Gbreve
{0x02ac, 0x0134}, // Jcircumflex
{0x02b1, 0x0127}, // hstroke
{0x02b6, 0x0125}, // hcircumflex
{0x02b9, 0x0131}, // idotless
{0x02bb, 0x011f}, // gbreve
{0x02bc, 0x0135}, // jcircumflex
{0x02c5, 0x010a}, // Cabovedot
{0x02c6, 0x0108}, // Ccircumflex
{0x02d5, 0x0120}, // Gabovedot
{0x02d8, 0x011c}, // Gcircumflex
{0x02dd, 0x016c}, // Ubreve
{0x02de, 0x015c}, // Scircumflex
{0x02e5, 0x010b}, // cabovedot
{0x02e6, 0x0109}, // ccircumflex
{0x02f5, 0x0121}, // gabovedot
{0x02f8, 0x011d}, // gcircumflex
{0x02fd, 0x016d}, // ubreve
{0x02fe, 0x015d}, // scircumflex
{0x03a2, 0x0138}, // kra
{0x03a3, 0x0156}, // Rcedilla
{0x03a5, 0x0128}, // Itilde
{0x03a6, 0x013b}, // Lcedilla
{0x03aa, 0x0112}, // Emacron
{0x03ab, 0x0122}, // Gcedilla
{0x03ac, 0x0166}, // Tslash
{0x03b3, 0x0157}, // rcedilla
{0x03b5, 0x0129}, // itilde
{0x03b6, 0x013c}, // lcedilla
{0x03ba, 0x0113}, // emacron
{0x03bb, 0x0123}, // gcedilla
{0x03bc, 0x0167}, // tslash
{0x03bd, 0x014a}, // ENG
{0x03bf, 0x014b}, // eng
{0x03c0, 0x0100}, // Amacron
{0x03c7, 0x012e}, // Iogonek
{0x03cc, 0x0116}, // Eabovedot
{0x03cf, 0x012a}, // Imacron
{0x03d1, 0x0145}, // Ncedilla
{0x03d2, 0x014c}, // Omacron
{0x03d3, 0x0136}, // Kcedilla
{0x03d9, 0x0172}, // Uogonek
{0x03dd, 0x0168}, // Utilde
{0x03de, 0x016a}, // Umacron
{0x03e0, 0x0101}, // amacron
{0x03e7, 0x012f}, // iogonek
{0x03ec, 0x0117}, // eabovedot
{0x03ef, 0x012b}, // imacron
{0x03f1, 0x0146}, // ncedilla
{0x03f2, 0x014d}, // omacron
{0x03f3, 0x0137}, // kcedilla
{0x03f9, 0x0173}, // uogonek
{0x03fd, 0x0169}, // utilde
{0x03fe, 0x016b}, // umacron
{0x047e, 0x203e}, // overline
{0x04a1, 0x3002}, // kana_fullstop
{0x04a2, 0x300c}, // kana_openingbracket
{0x04a3, 0x300d}, // kana_closingbracket
{0x04a4, 0x3001}, // kana_comma
{0x04a5, 0x30fb}, // kana_conjunctive
{0x04a6, 0x30f2}, // kana_WO
{0x04a7, 0x30a1}, // kana_a
{0x04a8, 0x30a3}, // kana_i
{0x04a9, 0x30a5}, // kana_u
{0x04aa, 0x30a7}, // kana_e
{0x04ab, 0x30a9}, // kana_o
{0x04ac, 0x30e3}, // kana_ya
{0x04ad, 0x30e5}, // kana_yu
{0x04ae, 0x30e7}, // kana_yo
{0x04af, 0x30c3}, // kana_tsu
{0x04b0, 0x30fc}, // prolongedsound
{0x04b1, 0x30a2}, // kana_A
{0x04b2, 0x30a4}, // kana_I
{0x04b3, 0x30a6}, // kana_U
{0x04b4, 0x30a8}, // kana_E
{0x04b5, 0x30aa}, // kana_O
{0x04b6, 0x30ab}, // kana_KA
{0x04b7, 0x30ad}, // kana_KI
{0x04b8, 0x30af}, // kana_KU
{0x04b9, 0x30b1}, // kana_KE
{0x04ba, 0x30b3}, // kana_KO
{0x04bb, 0x30b5}, // kana_SA
{0x04bc, 0x30b7}, // kana_SHI
{0x04bd, 0x30b9}, // kana_SU
{0x04be, 0x30bb}, // kana_SE
{0x04bf, 0x30bd}, // kana_SO
{0x04c0, 0x30bf}, // kana_TA
{0x04c1, 0x30c1}, // kana_CHI
{0x04c2, 0x30c4}, // kana_TSU
{0x04c3, 0x30c6}, // kana_TE
{0x04c4, 0x30c8}, // kana_TO
{0x04c5, 0x30ca}, // kana_NA
{0x04c6, 0x30cb}, // kana_NI
{0x04c7, 0x30cc}, // kana_NU
{0x04c8, 0x30cd}, // kana_NE
{0x04c9, 0x30ce}, // kana_NO
{0x04ca, 0x30cf}, // kana_HA
{0x04cb, 0x30d2}, // kana_HI
{0x04cc, 0x30d5}, // kana_FU
{0x04cd, 0x30d8}, // kana_HE
{0x04ce, 0x30db}, // kana_HO
{0x04cf, 0x30de}, // kana_MA
{0x04d0, 0x30df}, // kana_MI
{0x04d1, 0x30e0}, // kana_MU
{0x04d2, 0x30e1}, // kana_ME
{0x04d3, 0x30e2}, // kana_MO
{0x04d4, 0x30e4}, // kana_YA
{0x04d5, 0x30e6}, // kana_YU
{0x04d6, 0x30e8}, // kana_YO
{0x04d7, 0x30e9}, // kana_RA
{0x04d8, 0x30ea}, // kana_RI
{0x04d9, 0x30eb}, // kana_RU
{0x04da, 0x30ec}, // kana_RE
{0x04db, 0x30ed}, // kana_RO
{0x04dc, 0x30ef}, // kana_WA
{0x04dd, 0x30f3}, // kana_N
{0x04de, 0x309b}, // voicedsound
{0x04df, 0x309c}, // semivoicedsound
{0x05ac, 0x060c}, // Arabic_comma
{0x05bb, 0x061b}, // Arabic_semicolon
{0x05bf, 0x061f}, // Arabic_question_mark
{0x05c1, 0x0621}, // Arabic_hamza
{0x05c2, 0x0622}, // Arabic_maddaonalef
{0x05c3, 0x0623}, // Arabic_hamzaonalef
{0x05c4, 0x0624}, // Arabic_hamzaonwaw
{0x05c5, 0x0625}, // Arabic_hamzaunderalef
{0x05c6, 0x0626}, // Arabic_hamzaonyeh
{0x05c7, 0x0627}, // Arabic_alef
{0x05c8, 0x0628}, // Arabic_beh
{0x05c9, 0x0629}, // Arabic_tehmarbuta
{0x05ca, 0x062a}, // Arabic_teh
{0x05cb, 0x062b}, // Arabic_theh
{0x05cc, 0x062c}, // Arabic_jeem
{0x05cd, 0x062d}, // Arabic_hah
{0x05ce, 0x062e}, // Arabic_khah
{0x05cf, 0x062f}, // Arabic_dal
{0x05d0, 0x0630}, // Arabic_thal
{0x05d1, 0x0631}, // Arabic_ra
{0x05d2, 0x0632}, // Arabic_zain
{0x05d3, 0x0633}, // Arabic_seen
{0x05d4, 0x0634}, // Arabic_sheen
{0x05d5, 0x0635}, // Arabic_sad
{0x05d6, 0x0636}, // Arabic_dad
{0x05d7, 0x0637}, // Arabic_tah
{0x05d8, 0x0638}, // Arabic_zah
{0x05d9, 0x0639}, // Arabic_ain
{0x05da, 0x063a}, // Arabic_ghain
{0x05e0, 0x0640}, // Arabic_tatweel
{0x05e1, 0x0641}, // Arabic_feh
{0x05e2, 0x0642}, // Arabic_qaf
{0x05e3, 0x0643}, // Arabic_kaf
{0x05e4, 0x0644}, // Arabic_lam
{0x05e5, 0x0645}, // Arabic_meem
{0x05e6, 0x0646}, // Arabic_noon
{0x05e7, 0x0647}, // Arabic_ha
{0x05e8, 0x0648}, // Arabic_waw
{0x05e9, 0x0649}, // Arabic_alefmaksura
{0x05ea, 0x064a}, // Arabic_yeh
{0x05eb, 0x064b}, // Arabic_fathatan
{0x05ec, 0x064c}, // Arabic_dammatan
{0x05ed, 0x064d}, // Arabic_kasratan
{0x05ee, 0x064e}, // Arabic_fatha
{0x05ef, 0x064f}, // Arabic_damma
{0x05f0, 0x0650}, // Arabic_kasra
{0x05f1, 0x0651}, // Arabic_shadda
{0x05f2, 0x0652}, // Arabic_sukun
{0x06a1, 0x0452}, // Serbian_dje
{0x06a2, 0x0453}, // Macedonia_gje
{0x06a3, 0x0451}, // Cyrillic_io
{0x06a4, 0x0454}, // Ukrainian_ie
{0x06a5, 0x0455}, // Macedonia_dse
{0x06a6, 0x0456}, // Ukrainian_i
{0x06a7, 0x0457}, // Ukrainian_yi
{0x06a8, 0x0458}, // Cyrillic_je
{0x06a9, 0x0459}, // Cyrillic_lje
{0x06aa, 0x045a}, // Cyrillic_nje
{0x06ab, 0x045b}, // Serbian_tshe
{0x06ac, 0x045c}, // Macedonia_kje
{0x06ad, 0x0491}, // Ukrainian_ghe_with_upturn
{0x06ae, 0x045e}, // Byelorussian_shortu
{0x06af, 0x045f}, // Cyrillic_dzhe
{0x06b0, 0x2116}, // numerosign
{0x06b1, 0x0402}, // Serbian_DJE
{0x06b2, 0x0403}, // Macedonia_GJE
{0x06b3, 0x0401}, // Cyrillic_IO
{0x06b4, 0x0404}, // Ukrainian_IE
{0x06b5, 0x0405}, // Macedonia_DSE
{0x06b6, 0x0406}, // Ukrainian_I
{0x06b7, 0x0407}, // Ukrainian_YI
{0x06b8, 0x0408}, // Cyrillic_JE
{0x06b9, 0x0409}, // Cyrillic_LJE
{0x06ba, 0x040a}, // Cyrillic_NJE
{0x06bb, 0x040b}, // Serbian_TSHE
{0x06bc, 0x040c}, // Macedonia_KJE
{0x06bd, 0x0490}, // Ukrainian_GHE_WITH_UPTURN
{0x06be, 0x040e}, // Byelorussian_SHORTU
{0x06bf, 0x040f}, // Cyrillic_DZHE
{0x06c0, 0x044e}, // Cyrillic_yu
{0x06c1, 0x0430}, // Cyrillic_a
{0x06c2, 0x0431}, // Cyrillic_be
{0x06c3, 0x0446}, // Cyrillic_tse
{0x06c4, 0x0434}, // Cyrillic_de
{0x06c5, 0x0435}, // Cyrillic_ie
{0x06c6, 0x0444}, // Cyrillic_ef
{0x06c7, 0x0433}, // Cyrillic_ghe
{0x06c8, 0x0445}, // Cyrillic_ha
{0x06c9, 0x0438}, // Cyrillic_i
{0x06ca, 0x0439}, // Cyrillic_shorti
{0x06cb, 0x043a}, // Cyrillic_ka
{0x06cc, 0x043b}, // Cyrillic_el
{0x06cd, 0x043c}, // Cyrillic_em
{0x06ce, 0x043d}, // Cyrillic_en
{0x06cf, 0x043e}, // Cyrillic_o
{0x06d0, 0x043f}, // Cyrillic_pe
{0x06d1, 0x044f}, // Cyrillic_ya
{0x06d2, 0x0440}, // Cyrillic_er
{0x06d3, 0x0441}, // Cyrillic_es
{0x06d4, 0x0442}, // Cyrillic_te
{0x06d5, 0x0443}, // Cyrillic_u
{0x06d6, 0x0436}, // Cyrillic_zhe
{0x06d7, 0x0432}, // Cyrillic_ve
{0x06d8, 0x044c}, // Cyrillic_softsign
{0x06d9, 0x044b}, // Cyrillic_yeru
{0x06da, 0x0437}, // Cyrillic_ze
{0x06db, 0x0448}, // Cyrillic_sha
{0x06dc, 0x044d}, // Cyrillic_e
{0x06dd, 0x0449}, // Cyrillic_shcha
{0x06de, 0x0447}, // Cyrillic_che
{0x06df, 0x044a}, // Cyrillic_hardsign
{0x06e0, 0x042e}, // Cyrillic_YU
{0x06e1, 0x0410}, // Cyrillic_A
{0x06e2, 0x0411}, // Cyrillic_BE
{0x06e3, 0x0426}, // Cyrillic_TSE
{0x06e4, 0x0414}, // Cyrillic_DE
{0x06e5, 0x0415}, // Cyrillic_IE
{0x06e6, 0x0424}, // Cyrillic_EF
{0x06e7, 0x0413}, // Cyrillic_GHE
{0x06e8, 0x0425}, // Cyrillic_HA
{0x06e9, 0x0418}, // Cyrillic_I
{0x06ea, 0x0419}, // Cyrillic_SHORTI
{0x06eb, 0x041a}, // Cyrillic_KA
{0x06ec, 0x041b}, // Cyrillic_EL
{0x06ed, 0x041c}, // Cyrillic_EM
{0x06ee, 0x041d}, // Cyrillic_EN
{0x06ef, 0x041e}, // Cyrillic_O
{0x06f0, 0x041f}, // Cyrillic_PE
{0x06f1, 0x042f}, // Cyrillic_YA
{0x06f2, 0x0420}, // Cyrillic_ER
{0x06f3, 0x0421}, // Cyrillic_ES
{0x06f4, 0x0422}, // Cyrillic_TE
{0x06f5, 0x0423}, // Cyrillic_U
{0x06f6, 0x0416}, // Cyrillic_ZHE
{0x06f7, 0x0412}, // Cyrillic_VE
{0x06f8, 0x042c}, // Cyrillic_SOFTSIGN
{0x06f9, 0x042b}, // Cyrillic_YERU
{0x06fa, 0x0417}, // Cyrillic_ZE
{0x06fb, 0x0428}, // Cyrillic_SHA
{0x06fc, 0x042d}, // Cyrillic_E
{0x06fd, 0x0429}, // Cyrillic_SHCHA
{0x06fe, 0x0427}, // Cyrillic_CHE
{0x06ff, 0x042a}, // Cyrillic_HARDSIGN
{0x07a1, 0x0386}, // Greek_ALPHAaccent
{0x07a2, 0x0388}, // Greek_EPSILONaccent
{0x07a3, 0x0389}, // Greek_ETAaccent
{0x07a4, 0x038a}, // Greek_IOTAaccent
{0x07a5, 0x03aa}, // Greek_IOTAdieresis
{0x07a7, 0x038c}, // Greek_OMICRONaccent
{0x07a8, 0x038e}, // Greek_UPSILONaccent
{0x07a9, 0x03ab}, // Greek_UPSILONdieresis
{0x07ab, 0x038f}, // Greek_OMEGAaccent
{0x07ae, 0x0385}, // Greek_accentdieresis
{0x07af, 0x2015}, // Greek_horizbar
{0x07b1, 0x03ac}, // Greek_alphaaccent
{0x07b2, 0x03ad}, // Greek_epsilonaccent
{0x07b3, 0x03ae}, // Greek_etaaccent
{0x07b4, 0x03af}, // Greek_iotaaccent
{0x07b5, 0x03ca}, // Greek_IOTAdieresis
{0x07b6, 0x0390}, // Greek_iotaaccentdieresis
{0x07b7, 0x03cc}, // Greek_omicronaccent
{0x07b8, 0x03cd}, // Greek_upsilonaccent
{0x07b9, 0x03cb}, // Greek_upsilondieresis
{0x07ba, 0x03b0}, // Greek_upsilonaccentdieresis
{0x07bb, 0x03ce}, // Greek_omegaaccent
{0x07c1, 0x0391}, // Greek_ALPHA
{0x07c2, 0x0392}, // Greek_BETA
{0x07c3, 0x0393}, // Greek_GAMMA
{0x07c4, 0x0394}, // Greek_DELTA
{0x07c5, 0x0395}, // Greek_EPSILON
{0x07c6, 0x0396}, // Greek_ZETA
{0x07c7, 0x0397}, // Greek_ETA
{0x07c8, 0x0398}, // Greek_THETA
{0x07c9, 0x0399}, // Greek_IOTA
{0x07ca, 0x039a}, // Greek_KAPPA
{0x07cb, 0x039b}, // Greek_LAMBDA
{0x07cb, 0x039b}, // Greek_LAMDA
{0x07cc, 0x039c}, // Greek_MU
{0x07cd, 0x039d}, // Greek_NU
{0x07ce, 0x039e}, // Greek_XI
{0x07cf, 0x039f}, // Greek_OMICRON
{0x07d0, 0x03a0}, // Greek_PI
{0x07d1, 0x03a1}, // Greek_RHO
{0x07d2, 0x03a3}, // Greek_SIGMA
{0x07d4, 0x03a4}, // Greek_TAU
{0x07d5, 0x03a5}, // Greek_UPSILON
{0x07d6, 0x03a6}, // Greek_PHI
{0x07d7, 0x03a7}, // Greek_CHI
{0x07d8, 0x03a8}, // Greek_PSI
{0x07d9, 0x03a9}, // Greek_OMEGA
{0x07e1, 0x03b1}, // Greek_alpha
{0x07e2, 0x03b2}, // Greek_beta
{0x07e3, 0x03b3}, // Greek_gamma
{0x07e4, 0x03b4}, // Greek_delta
{0x07e5, 0x03b5}, // Greek_epsilon
{0x07e6, 0x03b6}, // Greek_zeta
{0x07e7, 0x03b7}, // Greek_eta
{0x07e8, 0x03b8}, // Greek_theta
{0x07e9, 0x03b9}, // Greek_iota
{0x07ea, 0x03ba}, // Greek_kappa
{0x07eb, 0x03bb}, // Greek_lambda
{0x07ec, 0x03bc}, // Greek_mu
{0x07ed, 0x03bd}, // Greek_nu
{0x07ee, 0x03be}, // Greek_xi
{0x07ef, 0x03bf}, // Greek_omicron
{0x07f0, 0x03c0}, // Greek_pi
{0x07f1, 0x03c1}, // Greek_rho
{0x07f2, 0x03c3}, // Greek_sigma
{0x07f3, 0x03c2}, // Greek_finalsmallsigma
{0x07f4, 0x03c4}, // Greek_tau
{0x07f5, 0x03c5}, // Greek_upsilon
{0x07f6, 0x03c6}, // Greek_phi
{0x07f7, 0x03c7}, // Greek_chi
{0x07f8, 0x03c8}, // Greek_psi
{0x07f9, 0x03c9}, // Greek_omega
{0x08a1, 0x23b7}, // leftradical
{0x08a2, 0x250c}, // topleftradical
{0x08a3, 0x2500}, // horizconnector
{0x08a4, 0x2320}, // topintegral
{0x08a5, 0x2321}, // botintegral
{0x08a6, 0x2502}, // vertconnector
{0x08a7, 0x23a1}, // topleftsqbracket
{0x08a8, 0x23a3}, // botleftsqbracket
{0x08a9, 0x23a4}, // toprightsqbracket
{0x08aa, 0x23a6}, // botrightsqbracket
{0x08ab, 0x239b}, // topleftparens
{0x08ac, 0x239d}, // botleftparens
{0x08ad, 0x239e}, // toprightparens
{0x08ae, 0x23a0}, // botrightparens
{0x08af, 0x23a8}, // leftmiddlecurlybrace
{0x08b0, 0x23ac}, // rightmiddlecurlybrace
{0x08bc, 0x2264}, // lessthanequal
{0x08bd, 0x2260}, // notequal
{0x08be, 0x2265}, // greaterthanequal
{0x08bf, 0x222b}, // integral
{0x08c0, 0x2234}, // therefore
{0x08c1, 0x221d}, // variation
{0x08c2, 0x221e}, // infinity
{0x08c5, 0x2207}, // nabla
{0x08c8, 0x223c}, // approximate
{0x08c9, 0x2243}, // similarequal
{0x08cd, 0x21d4}, // ifonlyif
{0x08ce, 0x21d2}, // implies
{0x08cf, 0x2261}, // identical
{0x08d6, 0x221a}, // radical
{0x08da, 0x2282}, // includedin
{0x08db, 0x2283}, // includes
{0x08dc, 0x2229}, // intersection
{0x08dd, 0x222a}, // union
{0x08de, 0x2227}, // logicaland
{0x08df, 0x2228}, // logicalor
{0x08ef, 0x2202}, // partialderivative
{0x08f6, 0x0192}, // function
{0x08fb, 0x2190}, // leftarrow
{0x08fc, 0x2191}, // uparrow
{0x08fd, 0x2192}, // rightarrow
{0x08fe, 0x2193}, // downarrow
{0x09df, 0x2422}, // blank
{0x09e0, 0x25c6}, // soliddiamond
{0x09e1, 0x2592}, // checkerboard
{0x09e2, 0x2409}, // ht
{0x09e3, 0x240c}, // ff
{0x09e4, 0x240d}, // cr
{0x09e5, 0x240a}, // lf
{0x09e8, 0x2424}, // nl
{0x09e9, 0x240b}, // vt
{0x09ea, 0x2518}, // lowrightcorner
{0x09eb, 0x2510}, // uprightcorner
{0x09ec, 0x250c}, // upleftcorner
{0x09ed, 0x2514}, // lowleftcorner
{0x09ee, 0x253c}, // crossinglines
{0x09ef, 0x23ba}, // horizlinescan1
{0x09f0, 0x23bb}, // horizlinescan3
{0x09f1, 0x2500}, // horizlinescan5
{0x09f2, 0x23bc}, // horizlinescan7
{0x09f3, 0x23bd}, // horizlinescan9
{0x09f4, 0x251c}, // leftt
{0x09f5, 0x2524}, // rightt
{0x09f6, 0x2534}, // bott
{0x09f7, 0x252c}, // topt
{0x09f8, 0x2502}, // vertbar
{0x0aa1, 0x2003}, // emspace
{0x0aa2, 0x2002}, // enspace
{0x0aa3, 0x2004}, // em3space
{0x0aa4, 0x2005}, // em4space
{0x0aa5, 0x2007}, // digitspace
{0x0aa6, 0x2008}, // punctspace
{0x0aa7, 0x2009}, // thinspace
{0x0aa8, 0x200a}, // hairspace
{0x0aa9, 0x2014}, // emdash
{0x0aaa, 0x2013}, // endash
{0x0aac, 0x2423}, // signifblank
{0x0aae, 0x2026}, // ellipsis
{0x0aaf, 0x2025}, // doubbaselinedot
{0x0ab0, 0x2153}, // onethird
{0x0ab1, 0x2154}, // twothirds
{0x0ab2, 0x2155}, // onefifth
{0x0ab3, 0x2156}, // twofifths
{0x0ab4, 0x2157}, // threefifths
{0x0ab5, 0x2158}, // fourfifths
{0x0ab6, 0x2159}, // onesixth
{0x0ab7, 0x215a}, // fivesixths
{0x0ab8, 0x2105}, // careof
{0x0abb, 0x2012}, // figdash
{0x0abc, 0x2329}, // leftanglebracket (not U+27E8)
{0x0abd, 0x002e}, // decimalpoint
{0x0abe, 0x232a}, // rightanglebracket (not U+27E9)
{0x0ac3, 0x215b}, // oneeighth
{0x0ac4, 0x215c}, // threeeighths
{0x0ac5, 0x215d}, // fiveeighths
{0x0ac6, 0x215e}, // seveneighths
{0x0ac9, 0x2122}, // trademark
{0x0aca, 0x2613}, // signaturemark
{0x0acc, 0x25c1}, // leftopentriangle
{0x0acd, 0x25b7}, // rightopentriangle
{0x0ace, 0x25cb}, // emopencircle
{0x0acf, 0x25af}, // emopenrectangle
{0x0ad0, 0x2018}, // leftsinglequotemark
{0x0ad1, 0x2019}, // rightsinglequotemark
{0x0ad2, 0x201c}, // leftdoublequotemark
{0x0ad3, 0x201d}, // rightdoublequotemark
{0x0ad4, 0x211e}, // prescription
{0x0ad5, 0x2030}, // per mille
{0x0ad6, 0x2032}, // minutes
{0x0ad7, 0x2033}, // seconds
{0x0ad9, 0x271d}, // latincross
{0x0adb, 0x25ac}, // filledrectbullet
{0x0adc, 0x25c0}, // filledlefttribullet
{0x0add, 0x25b6}, // filledrighttribullet
{0x0ade, 0x25cf}, // emfilledcircle
{0x0adf, 0x25ae}, // emfilledrect
{0x0ae0, 0x25e6}, // enopencircbullet
{0x0ae1, 0x25ab}, // enopensquarebullet
{0x0ae2, 0x25ad}, // openrectbullet
{0x0ae3, 0x25b3}, // opentribulletup
{0x0ae4, 0x25bd}, // opentribulletdown
{0x0ae5, 0x2606}, // openstar
{0x0ae6, 0x2022}, // enfilledcircbullet
{0x0ae7, 0x25aa}, // enfilledsqbullet
{0x0ae8, 0x25b2}, // filledtribulletup
{0x0ae9, 0x25bc}, // filledtribulletdown
{0x0aea, 0x261c}, // leftpointer
{0x0aeb, 0x261e}, // rightpointer
{0x0aec, 0x2663}, // club
{0x0aed, 0x2666}, // diamond
{0x0aee, 0x2665}, // heart
{0x0af0, 0x2720}, // maltesecross
{0x0af1, 0x2020}, // dagger
{0x0af2, 0x2021}, // doubledagger
{0x0af3, 0x2713}, // checkmark
{0x0af4, 0x2717}, // ballotcross
{0x0af5, 0x266f}, // musicalsharp
{0x0af6, 0x266d}, // musicalflat
{0x0af7, 0x2642}, // malesymbol
{0x0af8, 0x2640}, // femalesymbol
{0x0af9, 0x260e}, // telephone
{0x0afa, 0x2315}, // telephonerecorder
{0x0afb, 0x2117}, // phonographcopyright
{0x0afc, 0x2038}, // caret
{0x0afd, 0x201a}, // singlelowquotemark
{0x0afe, 0x201e}, // doublelowquotemark
{0x0ba3, 0x003c}, // leftcaret
{0x0ba6, 0x003e}, // rightcaret
{0x0ba8, 0x2228}, // downcaret
{0x0ba9, 0x2227}, // upcaret
{0x0bc0, 0x00af}, // overbar
{0x0bc2, 0x22a4}, // downtack
{0x0bc3, 0x2229}, // upshoe
{0x0bc4, 0x230a}, // downstile
{0x0bc6, 0x005f}, // underbar
{0x0bca, 0x2218}, // jot
{0x0bcc, 0x2395}, // quad
{0x0bce, 0x22a5}, // uptack
{0x0bcf, 0x25cb}, // circle
{0x0bd3, 0x2308}, // upstile
{0x0bd6, 0x222a}, // downshoe
{0x0bd8, 0x2283}, // rightshoe
{0x0bda, 0x2282}, // leftshoe
{0x0bdc, 0x22a3}, // lefttack
{0x0bfc, 0x22a2}, // righttack
{0x0cdf, 0x2017}, // hebrew_doublelowline
{0x0ce0, 0x05d0}, // hebrew_aleph
{0x0ce1, 0x05d1}, // hebrew_bet
{0x0ce2, 0x05d2}, // hebrew_gimel
{0x0ce3, 0x05d3}, // hebrew_dalet
{0x0ce4, 0x05d4}, // hebrew_he
{0x0ce5, 0x05d5}, // hebrew_waw
{0x0ce6, 0x05d6}, // hebrew_zain
{0x0ce7, 0x05d7}, // hebrew_chet
{0x0ce8, 0x05d8}, // hebrew_tet
{0x0ce9, 0x05d9}, // hebrew_yod
{0x0cea, 0x05da}, // hebrew_finalkaph
{0x0ceb, 0x05db}, // hebrew_kaph
{0x0cec, 0x05dc}, // hebrew_lamed
{0x0ced, 0x05dd}, // hebrew_finalmem
{0x0cee, 0x05de}, // hebrew_mem
{0x0cef, 0x05df}, // hebrew_finalnun
{0x0cf0, 0x05e0}, // hebrew_nun
{0x0cf1, 0x05e1}, // hebrew_samech
{0x0cf2, 0x05e2}, // hebrew_ayin
{0x0cf3, 0x05e3}, // hebrew_finalpe
{0x0cf4, 0x05e4}, // hebrew_pe
{0x0cf5, 0x05e5}, // hebrew_finalzade
{0x0cf6, 0x05e6}, // hebrew_zade
{0x0cf7, 0x05e7}, // hebrew_qoph
{0x0cf8, 0x05e8}, // hebrew_resh
{0x0cf9, 0x05e9}, // hebrew_shin
{0x0cfa, 0x05ea}, // hebrew_taw
{0x0da1, 0x0e01}, // Thai_kokai
{0x0da2, 0x0e02}, // Thai_khokhai
{0x0da3, 0x0e03}, // Thai_khokhuat
{0x0da4, 0x0e04}, // Thai_khokhwai
{0x0da5, 0x0e05}, // Thai_khokhon
{0x0da6, 0x0e06}, // Thai_khorakhang
{0x0da7, 0x0e07}, // Thai_ngongu
{0x0da8, 0x0e08}, // Thai_chochan
{0x0da9, 0x0e09}, // Thai_choching
{0x0daa, 0x0e0a}, // Thai_chochang
{0x0dab, 0x0e0b}, // Thai_soso
{0x0dac, 0x0e0c}, // Thai_chochoe
{0x0dad, 0x0e0d}, // Thai_yoying
{0x0dae, 0x0e0e}, // Thai_dochada
{0x0daf, 0x0e0f}, // Thai_topatak
{0x0db0, 0x0e10}, // Thai_thothan
{0x0db1, 0x0e11}, // Thai_thonangmontho
{0x0db2, 0x0e12}, // Thai_thophuthao
{0x0db3, 0x0e13}, // Thai_nonen
{0x0db4, 0x0e14}, // Thai_dodek
{0x0db5, 0x0e15}, // Thai_totao
{0x0db6, 0x0e16}, // Thai_thothung
{0x0db7, 0x0e17}, // Thai_thothahan
{0x0db8, 0x0e18}, // Thai_thothong
{0x0db9, 0x0e19}, // Thai_nonu
{0x0dba, 0x0e1a}, // Thai_bobaimai
{0x0dbb, 0x0e1b}, // Thai_popla
{0x0dbc, 0x0e1c}, // Thai_phophung
{0x0dbd, 0x0e1d}, // Thai_fofa
{0x0dbe, 0x0e1e}, // Thai_phophan
{0x0dbf, 0x0e1f}, // Thai_fofan
{0x0dc0, 0x0e20}, // Thai_phosamphao
{0x0dc1, 0x0e21}, // Thai_moma
{0x0dc2, 0x0e22}, // Thai_yoyak
{0x0dc3, 0x0e23}, // Thai_rorua
{0x0dc4, 0x0e24}, // Thai_ru
{0x0dc5, 0x0e25}, // Thai_loling
{0x0dc6, 0x0e26}, // Thai_lu
{0x0dc7, 0x0e27}, // Thai_wowaen
{0x0dc8, 0x0e28}, // Thai_sosala
{0x0dc9, 0x0e29}, // Thai_sorusi
{0x0dca, 0x0e2a}, // Thai_sosua
{0x0dcb, 0x0e2b}, // Thai_hohip
{0x0dcc, 0x0e2c}, // Thai_lochula
{0x0dcd, 0x0e2d}, // Thai_oang
{0x0dce, 0x0e2e}, // Thai_honokhuk
{0x0dcf, 0x0e2f}, // Thai_paiyannoi
{0x0dd0, 0x0e30}, // Thai_saraa
{0x0dd1, 0x0e31}, // Thai_maihanakat
{0x0dd2, 0x0e32}, // Thai_saraaa
{0x0dd3, 0x0e33}, // Thai_saraam
{0x0dd4, 0x0e34}, // Thai_sarai
{0x0dd5, 0x0e35}, // Thai_saraii
{0x0dd6, 0x0e36}, // Thai_saraue
{0x0dd7, 0x0e37}, // Thai_sarauee
{0x0dd8, 0x0e38}, // Thai_sarau
{0x0dd9, 0x0e39}, // Thai_sarauu
{0x0dda, 0x0e3a}, // Thai_phinthu
{0x0ddf, 0x0e3f}, // Thai_baht
{0x0de0, 0x0e40}, // Thai_sarae
{0x0de1, 0x0e41}, // Thai_saraae
{0x0de2, 0x0e42}, // Thai_sarao
{0x0de3, 0x0e43}, // Thai_saraaimaimuan
{0x0de4, 0x0e44}, // Thai_saraaimaimalai
{0x0de5, 0x0e45}, // Thai_lakkhangyao
{0x0de6, 0x0e46}, // Thai_maiyamok
{0x0de7, 0x0e47}, // Thai_maitaikhu
{0x0de8, 0x0e48}, // Thai_maiek
{0x0de9, 0x0e49}, // Thai_maitho
{0x0dea, 0x0e4a}, // Thai_maitri
{0x0deb, 0x0e4b}, // Thai_maichattawa
{0x0dec, 0x0e4c}, // Thai_thanthakhat
{0x0ded, 0x0e4d}, // Thai_nikhahit
{0x0df0, 0x0e50}, // Thai_leksun
{0x0df1, 0x0e51}, // Thai_leknung
{0x0df2, 0x0e52}, // Thai_leksong
{0x0df3, 0x0e53}, // Thai_leksam
{0x0df4, 0x0e54}, // Thai_leksi
{0x0df5, 0x0e55}, // Thai_lekha
{0x0df6, 0x0e56}, // Thai_lekhok
{0x0df7, 0x0e57}, // Thai_lekchet
{0x0df8, 0x0e58}, // Thai_lekpaet
{0x0df9, 0x0e59}, // Thai_lekkao
{0x0ea1, 0x3131}, // Hangul_Kiyeog
{0x0ea2, 0x3132}, // Hangul_SsangKiyeog
{0x0ea3, 0x3133}, // Hangul_KiyeogSios
{0x0ea4, 0x3134}, // Hangul_Nieun
{0x0ea5, 0x3135}, // Hangul_NieunJieuj
{0x0ea6, 0x3136}, // Hangul_NieunHieuh
{0x0ea7, 0x3137}, // Hangul_Dikeud
{0x0ea8, 0x3138}, // Hangul_SsangDikeud
{0x0ea9, 0x3139}, // Hangul_Rieul
{0x0eaa, 0x313a}, // Hangul_RieulKiyeog
{0x0eab, 0x313b}, // Hangul_RieulMieum
{0x0eac, 0x313c}, // Hangul_RieulPieub
{0x0ead, 0x313d}, // Hangul_RieulSios
{0x0eae, 0x313e}, // Hangul_RieulTieut
{0x0eaf, 0x313f}, // Hangul_RieulPhieuf
{0x0eb0, 0x3140}, // Hangul_RieulHieuh
{0x0eb1, 0x3141}, // Hangul_Mieum
{0x0eb2, 0x3142}, // Hangul_Pieub
{0x0eb3, 0x3143}, // Hangul_SsangPieub
{0x0eb4, 0x3144}, // Hangul_PieubSios
{0x0eb5, 0x3145}, // Hangul_Sios
{0x0eb6, 0x3146}, // Hangul_SsangSios
{0x0eb7, 0x3147}, // Hangul_Ieung
{0x0eb8, 0x3148}, // Hangul_Jieuj
{0x0eb9, 0x3149}, // Hangul_SsangJieuj
{0x0eba, 0x314a}, // Hangul_Cieuc
{0x0ebb, 0x314b}, // Hangul_Khieuq
{0x0ebc, 0x314c}, // Hangul_Tieut
{0x0ebd, 0x314d}, // Hangul_Phieuf
{0x0ebe, 0x314e}, // Hangul_Hieuh
{0x0ebf, 0x314f}, // Hangul_A
{0x0ec0, 0x3150}, // Hangul_AE
{0x0ec1, 0x3151}, // Hangul_YA
{0x0ec2, 0x3152}, // Hangul_YAE
{0x0ec3, 0x3153}, // Hangul_EO
{0x0ec4, 0x3154}, // Hangul_E
{0x0ec5, 0x3155}, // Hangul_YEO
{0x0ec6, 0x3156}, // Hangul_YE
{0x0ec7, 0x3157}, // Hangul_O
{0x0ec8, 0x3158}, // Hangul_WA
{0x0ec9, 0x3159}, // Hangul_WAE
{0x0eca, 0x315a}, // Hangul_OE
{0x0ecb, 0x315b}, // Hangul_YO
{0x0ecc, 0x315c}, // Hangul_U
{0x0ecd, 0x315d}, // Hangul_WEO
{0x0ece, 0x315e}, // Hangul_WE
{0x0ecf, 0x315f}, // Hangul_WI
{0x0ed0, 0x3160}, // Hangul_YU
{0x0ed1, 0x3161}, // Hangul_EU
{0x0ed2, 0x3162}, // Hangul_YI
{0x0ed3, 0x3163}, // Hangul_I
{0x0ed4, 0x11a8}, // Hangul_J_Kiyeog
{0x0ed5, 0x11a9}, // Hangul_J_SsangKiyeog
{0x0ed6, 0x11aa}, // Hangul_J_KiyeogSios
{0x0ed7, 0x11ab}, // Hangul_J_Nieun
{0x0ed8, 0x11ac}, // Hangul_J_NieunJieuj
{0x0ed9, 0x11ad}, // Hangul_J_NieunHieuh
{0x0eda, 0x11ae}, // Hangul_J_Dikeud
{0x0edb, 0x11af}, // Hangul_J_Rieul
{0x0edc, 0x11b0}, // Hangul_J_RieulKiyeog
{0x0edd, 0x11b1}, // Hangul_J_RieulMieum
{0x0ede, 0x11b2}, // Hangul_J_RieulPieub
{0x0edf, 0x11b3}, // Hangul_J_RieulSios
{0x0ee0, 0x11b4}, // Hangul_J_RieulTieut
{0x0ee1, 0x11b5}, // Hangul_J_RieulPhieuf
{0x0ee2, 0x11b6}, // Hangul_J_RieulHieuh
{0x0ee3, 0x11b7}, // Hangul_J_Mieum
{0x0ee4, 0x11b8}, // Hangul_J_Pieub
{0x0ee5, 0x11b9}, // Hangul_J_PieubSios
{0x0ee6, 0x11ba}, // Hangul_J_Sios
{0x0ee7, 0x11bb}, // Hangul_J_SsangSios
{0x0ee8, 0x11bc}, // Hangul_J_Ieung
{0x0ee9, 0x11bd}, // Hangul_J_Jieuj
{0x0eea, 0x11be}, // Hangul_J_Cieuc
{0x0eeb, 0x11bf}, // Hangul_J_Khieuq
{0x0eec, 0x11c0}, // Hangul_J_Tieut
{0x0eed, 0x11c1}, // Hangul_J_Phieuf
{0x0eee, 0x11c2}, // Hangul_J_Hieuh
{0x0eef, 0x316d}, // Hangul_RieulYeorinHieuh
{0x0ef0, 0x3171}, // Hangul_SunkyeongeumMieum
{0x0ef1, 0x3178}, // Hangul_SunkyeongeumPieub
{0x0ef2, 0x317f}, // Hangul_PanSios
{0x0ef3, 0x3181}, // Hangul_KkogjiDalrinIeung
{0x0ef4, 0x3184}, // Hangul_SunkyeongeumPhieuf
{0x0ef5, 0x3186}, // Hangul_YeorinHieuh
{0x0ef6, 0x318d}, // Hangul_AraeA
{0x0ef7, 0x318e}, // Hangul_AraeAE
{0x0ef8, 0x11eb}, // Hangul_J_PanSios
{0x0ef9, 0x11f0}, // Hangul_J_KkogjiDalrinIeung
{0x0efa, 0x11f9}, // Hangul_J_YeorinHieuh
{0x0eff, 0x20a9}, // Korean_Won
{0x13a4, 0x20ac}, // Euro
{0x13bc, 0x0152}, // OE
{0x13bd, 0x0153}, // oe
{0x13be, 0x0178}, // Ydiaeresis
{0x20a0, 0x20a0}, // EcuSign
{0x20a1, 0x20a1}, // ColonSign
{0x20a2, 0x20a2}, // CruzeiroSign
{0x20a3, 0x20a3}, // FFrancSign
{0x20a4, 0x20a4}, // LiraSign
{0x20a5, 0x20a5}, // MillSign
{0x20a6, 0x20a6}, // NairaSign
{0x20a7, 0x20a7}, // PesetaSign
{0x20a8, 0x20a8}, // RupeeSign
{0x20a9, 0x20a9}, // WonSign
{0x20aa, 0x20aa}, // NewSheqelSign
{0x20ab, 0x20ab}, // DongSign
{0x20ac, 0x20ac}, // EuroSign
{0xfe50, 0x0300}, // dead_grave
{0xfe51, 0x0301}, // dead_acute
{0xfe52, 0x0302}, // dead_circumflex
{0xfe53, 0x0303}, // dead_tilde
{0xfe54, 0x0304}, // dead_macron
{0xfe55, 0x0306}, // dead_breve
{0xfe56, 0x0307}, // dead_abovedot
{0xfe57, 0x0308}, // dead_diaeresis
{0xfe58, 0x030a}, // dead_abovering
{0xfe59, 0x030b}, // dead_doubleacute
{0xfe5a, 0x030c}, // dead_caron
{0xfe5b, 0x0327}, // dead_cedilla
{0xfe5c, 0x0328}, // dead_ogonek
{0xfe5d, 0x0345}, // dead_iota
{0xfe5e, 0x3099}, // dead_voiced_sound
{0xfe5f, 0x309a}, // dead_semivoiced_sound
{0xff08, 0x0008}, // BackSpace /* back space, back char */
{0xff09, 0x0009}, // Tab
{0xff0a, 0x000a}, // Linefeed /* Linefeed, LF */
{0xff0b, 0x000b}, // Clear
{0xff0d, 0x000d}, // Return /* Return, enter */
{0xff13, 0x0013}, // Pause /* Pause, hold */
{0xff14, 0x0014}, // Scroll_Lock
{0xff15, 0x0015}, // Sys_Req
{0xff1b, 0x001b}, // Escape
{0xff80, 0x0032}, // KP_Space /* space */
{0xff89, 0x0009}, // KP_Tab
{0xff8d, 0x000d}, // KP_Enter /* enter */
{0xffaa, 0x002a}, // KP_Multiply
{0xffab, 0x002b}, // KP_Add
{0xffac, 0x002c}, // KP_Separator /* separator, often comma */
{0xffad, 0x002d}, // KP_Subtract
{0xffae, 0x002e}, // KP_Decimal
{0xffaf, 0x002f}, // KP_Divide
{0xffb0, 0x0030}, // KP_0
{0xffb1, 0x0031}, // KP_1
{0xffb2, 0x0032}, // KP_2
{0xffb3, 0x0033}, // KP_3
{0xffb4, 0x0034}, // KP_4
{0xffb5, 0x0035}, // KP_5
{0xffb6, 0x0036}, // KP_6
{0xffb7, 0x0037}, // KP_7
{0xffb8, 0x0038}, // KP_8
{0xffb9, 0x0039}, // KP_9
{0xffbd, 0x003d}, // KP_Equal /* equals */
}
+2
View File
@@ -0,0 +1,2 @@
xgbgen/xgbgen
.*.swp
+26
View File
@@ -0,0 +1,26 @@
# This is the official list of neXGB authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS file.
# See the latter for an explanation.
# Names should be added to this file as
# Name or Organization <email address>
# The email address is not required for organizations.
Anthony Martin <ality@pbrane.org>
Firmansyah Adiputra <frm.adiputra@gmail.com>
Google Inc.
Scott Lawrence <bytbox@gmail.com>
Tor Andersson <tor.andersson@gmail.com>
# The names above come from the original x-go-binding by Google.
# The following list pertains to BurntSushi/xgb and janouch.name/haven/nexgb:
Andrew Gallant <jamslam@gmail.com>
Paul Sbarra <Sbarra.Paul@gmail.com>
Axel Wagner <mail@merovius.de>
snyh <snyh@snyh.org>
Alessandro Arzilli <alessandro.arzilli@gmail.com>
fangyuanziti <tiziyuanfang@gmail.com>
Bryan Matsuo <bryan.matsuo@gmail.com>
Rabin Vincent <rabin@rab.in>
Přemysl Eric Janouch <p@janouch.name>
+35
View File
@@ -0,0 +1,35 @@
# This is the official list of people who may have contributed
# code to the neXGB repository.
#
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file.
# Names should be added to this file like so:
# Name <email address>
Anthony Martin <ality@pbrane.org>
Firmansyah Adiputra <frm.adiputra@gmail.com>
Ian Lance Taylor <iant@golang.org>
Nigel Tao <nigeltao@golang.org>
Robert Griesemer <gri@golang.org>
Russ Cox <rsc@golang.org>
Scott Lawrence <bytbox@gmail.com>
Tor Andersson <tor.andersson@gmail.com>
# The names above come from the original x-go-binding by Google.
# The following list pertains to BurntSushi/xgb and janouch.name/haven/nexgb:
Andrew Gallant <jamslam@gmail.com>
Paul Sbarra <Sbarra.Paul@gmail.com>
Axel Wagner <mail@merovius.de>
snyh <snyh@snyh.org>
Alessandro Arzilli <alessandro.arzilli@gmail.com>
fangyuanziti <tiziyuanfang@gmail.com>
Bryan Matsuo <bryan.matsuo@gmail.com>
Rabin Vincent <rabin@rab.in>
Přemysl Eric Janouch <p@janouch.name>
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2009 The XGB Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+76
View File
@@ -0,0 +1,76 @@
# This Makefile is used by the developer. It is not needed in any way to build
# a checkout of the neXGB repository.
# It will be useful, however, if you are hacking at the code generator.
# i.e., after making a change to the code generator, run 'make' in the
# xgb directory. This will build xgbgen and regenerate each sub-package.
# 'make test' will then run any appropriate tests (just tests xproto right now).
# 'make bench' will test a couple of benchmarks.
# 'make build-all' will then try to build each extension. This isn't strictly
# necessary, but it's a good idea to make sure each sub-package is a valid
# Go package.
# My path to the X protocol XML descriptions.
ifndef XPROTO
XPROTO=/usr/share/xcb
endif
# All of the XML files in my /usr/share/xcb directory except XKB, Xinput, SYNC.
# This is intended to build xgbgen and generate Go code for each supported
# extension.
all: build-xgbgen \
bigreq.xml composite.xml damage.xml dpms.xml dri2.xml \
ge.xml glx.xml randr.xml record.xml render.xml res.xml \
screensaver.xml shape.xml shm.xml xc_misc.xml \
xevie.xml xf86dri.xml xf86vidmode.xml xfixes.xml xinerama.xml \
xprint.xml xproto.xml xselinux.xml xtest.xml \
xvmc.xml xv.xml
build-xgbgen:
(cd xgbgen && go build)
# Builds each individual sub-package to make sure its valid Go code.
build-all: bigreq.b composite.b damage.b dpms.b dri2.b ge.b glx.b randr.b \
record.b render.b res.b screensaver.b shape.b shm.b xcmisc.b \
xevie.b xf86dri.b xf86vidmode.b xfixes.b xinerama.b \
xprint.b xproto.b xselinux.b xtest.b xv.b xvmc.b
%.b:
(cd $* ; go build)
# Installs each individual sub-package.
install: bigreq.i composite.i damage.i dpms.i dri2.i ge.i glx.i randr.i \
record.i render.i res.i screensaver.i shape.i shm.i xcmisc.i \
xevie.i xf86dri.i xf86vidmode.i xfixes.i xinerama.i \
xprint.i xproto.i xselinux.i xtest.i xv.i xvmc.i
go install
%.i:
(cd $* ; go install)
# xc_misc is special because it has an underscore.
# There's probably a way to do this better, but Makefiles aren't my strong suit.
xc_misc.xml: build-xgbgen
mkdir -p xcmisc
xgbgen/xgbgen --proto-path $(XPROTO) $(XPROTO)/xc_misc.xml > xcmisc/xcmisc.go
%.xml: build-xgbgen
mkdir -p $*
xgbgen/xgbgen --proto-path $(XPROTO) $(XPROTO)/$*.xml > $*/$*.go
# Just test the xproto core protocol for now.
test:
(cd xproto ; go test)
# Force all xproto benchmarks to run and no tests.
bench:
(cd xproto ; go test -run 'nomatch' -bench '.*' -cpu 1,2,3,6)
# gofmt all non-auto-generated code.
# (auto-generated code is already gofmt'd.)
# Also do a column check (80 cols) after a gofmt.
# But don't check columns on auto-generated code, since I don't care if they
# break 80 cols.
gofmt:
gofmt -w *.go xgbgen/*.go examples/*.go examples/*/*.go xproto/xproto_test.go
colcheck *.go xgbgen/*.go examples/*.go examples/*/*.go xproto/xproto_test.go
+49
View File
@@ -0,0 +1,49 @@
neXGB is a fork of a fork of the X Go Binding, which is a low-level API to
communicate with the core X protocol and many of the X extensions.
It is closely modelled after XCB and xpyb.
It is thread safe and gets immediate improvement from parallelism when
GOMAXPROCS > 1. (See the benchmarks in xproto/xproto_test.go for evidence.)
Please see doc.go for more info.
Quick usage
-----------
go get janouch.name/haven/nexgb
go run $GOPATH/src/janouch.name/haven/nexgb/examples/create-window/main.go
Přemysl Eric Janouch's fork
---------------------------
I've merged BurntSushi/xgb into haven as a subdirectory due to a/ inactivity
upstream, and b/ intentions to make incompatible changes meant to be in sync
with the rest of the project.
It's Not Exactly XGB anymore. Notable changes:
- included aarzilli's changes to support xcb-proto 1.12+, updated to 1.13
- improved documentation, using as much as possible from XCB's <doc> elements
- exporting {Major,Minor}Version of extensions for QueryVersion purposes
BurntSushi's fork
-----------------
I've forked the XGB repository from Google Code due to inactivity upstream.
Much of the code has been rewritten in an effort to support thread safety
and multiple extensions. Namely, go_client.py has been thrown away in favor
of an xgbgen package.
The biggest parts that *haven't* been rewritten by me are the connection and
authentication handshakes. They're inherently messy, and there's really no
reason to re-work them. The rest of XGB has been completely rewritten.
I like to release my code under the WTFPL, but since I'm starting with someone
else's work, I'm leaving the original license/contributor/author information
in tact.
I suppose I can legitimately release xgbgen under the WTFPL. To be fair, it is
at least as complex as XGB itself. *sigh*
License
-------
Unless otherwise noted, the neXGB source files are distributed
under the BSD-style license found in the LICENSE file.
+29
View File
@@ -0,0 +1,29 @@
Keep all code to 80 columns or less. We, the maintainers, have plenty of screen
real estate, but enjoy 80 columns so that we can have multiple code windows
open side to side and not be plagued by the ugly auto-wrapping of a text editor.
If you don't oblige us, we will fix any patch you submit to abide 80 columns.
Note that this style restriction does not preclude gofmt, but introduces a few
peculiarities. The first is that gofmt will occasionally add spacing (typically
to comments) that ends up going over 80 columns. Either shorten the comment or
put it on its own line.
The second and more common hiccup is when a function definition extends beyond
80 columns. If one adds line breaks to keep it below 80 columns, gofmt will
indent all subsequent lines in a function definition to the same indentation
level of the function body. This results in a less-than-ideal separation
between function definition and function body. To remedy this, simply add a
line break like so:
func RestackWindowExtra(xu *xgbutil.XUtil, win xproto.Window, stackMode int,
sibling xproto.Window, source int) error {
return ClientEvent(xu, win, "_NET_RESTACK_WINDOW", source, int(sibling),
stackMode)
}
Something similar should also be applied to long 'if' or 'for' conditionals,
although it would probably be preferrable to break up the conditional to
smaller chunks with a few helper variables.
+110
View File
@@ -0,0 +1,110 @@
package nexgb
/*
auth.go contains functions to facilitate the parsing of .Xauthority files.
It is largely unmodified from the original XGB package that I forked.
*/
import (
"encoding/binary"
"errors"
"io"
"os"
)
// readAuthority reads the X authority file for the DISPLAY.
// If hostname == "" or hostname == "localhost",
// then use the system's hostname (as returned by os.Hostname) instead.
func readAuthority(hostname, display string) (
name string, data []byte, err error) {
// b is a scratch buffer to use and should be at least 256 bytes long
// (i.e. it should be able to hold a hostname).
b := make([]byte, 256)
// As per /usr/include/X11/Xauth.h.
const familyLocal = 256
const familyWild = 65535
if len(hostname) == 0 || hostname == "localhost" {
hostname, err = os.Hostname()
if err != nil {
return "", nil, err
}
}
fname := os.Getenv("XAUTHORITY")
if len(fname) == 0 {
home := os.Getenv("HOME")
if len(home) == 0 {
err = errors.New("Xauthority not found: $XAUTHORITY, $HOME not set")
return "", nil, err
}
fname = home + "/.Xauthority"
}
r, err := os.Open(fname)
if err != nil {
return "", nil, err
}
defer r.Close()
for {
var family uint16
if err := binary.Read(r, binary.BigEndian, &family); err != nil {
return "", nil, err
}
addr, err := getString(r, b)
if err != nil {
return "", nil, err
}
disp, err := getString(r, b)
if err != nil {
return "", nil, err
}
name0, err := getString(r, b)
if err != nil {
return "", nil, err
}
data0, err := getBytes(r, b)
if err != nil {
return "", nil, err
}
addrmatch := (family == familyWild) ||
(family == familyLocal && addr == hostname)
dispmatch := (disp == "") || (disp == display)
if addrmatch && dispmatch {
return name0, data0, nil
}
}
panic("unreachable")
}
func getBytes(r io.Reader, b []byte) ([]byte, error) {
var n uint16
if err := binary.Read(r, binary.BigEndian, &n); err != nil {
return nil, err
} else if n > uint16(len(b)) {
return nil, errors.New("bytes too long for buffer")
}
if _, err := io.ReadFull(r, b[0:n]); err != nil {
return nil, err
}
return b[0:n], nil
}
func getString(r io.Reader, b []byte) (string, error) {
b, err := getBytes(r, b)
if err != nil {
return "", err
}
return string(b), nil
}
+156
View File
@@ -0,0 +1,156 @@
// Package bigreq is the X client API for the BIG-REQUESTS extension.
package bigreq
// This file is automatically generated from bigreq.xml. Edit at your peril!
import (
xgb "janouch.name/haven/nexgb"
"janouch.name/haven/nexgb/xproto"
)
const (
MajorVersion = 0
MinorVersion = 0
)
// Init must be called before using the BIG-REQUESTS extension.
func Init(c *xgb.Conn) error {
reply, err := xproto.QueryExtension(c, 12, "BIG-REQUESTS").Reply()
switch {
case err != nil:
return err
case !reply.Present:
return xgb.Errorf("No extension named BIG-REQUESTS could be found on on the server.")
}
c.ExtLock.Lock()
c.Extensions["BIG-REQUESTS"] = reply.MajorOpcode
c.ExtLock.Unlock()
for evNum, fun := range xgb.NewExtEventFuncs["BIG-REQUESTS"] {
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
}
for errNum, fun := range xgb.NewExtErrorFuncs["BIG-REQUESTS"] {
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
}
return nil
}
func init() {
xgb.NewExtEventFuncs["BIG-REQUESTS"] = make(map[int]xgb.NewEventFun)
xgb.NewExtErrorFuncs["BIG-REQUESTS"] = make(map[int]xgb.NewErrorFun)
}
// Skipping definition for base type 'Bool'
// Skipping definition for base type 'Byte'
// Skipping definition for base type 'Card8'
// Skipping definition for base type 'Char'
// Skipping definition for base type 'Void'
// Skipping definition for base type 'Double'
// Skipping definition for base type 'Float'
// Skipping definition for base type 'Int16'
// Skipping definition for base type 'Int32'
// Skipping definition for base type 'Int8'
// Skipping definition for base type 'Card16'
// Skipping definition for base type 'Card32'
// EnableCookie is a cookie used only for Enable requests.
type EnableCookie struct {
*xgb.Cookie
}
// Enable sends a checked request.
// If an error occurs, it will be returned with the reply by calling EnableCookie.Reply.
func Enable(c *xgb.Conn) EnableCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["BIG-REQUESTS"]; !ok {
panic("Cannot issue request 'Enable' using the uninitialized extension 'BIG-REQUESTS'. bigreq.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(enableRequest(c), cookie)
return EnableCookie{cookie}
}
// EnableUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func EnableUnchecked(c *xgb.Conn) EnableCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["BIG-REQUESTS"]; !ok {
panic("Cannot issue request 'Enable' using the uninitialized extension 'BIG-REQUESTS'. bigreq.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(enableRequest(c), cookie)
return EnableCookie{cookie}
}
// EnableReply represents the data returned from a Enable request.
type EnableReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
// padding: 1 bytes
MaximumRequestLength uint32
}
// Reply blocks and returns the reply data for a Enable request.
func (cook EnableCookie) Reply() (*EnableReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return enableReply(buf), nil
}
// enableReply reads a byte slice into a EnableReply value.
func enableReply(buf []byte) *EnableReply {
v := new(EnableReply)
b := 1 // skip reply determinant
b += 1 // padding
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.MaximumRequestLength = xgb.Get32(buf[b:])
b += 4
return v
}
// enableRequest writes a Enable request to a byte slice for transfer.
func enableRequest(c *xgb.Conn) []byte {
size := 4
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["BIG-REQUESTS"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 0 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
return buf
}
+717
View File
@@ -0,0 +1,717 @@
// Package composite is the X client API for the Composite extension.
package composite
// This file is automatically generated from composite.xml. Edit at your peril!
import (
xgb "janouch.name/haven/nexgb"
"janouch.name/haven/nexgb/xfixes"
"janouch.name/haven/nexgb/xproto"
)
const (
MajorVersion = 0
MinorVersion = 4
)
// Init must be called before using the Composite extension.
func Init(c *xgb.Conn) error {
reply, err := xproto.QueryExtension(c, 9, "Composite").Reply()
switch {
case err != nil:
return err
case !reply.Present:
return xgb.Errorf("No extension named Composite could be found on on the server.")
}
c.ExtLock.Lock()
c.Extensions["Composite"] = reply.MajorOpcode
c.ExtLock.Unlock()
for evNum, fun := range xgb.NewExtEventFuncs["Composite"] {
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
}
for errNum, fun := range xgb.NewExtErrorFuncs["Composite"] {
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
}
return nil
}
func init() {
xgb.NewExtEventFuncs["Composite"] = make(map[int]xgb.NewEventFun)
xgb.NewExtErrorFuncs["Composite"] = make(map[int]xgb.NewErrorFun)
}
const (
RedirectAutomatic = 0
RedirectManual = 1
)
// Skipping definition for base type 'Bool'
// Skipping definition for base type 'Byte'
// Skipping definition for base type 'Card8'
// Skipping definition for base type 'Char'
// Skipping definition for base type 'Void'
// Skipping definition for base type 'Double'
// Skipping definition for base type 'Float'
// Skipping definition for base type 'Int16'
// Skipping definition for base type 'Int32'
// Skipping definition for base type 'Int8'
// Skipping definition for base type 'Card16'
// Skipping definition for base type 'Card32'
// CreateRegionFromBorderClipCookie is a cookie used only for CreateRegionFromBorderClip requests.
type CreateRegionFromBorderClipCookie struct {
*xgb.Cookie
}
// CreateRegionFromBorderClip sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func CreateRegionFromBorderClip(c *xgb.Conn, Region xfixes.Region, Window xproto.Window) CreateRegionFromBorderClipCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'CreateRegionFromBorderClip' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(createRegionFromBorderClipRequest(c, Region, Window), cookie)
return CreateRegionFromBorderClipCookie{cookie}
}
// CreateRegionFromBorderClipChecked sends a checked request.
// If an error occurs, it can be retrieved using CreateRegionFromBorderClipCookie.Check.
func CreateRegionFromBorderClipChecked(c *xgb.Conn, Region xfixes.Region, Window xproto.Window) CreateRegionFromBorderClipCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'CreateRegionFromBorderClip' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(createRegionFromBorderClipRequest(c, Region, Window), cookie)
return CreateRegionFromBorderClipCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook CreateRegionFromBorderClipCookie) Check() error {
return cook.Cookie.Check()
}
// createRegionFromBorderClipRequest writes a CreateRegionFromBorderClip request to a byte slice for transfer.
func createRegionFromBorderClipRequest(c *xgb.Conn, Region xfixes.Region, Window xproto.Window) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["Composite"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 5 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Region))
b += 4
xgb.Put32(buf[b:], uint32(Window))
b += 4
return buf
}
// GetOverlayWindowCookie is a cookie used only for GetOverlayWindow requests.
type GetOverlayWindowCookie struct {
*xgb.Cookie
}
// GetOverlayWindow sends a checked request.
// If an error occurs, it will be returned with the reply by calling GetOverlayWindowCookie.Reply.
func GetOverlayWindow(c *xgb.Conn, Window xproto.Window) GetOverlayWindowCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'GetOverlayWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(getOverlayWindowRequest(c, Window), cookie)
return GetOverlayWindowCookie{cookie}
}
// GetOverlayWindowUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func GetOverlayWindowUnchecked(c *xgb.Conn, Window xproto.Window) GetOverlayWindowCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'GetOverlayWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(getOverlayWindowRequest(c, Window), cookie)
return GetOverlayWindowCookie{cookie}
}
// GetOverlayWindowReply represents the data returned from a GetOverlayWindow request.
type GetOverlayWindowReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
// padding: 1 bytes
OverlayWin xproto.Window
// padding: 20 bytes
}
// Reply blocks and returns the reply data for a GetOverlayWindow request.
func (cook GetOverlayWindowCookie) Reply() (*GetOverlayWindowReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return getOverlayWindowReply(buf), nil
}
// getOverlayWindowReply reads a byte slice into a GetOverlayWindowReply value.
func getOverlayWindowReply(buf []byte) *GetOverlayWindowReply {
v := new(GetOverlayWindowReply)
b := 1 // skip reply determinant
b += 1 // padding
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.OverlayWin = xproto.Window(xgb.Get32(buf[b:]))
b += 4
b += 20 // padding
return v
}
// getOverlayWindowRequest writes a GetOverlayWindow request to a byte slice for transfer.
func getOverlayWindowRequest(c *xgb.Conn, Window xproto.Window) []byte {
size := 8
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["Composite"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 7 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
return buf
}
// NameWindowPixmapCookie is a cookie used only for NameWindowPixmap requests.
type NameWindowPixmapCookie struct {
*xgb.Cookie
}
// NameWindowPixmap sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func NameWindowPixmap(c *xgb.Conn, Window xproto.Window, Pixmap xproto.Pixmap) NameWindowPixmapCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'NameWindowPixmap' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(nameWindowPixmapRequest(c, Window, Pixmap), cookie)
return NameWindowPixmapCookie{cookie}
}
// NameWindowPixmapChecked sends a checked request.
// If an error occurs, it can be retrieved using NameWindowPixmapCookie.Check.
func NameWindowPixmapChecked(c *xgb.Conn, Window xproto.Window, Pixmap xproto.Pixmap) NameWindowPixmapCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'NameWindowPixmap' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(nameWindowPixmapRequest(c, Window, Pixmap), cookie)
return NameWindowPixmapCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook NameWindowPixmapCookie) Check() error {
return cook.Cookie.Check()
}
// nameWindowPixmapRequest writes a NameWindowPixmap request to a byte slice for transfer.
func nameWindowPixmapRequest(c *xgb.Conn, Window xproto.Window, Pixmap xproto.Pixmap) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["Composite"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 6 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
xgb.Put32(buf[b:], uint32(Pixmap))
b += 4
return buf
}
// QueryVersionCookie is a cookie used only for QueryVersion requests.
type QueryVersionCookie struct {
*xgb.Cookie
}
// QueryVersion sends a checked request.
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply.
func QueryVersion(c *xgb.Conn, ClientMajorVersion, ClientMinorVersion uint32) QueryVersionCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
return QueryVersionCookie{cookie}
}
// QueryVersionUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func QueryVersionUnchecked(c *xgb.Conn, ClientMajorVersion, ClientMinorVersion uint32) QueryVersionCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
return QueryVersionCookie{cookie}
}
// QueryVersionReply represents the data returned from a QueryVersion request.
type QueryVersionReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
// padding: 1 bytes
MajorVersion uint32
MinorVersion uint32
// padding: 16 bytes
}
// Reply blocks and returns the reply data for a QueryVersion request.
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return queryVersionReply(buf), nil
}
// queryVersionReply reads a byte slice into a QueryVersionReply value.
func queryVersionReply(buf []byte) *QueryVersionReply {
v := new(QueryVersionReply)
b := 1 // skip reply determinant
b += 1 // padding
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.MajorVersion = xgb.Get32(buf[b:])
b += 4
v.MinorVersion = xgb.Get32(buf[b:])
b += 4
b += 16 // padding
return v
}
// queryVersionRequest writes a QueryVersion request to a byte slice for transfer.
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion, ClientMinorVersion uint32) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["Composite"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 0 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], ClientMajorVersion)
b += 4
xgb.Put32(buf[b:], ClientMinorVersion)
b += 4
return buf
}
// RedirectSubwindowsCookie is a cookie used only for RedirectSubwindows requests.
type RedirectSubwindowsCookie struct {
*xgb.Cookie
}
// RedirectSubwindows sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func RedirectSubwindows(c *xgb.Conn, Window xproto.Window, Update byte) RedirectSubwindowsCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'RedirectSubwindows' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(redirectSubwindowsRequest(c, Window, Update), cookie)
return RedirectSubwindowsCookie{cookie}
}
// RedirectSubwindowsChecked sends a checked request.
// If an error occurs, it can be retrieved using RedirectSubwindowsCookie.Check.
func RedirectSubwindowsChecked(c *xgb.Conn, Window xproto.Window, Update byte) RedirectSubwindowsCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'RedirectSubwindows' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(redirectSubwindowsRequest(c, Window, Update), cookie)
return RedirectSubwindowsCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook RedirectSubwindowsCookie) Check() error {
return cook.Cookie.Check()
}
// redirectSubwindowsRequest writes a RedirectSubwindows request to a byte slice for transfer.
func redirectSubwindowsRequest(c *xgb.Conn, Window xproto.Window, Update byte) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["Composite"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 2 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
buf[b] = Update
b += 1
b += 3 // padding
return buf
}
// RedirectWindowCookie is a cookie used only for RedirectWindow requests.
type RedirectWindowCookie struct {
*xgb.Cookie
}
// RedirectWindow sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func RedirectWindow(c *xgb.Conn, Window xproto.Window, Update byte) RedirectWindowCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'RedirectWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(redirectWindowRequest(c, Window, Update), cookie)
return RedirectWindowCookie{cookie}
}
// RedirectWindowChecked sends a checked request.
// If an error occurs, it can be retrieved using RedirectWindowCookie.Check.
func RedirectWindowChecked(c *xgb.Conn, Window xproto.Window, Update byte) RedirectWindowCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'RedirectWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(redirectWindowRequest(c, Window, Update), cookie)
return RedirectWindowCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook RedirectWindowCookie) Check() error {
return cook.Cookie.Check()
}
// redirectWindowRequest writes a RedirectWindow request to a byte slice for transfer.
func redirectWindowRequest(c *xgb.Conn, Window xproto.Window, Update byte) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["Composite"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 1 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
buf[b] = Update
b += 1
b += 3 // padding
return buf
}
// ReleaseOverlayWindowCookie is a cookie used only for ReleaseOverlayWindow requests.
type ReleaseOverlayWindowCookie struct {
*xgb.Cookie
}
// ReleaseOverlayWindow sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func ReleaseOverlayWindow(c *xgb.Conn, Window xproto.Window) ReleaseOverlayWindowCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'ReleaseOverlayWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(releaseOverlayWindowRequest(c, Window), cookie)
return ReleaseOverlayWindowCookie{cookie}
}
// ReleaseOverlayWindowChecked sends a checked request.
// If an error occurs, it can be retrieved using ReleaseOverlayWindowCookie.Check.
func ReleaseOverlayWindowChecked(c *xgb.Conn, Window xproto.Window) ReleaseOverlayWindowCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'ReleaseOverlayWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(releaseOverlayWindowRequest(c, Window), cookie)
return ReleaseOverlayWindowCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook ReleaseOverlayWindowCookie) Check() error {
return cook.Cookie.Check()
}
// releaseOverlayWindowRequest writes a ReleaseOverlayWindow request to a byte slice for transfer.
func releaseOverlayWindowRequest(c *xgb.Conn, Window xproto.Window) []byte {
size := 8
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["Composite"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 8 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
return buf
}
// UnredirectSubwindowsCookie is a cookie used only for UnredirectSubwindows requests.
type UnredirectSubwindowsCookie struct {
*xgb.Cookie
}
// UnredirectSubwindows sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func UnredirectSubwindows(c *xgb.Conn, Window xproto.Window, Update byte) UnredirectSubwindowsCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'UnredirectSubwindows' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(unredirectSubwindowsRequest(c, Window, Update), cookie)
return UnredirectSubwindowsCookie{cookie}
}
// UnredirectSubwindowsChecked sends a checked request.
// If an error occurs, it can be retrieved using UnredirectSubwindowsCookie.Check.
func UnredirectSubwindowsChecked(c *xgb.Conn, Window xproto.Window, Update byte) UnredirectSubwindowsCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'UnredirectSubwindows' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(unredirectSubwindowsRequest(c, Window, Update), cookie)
return UnredirectSubwindowsCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook UnredirectSubwindowsCookie) Check() error {
return cook.Cookie.Check()
}
// unredirectSubwindowsRequest writes a UnredirectSubwindows request to a byte slice for transfer.
func unredirectSubwindowsRequest(c *xgb.Conn, Window xproto.Window, Update byte) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["Composite"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 4 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
buf[b] = Update
b += 1
b += 3 // padding
return buf
}
// UnredirectWindowCookie is a cookie used only for UnredirectWindow requests.
type UnredirectWindowCookie struct {
*xgb.Cookie
}
// UnredirectWindow sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func UnredirectWindow(c *xgb.Conn, Window xproto.Window, Update byte) UnredirectWindowCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'UnredirectWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(unredirectWindowRequest(c, Window, Update), cookie)
return UnredirectWindowCookie{cookie}
}
// UnredirectWindowChecked sends a checked request.
// If an error occurs, it can be retrieved using UnredirectWindowCookie.Check.
func UnredirectWindowChecked(c *xgb.Conn, Window xproto.Window, Update byte) UnredirectWindowCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["Composite"]; !ok {
panic("Cannot issue request 'UnredirectWindow' using the uninitialized extension 'Composite'. composite.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(unredirectWindowRequest(c, Window, Update), cookie)
return UnredirectWindowCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook UnredirectWindowCookie) Check() error {
return cook.Cookie.Check()
}
// unredirectWindowRequest writes a UnredirectWindow request to a byte slice for transfer.
func unredirectWindowRequest(c *xgb.Conn, Window xproto.Window, Update byte) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["Composite"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 3 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Window))
b += 4
buf[b] = Update
b += 1
b += 3 // padding
return buf
}
+185
View File
@@ -0,0 +1,185 @@
package nexgb
/*
conn.go contains a couple of functions that do some real dirty work related
to the initial connection handshake with X.
This code is largely unmodified from the original XGB package that I forked.
*/
import (
"errors"
"fmt"
"io"
"net"
"os"
"strconv"
"strings"
)
// connect connects to the X server given in the 'display' string,
// and does all the necessary setup handshaking.
// If 'display' is empty it will be taken from os.Getenv("DISPLAY").
// Note that you should read and understand the "Connection Setup" of the
// X Protocol Reference Manual before changing this function:
// http://goo.gl/4zGQg
func (c *Conn) connect(display string) error {
err := c.dial(display)
if err != nil {
return err
}
return c.postConnect()
}
// connect init from to the net.Conn,
func (c *Conn) connectNet(netConn net.Conn) error {
c.conn = netConn
return c.postConnect()
}
// do the postConnect action after Conn get it's underly net.Conn
func (c *Conn) postConnect() error {
// Get authentication data
authName, authData, err := readAuthority(c.host, c.display)
noauth := false
if err != nil {
Logger.Printf("Could not get authority info: %v", err)
Logger.Println("Trying connection without authority info...")
authName = ""
authData = []byte{}
noauth = true
}
// Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1".
if !noauth && (authName != "MIT-MAGIC-COOKIE-1" || len(authData) != 16) {
return errors.New("unsupported auth protocol " + authName)
}
buf := make([]byte, 12+Pad(len(authName))+Pad(len(authData)))
buf[0] = 0x6c
buf[1] = 0
Put16(buf[2:], 11)
Put16(buf[4:], 0)
Put16(buf[6:], uint16(len(authName)))
Put16(buf[8:], uint16(len(authData)))
Put16(buf[10:], 0)
copy(buf[12:], []byte(authName))
copy(buf[12+Pad(len(authName)):], authData)
if _, err = c.conn.Write(buf); err != nil {
return err
}
head := make([]byte, 8)
if _, err = io.ReadFull(c.conn, head[0:8]); err != nil {
return err
}
code := head[0]
reasonLen := head[1]
major := Get16(head[2:])
minor := Get16(head[4:])
dataLen := Get16(head[6:])
if major != 11 || minor != 0 {
return fmt.Errorf("x protocol version mismatch: %d.%d", major, minor)
}
buf = make([]byte, int(dataLen)*4+8, int(dataLen)*4+8)
copy(buf, head)
if _, err = io.ReadFull(c.conn, buf[8:]); err != nil {
return err
}
if code == 0 {
reason := buf[8 : 8+reasonLen]
return fmt.Errorf("x protocol authentication refused: %s",
string(reason))
}
// Unfortunately, it isn't really feasible to read the setup bytes here,
// since the code to do so is in a different package.
// Users must call 'xproto.Setup(X)' to get the setup info.
c.SetupBytes = buf
// But also read stuff that we *need* to get started.
c.setupResourceIdBase = Get32(buf[12:])
c.setupResourceIdMask = Get32(buf[16:])
return nil
}
// dial initializes the actual net connection with X.
func (c *Conn) dial(display string) error {
if len(display) == 0 {
display = os.Getenv("DISPLAY")
}
display0 := display
if len(display) == 0 {
return errors.New("empty display string")
}
colonIdx := strings.LastIndex(display, ":")
if colonIdx < 0 {
return errors.New("bad display string: " + display0)
}
var protocol, socket string
if display[0] == '/' {
socket = display[0:colonIdx]
} else {
slashIdx := strings.LastIndex(display, "/")
if slashIdx >= 0 {
protocol = display[0:slashIdx]
c.host = display[slashIdx+1 : colonIdx]
} else {
c.host = display[0:colonIdx]
}
}
display = display[colonIdx+1:]
if len(display) == 0 {
return errors.New("bad display string: " + display0)
}
var scr string
dotIdx := strings.LastIndex(display, ".")
if dotIdx < 0 {
c.display = display[0:]
} else {
c.display = display[0:dotIdx]
scr = display[dotIdx+1:]
}
var err error
c.DisplayNumber, err = strconv.Atoi(c.display)
if err != nil || c.DisplayNumber < 0 {
return errors.New("bad display string: " + display0)
}
if len(scr) != 0 {
c.DefaultScreen, err = strconv.Atoi(scr)
if err != nil {
return errors.New("bad display string: " + display0)
}
}
// Connect to server
if len(socket) != 0 {
c.conn, err = net.Dial("unix", socket+":"+c.display)
} else if len(c.host) != 0 {
if protocol == "" {
protocol = "tcp"
}
c.conn, err = net.Dial(protocol,
c.host+":"+strconv.Itoa(6000+c.DisplayNumber))
} else {
c.conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+c.display)
}
if err != nil {
return errors.New("cannot connect to " + display0 + ": " + err.Error())
}
return nil
}
+165
View File
@@ -0,0 +1,165 @@
package nexgb
import (
"errors"
)
// Cookie is the internal representation of a cookie, where one is generated
// for *every* request sent by XGB.
// 'cookie' is most frequently used by embedding it into a more specific
// kind of cookie, i.e., 'GetInputFocusCookie'.
type Cookie struct {
conn *Conn
Sequence uint16
replyChan chan []byte
errorChan chan error
pingChan chan bool
}
// NewCookie creates a new cookie with the correct channels initialized
// depending upon the values of 'checked' and 'reply'. Together, there are
// four different kinds of cookies. (See more detailed comments in the
// function for more info on those.)
// Note that a sequence number is not set until just before the request
// corresponding to this cookie is sent over the wire.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c *Conn) NewCookie(checked, reply bool) *Cookie {
cookie := &Cookie{
conn: c,
Sequence: 0, // we add the sequence id just before sending a request
replyChan: nil,
errorChan: nil,
pingChan: nil,
}
// There are four different kinds of cookies:
// Checked requests with replies get a reply channel and an error channel.
// Unchecked requests with replies get a reply channel and a ping channel.
// Checked requests w/o replies get a ping channel and an error channel.
// Unchecked requests w/o replies get no channels.
// The reply channel is used to send reply data.
// The error channel is used to send error data.
// The ping channel is used when one of the 'reply' or 'error' channels
// is missing but the other is present. The ping channel is way to force
// the blocking to stop and basically say "the error has been received
// in the main event loop" (when the ping channel is coupled with a reply
// channel) or "the request you made that has no reply was successful"
// (when the ping channel is coupled with an error channel).
if checked {
cookie.errorChan = make(chan error, 1)
if !reply {
cookie.pingChan = make(chan bool, 1)
}
}
if reply {
cookie.replyChan = make(chan []byte, 1)
if !checked {
cookie.pingChan = make(chan bool, 1)
}
}
return cookie
}
// Reply detects whether this is a checked or unchecked cookie, and calls
// 'replyChecked' or 'replyUnchecked' appropriately.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c Cookie) Reply() ([]byte, error) {
// checked
if c.errorChan != nil {
return c.replyChecked()
}
return c.replyUnchecked()
}
// replyChecked waits for a response on either the replyChan or errorChan
// channels. If the former arrives, the bytes are returned with a nil error.
// If the latter arrives, no bytes are returned (nil) and the error received
// is returned.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c Cookie) replyChecked() ([]byte, error) {
if c.replyChan == nil {
return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
"is not expecting a *reply* or an error.")
}
if c.errorChan == nil {
return nil, errors.New("Cannot call 'replyChecked' on a cookie that " +
"is not expecting a reply or an *error*.")
}
select {
case reply := <-c.replyChan:
return reply, nil
case err := <-c.errorChan:
return nil, err
}
}
// replyUnchecked waits for a response on either the replyChan or pingChan
// channels. If the former arrives, the bytes are returned with a nil error.
// If the latter arrives, no bytes are returned (nil) and a nil error
// is returned. (In the latter case, the corresponding error can be retrieved
// from (Wait|Poll)ForEvent asynchronously.)
// In all honesty, you *probably* don't want to use this method.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c Cookie) replyUnchecked() ([]byte, error) {
if c.replyChan == nil {
return nil, errors.New("Cannot call 'replyUnchecked' on a cookie " +
"that is not expecting a *reply*.")
}
select {
case reply := <-c.replyChan:
return reply, nil
case <-c.pingChan:
return nil, nil
}
}
// Check is used for checked requests that have no replies. It is a mechanism
// by which to report "success" or "error" in a synchronous fashion. (Therefore,
// unchecked requests without replies cannot use this method.)
// If the request causes an error, it is sent to this cookie's errorChan.
// If the request was successful, there is no response from the server.
// Thus, pingChan is sent a value when the *next* reply is read.
// If no more replies are being processed, we force a round trip request with
// GetInputFocus.
//
// Unless you're building requests from bytes by hand, this method should
// not be used.
func (c Cookie) Check() error {
if c.replyChan != nil {
return errors.New("Cannot call 'Check' on a cookie that is " +
"expecting a *reply*. Use 'Reply' instead.")
}
if c.errorChan == nil {
return errors.New("Cannot call 'Check' on a cookie that is " +
"not expecting a possible *error*.")
}
// First do a quick non-blocking check to see if we've been pinged.
select {
case err := <-c.errorChan:
return err
case <-c.pingChan:
return nil
default:
}
// Now force a round trip and try again, but block this time.
c.conn.Sync()
select {
case err := <-c.errorChan:
return err
case <-c.pingChan:
return nil
}
}
+592
View File
@@ -0,0 +1,592 @@
// Package damage is the X client API for the DAMAGE extension.
package damage
// This file is automatically generated from damage.xml. Edit at your peril!
import (
xgb "janouch.name/haven/nexgb"
"janouch.name/haven/nexgb/xfixes"
"janouch.name/haven/nexgb/xproto"
)
const (
MajorVersion = 1
MinorVersion = 1
)
// Init must be called before using the DAMAGE extension.
func Init(c *xgb.Conn) error {
reply, err := xproto.QueryExtension(c, 6, "DAMAGE").Reply()
switch {
case err != nil:
return err
case !reply.Present:
return xgb.Errorf("No extension named DAMAGE could be found on on the server.")
}
c.ExtLock.Lock()
c.Extensions["DAMAGE"] = reply.MajorOpcode
c.ExtLock.Unlock()
for evNum, fun := range xgb.NewExtEventFuncs["DAMAGE"] {
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
}
for errNum, fun := range xgb.NewExtErrorFuncs["DAMAGE"] {
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
}
return nil
}
func init() {
xgb.NewExtEventFuncs["DAMAGE"] = make(map[int]xgb.NewEventFun)
xgb.NewExtErrorFuncs["DAMAGE"] = make(map[int]xgb.NewErrorFun)
}
// BadBadDamage is the error number for a BadBadDamage.
const BadBadDamage = 0
type BadDamageError struct {
Sequence uint16
NiceName string
}
// BadDamageErrorNew constructs a BadDamageError value that implements xgb.Error from a byte slice.
func BadDamageErrorNew(buf []byte) xgb.Error {
v := BadDamageError{}
v.NiceName = "BadDamage"
b := 1 // skip error determinant
b += 1 // don't read error number
v.Sequence = xgb.Get16(buf[b:])
b += 2
return v
}
// SequenceId returns the sequence id attached to the BadBadDamage error.
// This is mostly used internally.
func (err BadDamageError) SequenceId() uint16 {
return err.Sequence
}
// BadId returns the 'BadValue' number if one exists for the BadBadDamage error. If no bad value exists, 0 is returned.
func (err BadDamageError) BadId() uint32 {
return 0
}
// Error returns a rudimentary string representation of the BadBadDamage error.
func (err BadDamageError) Error() string {
fieldVals := make([]string, 0, 0)
fieldVals = append(fieldVals, "NiceName: "+err.NiceName)
fieldVals = append(fieldVals, xgb.Sprintf("Sequence: %d", err.Sequence))
return "BadBadDamage {" + xgb.StringsJoin(fieldVals, ", ") + "}"
}
func init() {
xgb.NewExtErrorFuncs["DAMAGE"][0] = BadDamageErrorNew
}
type Damage uint32
func NewDamageId(c *xgb.Conn) (Damage, error) {
id, err := c.NewId()
if err != nil {
return 0, err
}
return Damage(id), nil
}
// Notify is the event number for a NotifyEvent.
const Notify = 0
type NotifyEvent struct {
Sequence uint16
Level byte
Drawable xproto.Drawable
Damage Damage
Timestamp xproto.Timestamp
Area xproto.Rectangle
Geometry xproto.Rectangle
}
// NotifyEventNew constructs a NotifyEvent value that implements xgb.Event from a byte slice.
func NotifyEventNew(buf []byte) xgb.Event {
v := NotifyEvent{}
b := 1 // don't read event number
v.Level = buf[b]
b += 1
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Drawable = xproto.Drawable(xgb.Get32(buf[b:]))
b += 4
v.Damage = Damage(xgb.Get32(buf[b:]))
b += 4
v.Timestamp = xproto.Timestamp(xgb.Get32(buf[b:]))
b += 4
v.Area = xproto.Rectangle{}
b += xproto.RectangleRead(buf[b:], &v.Area)
v.Geometry = xproto.Rectangle{}
b += xproto.RectangleRead(buf[b:], &v.Geometry)
return v
}
// Bytes writes a NotifyEvent value to a byte slice.
func (v NotifyEvent) Bytes() []byte {
buf := make([]byte, 32)
b := 0
// write event number
buf[b] = 0
b += 1
buf[b] = v.Level
b += 1
b += 2 // skip sequence number
xgb.Put32(buf[b:], uint32(v.Drawable))
b += 4
xgb.Put32(buf[b:], uint32(v.Damage))
b += 4
xgb.Put32(buf[b:], uint32(v.Timestamp))
b += 4
{
structBytes := v.Area.Bytes()
copy(buf[b:], structBytes)
b += len(structBytes)
}
{
structBytes := v.Geometry.Bytes()
copy(buf[b:], structBytes)
b += len(structBytes)
}
return buf
}
// SequenceId returns the sequence id attached to the Notify event.
// Events without a sequence number (KeymapNotify) return 0.
// This is mostly used internally.
func (v NotifyEvent) SequenceId() uint16 {
return v.Sequence
}
// String is a rudimentary string representation of NotifyEvent.
func (v NotifyEvent) String() string {
fieldVals := make([]string, 0, 6)
fieldVals = append(fieldVals, xgb.Sprintf("Sequence: %d", v.Sequence))
fieldVals = append(fieldVals, xgb.Sprintf("Level: %d", v.Level))
fieldVals = append(fieldVals, xgb.Sprintf("Drawable: %d", v.Drawable))
fieldVals = append(fieldVals, xgb.Sprintf("Damage: %d", v.Damage))
fieldVals = append(fieldVals, xgb.Sprintf("Timestamp: %d", v.Timestamp))
return "Notify {" + xgb.StringsJoin(fieldVals, ", ") + "}"
}
func init() {
xgb.NewExtEventFuncs["DAMAGE"][0] = NotifyEventNew
}
const (
ReportLevelRawRectangles = 0
ReportLevelDeltaRectangles = 1
ReportLevelBoundingBox = 2
ReportLevelNonEmpty = 3
)
// Skipping definition for base type 'Bool'
// Skipping definition for base type 'Byte'
// Skipping definition for base type 'Card8'
// Skipping definition for base type 'Char'
// Skipping definition for base type 'Void'
// Skipping definition for base type 'Double'
// Skipping definition for base type 'Float'
// Skipping definition for base type 'Int16'
// Skipping definition for base type 'Int32'
// Skipping definition for base type 'Int8'
// Skipping definition for base type 'Card16'
// Skipping definition for base type 'Card32'
// AddCookie is a cookie used only for Add requests.
type AddCookie struct {
*xgb.Cookie
}
// Add sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func Add(c *xgb.Conn, Drawable xproto.Drawable, Region xfixes.Region) AddCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'Add' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(addRequest(c, Drawable, Region), cookie)
return AddCookie{cookie}
}
// AddChecked sends a checked request.
// If an error occurs, it can be retrieved using AddCookie.Check.
func AddChecked(c *xgb.Conn, Drawable xproto.Drawable, Region xfixes.Region) AddCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'Add' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(addRequest(c, Drawable, Region), cookie)
return AddCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook AddCookie) Check() error {
return cook.Cookie.Check()
}
// addRequest writes a Add request to a byte slice for transfer.
func addRequest(c *xgb.Conn, Drawable xproto.Drawable, Region xfixes.Region) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["DAMAGE"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 4 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Drawable))
b += 4
xgb.Put32(buf[b:], uint32(Region))
b += 4
return buf
}
// CreateCookie is a cookie used only for Create requests.
type CreateCookie struct {
*xgb.Cookie
}
// Create sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func Create(c *xgb.Conn, Damage Damage, Drawable xproto.Drawable, Level byte) CreateCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'Create' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(createRequest(c, Damage, Drawable, Level), cookie)
return CreateCookie{cookie}
}
// CreateChecked sends a checked request.
// If an error occurs, it can be retrieved using CreateCookie.Check.
func CreateChecked(c *xgb.Conn, Damage Damage, Drawable xproto.Drawable, Level byte) CreateCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'Create' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(createRequest(c, Damage, Drawable, Level), cookie)
return CreateCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook CreateCookie) Check() error {
return cook.Cookie.Check()
}
// createRequest writes a Create request to a byte slice for transfer.
func createRequest(c *xgb.Conn, Damage Damage, Drawable xproto.Drawable, Level byte) []byte {
size := 16
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["DAMAGE"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 1 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Damage))
b += 4
xgb.Put32(buf[b:], uint32(Drawable))
b += 4
buf[b] = Level
b += 1
b += 3 // padding
return buf
}
// DestroyCookie is a cookie used only for Destroy requests.
type DestroyCookie struct {
*xgb.Cookie
}
// Destroy sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func Destroy(c *xgb.Conn, Damage Damage) DestroyCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'Destroy' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(destroyRequest(c, Damage), cookie)
return DestroyCookie{cookie}
}
// DestroyChecked sends a checked request.
// If an error occurs, it can be retrieved using DestroyCookie.Check.
func DestroyChecked(c *xgb.Conn, Damage Damage) DestroyCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'Destroy' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(destroyRequest(c, Damage), cookie)
return DestroyCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook DestroyCookie) Check() error {
return cook.Cookie.Check()
}
// destroyRequest writes a Destroy request to a byte slice for transfer.
func destroyRequest(c *xgb.Conn, Damage Damage) []byte {
size := 8
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["DAMAGE"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 2 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Damage))
b += 4
return buf
}
// QueryVersionCookie is a cookie used only for QueryVersion requests.
type QueryVersionCookie struct {
*xgb.Cookie
}
// QueryVersion sends a checked request.
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply.
func QueryVersion(c *xgb.Conn, ClientMajorVersion, ClientMinorVersion uint32) QueryVersionCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, true)
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
return QueryVersionCookie{cookie}
}
// QueryVersionUnchecked sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func QueryVersionUnchecked(c *xgb.Conn, ClientMajorVersion, ClientMinorVersion uint32) QueryVersionCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, true)
c.NewRequest(queryVersionRequest(c, ClientMajorVersion, ClientMinorVersion), cookie)
return QueryVersionCookie{cookie}
}
// QueryVersionReply represents the data returned from a QueryVersion request.
type QueryVersionReply struct {
Sequence uint16 // sequence number of the request for this reply
Length uint32 // number of bytes in this reply
// padding: 1 bytes
MajorVersion uint32
MinorVersion uint32
// padding: 16 bytes
}
// Reply blocks and returns the reply data for a QueryVersion request.
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
buf, err := cook.Cookie.Reply()
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return queryVersionReply(buf), nil
}
// queryVersionReply reads a byte slice into a QueryVersionReply value.
func queryVersionReply(buf []byte) *QueryVersionReply {
v := new(QueryVersionReply)
b := 1 // skip reply determinant
b += 1 // padding
v.Sequence = xgb.Get16(buf[b:])
b += 2
v.Length = xgb.Get32(buf[b:]) // 4-byte units
b += 4
v.MajorVersion = xgb.Get32(buf[b:])
b += 4
v.MinorVersion = xgb.Get32(buf[b:])
b += 4
b += 16 // padding
return v
}
// queryVersionRequest writes a QueryVersion request to a byte slice for transfer.
func queryVersionRequest(c *xgb.Conn, ClientMajorVersion, ClientMinorVersion uint32) []byte {
size := 12
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["DAMAGE"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 0 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], ClientMajorVersion)
b += 4
xgb.Put32(buf[b:], ClientMinorVersion)
b += 4
return buf
}
// SubtractCookie is a cookie used only for Subtract requests.
type SubtractCookie struct {
*xgb.Cookie
}
// Subtract sends an unchecked request.
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
func Subtract(c *xgb.Conn, Damage Damage, Repair, Parts xfixes.Region) SubtractCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'Subtract' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(false, false)
c.NewRequest(subtractRequest(c, Damage, Repair, Parts), cookie)
return SubtractCookie{cookie}
}
// SubtractChecked sends a checked request.
// If an error occurs, it can be retrieved using SubtractCookie.Check.
func SubtractChecked(c *xgb.Conn, Damage Damage, Repair, Parts xfixes.Region) SubtractCookie {
c.ExtLock.RLock()
defer c.ExtLock.RUnlock()
if _, ok := c.Extensions["DAMAGE"]; !ok {
panic("Cannot issue request 'Subtract' using the uninitialized extension 'DAMAGE'. damage.Init(connObj) must be called first.")
}
cookie := c.NewCookie(true, false)
c.NewRequest(subtractRequest(c, Damage, Repair, Parts), cookie)
return SubtractCookie{cookie}
}
// Check returns an error if one occurred for checked requests that are not expecting a reply.
// This cannot be called for requests expecting a reply, nor for unchecked requests.
func (cook SubtractCookie) Check() error {
return cook.Cookie.Check()
}
// subtractRequest writes a Subtract request to a byte slice for transfer.
func subtractRequest(c *xgb.Conn, Damage Damage, Repair, Parts xfixes.Region) []byte {
size := 16
b := 0
buf := make([]byte, size)
c.ExtLock.RLock()
buf[b] = c.Extensions["DAMAGE"]
c.ExtLock.RUnlock()
b += 1
buf[b] = 3 // request opcode
b += 1
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
b += 2
xgb.Put32(buf[b:], uint32(Damage))
b += 4
xgb.Put32(buf[b:], uint32(Repair))
b += 4
xgb.Put32(buf[b:], uint32(Parts))
b += 4
return buf
}
+144
View File
@@ -0,0 +1,144 @@
/*
Package nexgb provides the X Go Binding, which is a low-level API to communicate
with the core X protocol and many of the X extensions.
It is *very* closely modeled on XCB, so that experience with XCB (or xpyb) is
easily translatable to neXGB. That is, it uses the same cookie/reply model
and is thread safe. There are otherwise no major differences (in the API).
Most uses of neXGB typically fall under the realm of window manager and GUI kit
development, but other applications (like pagers, panels, tilers, etc.) may
also require neXGB.
Example
This is an extremely terse example that demonstrates how to connect to X,
create a window, listen to StructureNotify events and Key{Press,Release}
events, map the window, and print out all events received. An example with
accompanying documentation can be found in examples/create-window.
package main
import (
"fmt"
xgb "janouch.name/haven/nexgb"
"janouch.name/haven/nexgb/xproto"
)
func main() {
X, err := xgb.NewConn()
if err != nil {
fmt.Println(err)
return
}
wid, _ := xproto.NewWindowId(X)
screen := xproto.Setup(X).DefaultScreen(X)
xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root,
0, 0, 500, 500, 0,
xproto.WindowClassInputOutput, screen.RootVisual,
xproto.CwBackPixel | xproto.CwEventMask,
[]uint32{ // values must be in the order defined by the protocol
0xffffffff,
xproto.EventMaskStructureNotify |
xproto.EventMaskKeyPress |
xproto.EventMaskKeyRelease})
xproto.MapWindow(X, wid)
for {
ev, xerr := X.WaitForEvent()
if ev == nil && xerr == nil {
fmt.Println("Both event and error are nil. Exiting...")
return
}
if ev != nil {
fmt.Printf("Event: %s\n", ev)
}
if xerr != nil {
fmt.Printf("Error: %s\n", xerr)
}
}
}
Xinerama Example
This is another small example that shows how to query Xinerama for geometry
information of each active head. Accompanying documentation for this example
can be found in examples/xinerama.
package main
import (
"fmt"
"log"
xgb "janouch.name/haven/nexgb"
"janouch.name/haven/nexgb/xinerama"
)
func main() {
X, err := xgb.NewConn()
if err != nil {
log.Fatal(err)
}
// Initialize the Xinerama extension.
// The appropriate 'Init' function must be run for *every*
// extension before any of its requests can be used.
err = xinerama.Init(X)
if err != nil {
log.Fatal(err)
}
reply, err := xinerama.QueryScreens(X).Reply()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Number of heads: %d\n", reply.Number)
for i, screen := range reply.ScreenInfo {
fmt.Printf("%d :: X: %d, Y: %d, Width: %d, Height: %d\n",
i, screen.XOrg, screen.YOrg, screen.Width, screen.Height)
}
}
Parallelism
neXGB can benefit greatly from parallelism due to its concurrent design. For
evidence of this claim, please see the benchmarks in xproto/xproto_test.go.
Tests
xproto/xproto_test.go contains a number of contrived tests that stress
particular corners of neXGB that I presume could be problem areas. Namely:
requests with no replies, requests with replies, checked errors, unchecked
errors, sequence number wrapping, cookie buffer flushing (i.e., forcing a round
trip every N requests made that don't have a reply), getting/setting properties
and creating a window and listening to StructureNotify events.
Code Generator
Both XCB and xpyb use the same Python module (xcbgen) for a code generator.
neXGB (before BurntSushi's fork) used the same code generator as well, but in my
attempt to add support for more extensions, I found the code generator extremely
difficult to work with. Therefore, I re-wrote the code generator in Go. It can
be found in its own sub-package, xgbgen, of xgb. My design of xgbgen includes a
rough consideration that it could be used for other languages.
What works
I am reasonably confident that the core X protocol is in full working form. I've
also tested the Xinerama and RandR extensions sparingly. Many of the other
existing extensions have Go source generated (and are compilable) and are
included in this package, but I am currently unsure of their status. They
*should* work.
What does not work
XKB is the only extension that intentionally does not work, although I suspect
that GLX also does not work (however, there is Go source code for GLX that
compiles, unlike XKB). I don't currently have any intention of getting XKB
working, due to its complexity and my current mental incapacity to test it.
*/
package nexgb
+712
View File
@@ -0,0 +1,712 @@
// Package dpms is the X client API for the DPMS extension.
package dpms
// This file is automatically generated from dpms.xml. Edit at your peril!
import (
xgb "janouch.name/haven/nexgb"
"janouch.name/haven/nexgb/xproto"
)
const (
MajorVersion = 0
MinorVersion = 0
)
// Init must be called before using the DPMS extension.
func Init(c *xgb.Conn) error {
reply, err := xproto.QueryExtension(c, 4, "DPMS").Reply()
switch {
case err != nil:
return err
case !reply.Present:
return xgb.Errorf("No extension named DPMS could be found on on the server.")
}
c.ExtLock.Lock()
c.Extensions["DPMS"] = reply.MajorOpcode
c.ExtLock.Unlock()
for evNum, fun := range xgb.NewExtEventFuncs["DPMS"] {
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
}
for errNum, fun := range xgb.NewExtErrorFuncs["DPMS"] {
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
}
return nil
}
func init() {
xgb.NewExtEventFuncs["DPMS"] = make(map[int]xgb.NewEventFun)
xgb.NewExtErrorFuncs["DPMS"] = make(map[int]xgb.NewErrorFun)
}
const (
DPMSModeOn = 0
DPMSModeStandby = 1
DPMSModeSuspend = 2
DPMSModeOff = 3
)
// Skipping definition for base type 'Bool'
// Skipping definition for base type 'Byte'
// Skipping definition for base type 'Card8'