mod: add gocui

pull/1/head
Edouard Paris 5 years ago
parent d81c0c8149
commit 5b5a49170d

@ -10,6 +10,7 @@ import (
"github.com/edouardparis/lntop/logging"
"github.com/edouardparis/lntop/network"
"github.com/edouardparis/lntop/network/backend"
"github.com/jroimartin/gocui"
)
// New creates a new cli app.
@ -22,6 +23,7 @@ func New() *cli.App {
return &cli.App{
Name: "lntop",
EnableShellCompletion: true,
Action: run,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "v",
@ -44,6 +46,40 @@ func New() *cli.App {
}
}
func run(c *cli.Context) error {
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
return err
}
defer g.Close()
g.SetManagerFunc(layout)
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
return err
}
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
return err
}
return err
}
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
if err != gocui.ErrUnknownView {
return err
}
fmt.Fprintln(v, "Hello world!")
}
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
func getNetworkFromConfig(c *cli.Context) (backend.Backend, error) {
cfg, err := config.Load(c.String("config"))
if err != nil {

@ -4,11 +4,14 @@ require (
github.com/btcsuite/btcwallet v0.0.0-20190313041134-68fc7c82e131 // indirect
github.com/gofrs/uuid v3.2.0+incompatible
github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect
github.com/jroimartin/gocui v0.4.0
github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c // indirect
github.com/juju/errors v0.0.0-20190207033735-e65537c515d7 // indirect
github.com/juju/loggo v0.0.0-20190212223446-d976af380377 // indirect
github.com/lightningnetwork/lnd v0.5.2-beta
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/miekg/dns v1.1.6 // indirect
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
github.com/pkg/errors v0.8.1
github.com/stretchr/testify v1.3.0 // indirect
go.etcd.io/bbolt v1.3.2 // indirect

@ -73,6 +73,8 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M
github.com/jessevdk/go-flags v0.0.0-20170926144705-f88afde2fa19/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8=
github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY=
github.com/juju/clock v0.0.0-20180808021310-bab88fc67299/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA=
github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c h1:3UvYABOQRhJAApj9MdCN+Ydv841ETSoy6xLzdmmr/9A=
github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA=
@ -115,9 +117,13 @@ github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qp
github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0=
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY=
github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.6 h1:jVwb4GDwD65q/gtItR/lIZHjNH93QfeGxZUkzJcW9mc=
github.com/miekg/dns v1.1.6/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=

@ -0,0 +1,30 @@
# This is the official list of gocui authors for copyright purposes.
# Names should be added to this file as
# Name or Organization <email address> contribution
# Contribution
# The email address is not required for organizations.
Roi Martin <jroi.martin@gmail.com>
Main developer
Ryan Sullivan <kayoticsully@gmail.com>
Toggleable view frames
Matthieu Rakotojaona <matthieu.rakotojaona@gmail.com>
Wrapped views
Harry Lawrence <hazbo@gmx.com>
Basic mouse support
Danny Tylman <dtylman@gmail.com>
Masked views
Frederik Deweerdt <frederik.deweerdt@gmail.com>
Colored fonts
Henri Koski <henri.t.koski@gmail.com>
Custom current view color
Dustin Willis Webber <dustin.webber@gmail.com>
256-colors output mode support

@ -0,0 +1,23 @@
Copyright (c) 2014 The gocui 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 the gocui Authors nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,110 @@
# GOCUI - Go Console User Interface
[![GoDoc](https://godoc.org/github.com/jroimartin/gocui?status.svg)](https://godoc.org/github.com/jroimartin/gocui)
Minimalist Go package aimed at creating Console User Interfaces.
## Features
* Minimalist API.
* Views (the "windows" in the GUI) implement the interface io.ReadWriter.
* Support for overlapping views.
* The GUI can be modified at runtime (concurrent-safe).
* Global and view-level keybindings.
* Mouse support.
* Colored text.
* Customizable edition mode.
* Easy to build reusable widgets, complex layouts...
## Installation
Execute:
```
$ go get github.com/jroimartin/gocui
```
## Documentation
Execute:
```
$ go doc github.com/jroimartin/gocui
```
Or visit [godoc.org](https://godoc.org/github.com/jroimartin/gocui) to read it
online.
## Example
```go
package main
import (
"fmt"
"log"
"github.com/jroimartin/gocui"
)
func main() {
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
log.Panicln(err)
}
defer g.Close()
g.SetManagerFunc(layout)
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
log.Panicln(err)
}
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
log.Panicln(err)
}
}
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
if err != gocui.ErrUnknownView {
return err
}
fmt.Fprintln(v, "Hello world!")
}
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
```
## Screenshots
![r2cui](https://cloud.githubusercontent.com/assets/1223476/19418932/63645052-93ce-11e6-867c-da5e97e37237.png)
![_examples/demo.go](https://cloud.githubusercontent.com/assets/1223476/5992750/720b84f0-aa36-11e4-88ec-296fa3247b52.png)
![_examples/dynamic.go](https://cloud.githubusercontent.com/assets/1223476/5992751/76ad5cc2-aa36-11e4-8204-6a90269db827.png)
## Projects using gocui
* [komanda-cli](https://github.com/mephux/komanda-cli): IRC Client For Developers.
* [vuls](https://github.com/future-architect/vuls): Agentless vulnerability scanner for Linux/FreeBSD.
* [wuzz](https://github.com/asciimoo/wuzz): Interactive cli tool for HTTP inspection.
* [httplab](https://github.com/gchaincl/httplab): Interactive web server.
* [domainr](https://github.com/MichaelThessel/domainr): Tool that checks the availability of domains based on keywords.
* [gotime](https://github.com/nanohard/gotime): Time tracker for projects and tasks.
* [claws](https://github.com/thehowl/claws): Interactive command line client for testing websockets.
* [terminews](http://github.com/antavelos/terminews): Terminal based RSS reader.
* [diagram](https://github.com/esimov/diagram): Tool to convert ascii arts into hand drawn diagrams.
* [pody](https://github.com/JulienBreux/pody): CLI app to manage Pods in a Kubernetes cluster.
* [kubexp](https://github.com/alitari/kubexp): Kubernetes client.
* [kcli](https://github.com/cswank/kcli): Tool for inspecting kafka topics/partitions/messages.
* [fac](https://github.com/mkchoi212/fac): git merge conflict resolver
* [jsonui](https://github.com/gulyasm/jsonui): Interactive JSON explorer for your terminal.
* [cointop](https://github.com/miguelmota/cointop): Interactive terminal based UI application for tracking cryptocurrencies.
Note: if your project is not listed here, let us know! :)

@ -0,0 +1,32 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gocui
import "github.com/nsf/termbox-go"
// Attribute represents a terminal attribute, like color, font style, etc. They
// can be combined using bitwise OR (|). Note that it is not possible to
// combine multiple color attributes.
type Attribute termbox.Attribute
// Color attributes.
const (
ColorDefault Attribute = Attribute(termbox.ColorDefault)
ColorBlack = Attribute(termbox.ColorBlack)
ColorRed = Attribute(termbox.ColorRed)
ColorGreen = Attribute(termbox.ColorGreen)
ColorYellow = Attribute(termbox.ColorYellow)
ColorBlue = Attribute(termbox.ColorBlue)
ColorMagenta = Attribute(termbox.ColorMagenta)
ColorCyan = Attribute(termbox.ColorCyan)
ColorWhite = Attribute(termbox.ColorWhite)
)
// Text style attributes.
const (
AttrBold Attribute = Attribute(termbox.AttrBold)
AttrUnderline = Attribute(termbox.AttrUnderline)
AttrReverse = Attribute(termbox.AttrReverse)
)

@ -0,0 +1,118 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package gocui allows to create console user interfaces.
Create a new GUI:
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
// handle error
}
defer g.Close()
// Set GUI managers and key bindings
// ...
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
// handle error
}
Set GUI managers:
g.SetManager(mgr1, mgr2)
Managers are in charge of GUI's layout and can be used to build widgets. On
each iteration of the GUI's main loop, the Layout function of each configured
manager is executed. Managers are used to set-up and update the application's
main views, being possible to freely change them during execution. Also, it is
important to mention that a main loop iteration is executed on each reported
event (key-press, mouse event, window resize, etc).
GUIs are composed by Views, you can think of it as buffers. Views implement the
io.ReadWriter interface, so you can just write to them if you want to modify
their content. The same is valid for reading.
Create and initialize a view with absolute coordinates:
if v, err := g.SetView("viewname", 2, 2, 22, 7); err != nil {
if err != gocui.ErrUnknownView {
// handle error
}
fmt.Fprintln(v, "This is a new view")
// ...
}
Views can also be created using relative coordinates:
maxX, maxY := g.Size()
if v, err := g.SetView("viewname", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil {
// ...
}
Configure keybindings:
if err := g.SetKeybinding("viewname", gocui.KeyEnter, gocui.ModNone, fcn); err != nil {
// handle error
}
gocui implements full mouse support that can be enabled with:
g.Mouse = true
Mouse events are handled like any other keybinding:
if err := g.SetKeybinding("viewname", gocui.MouseLeft, gocui.ModNone, fcn); err != nil {
// handle error
}
IMPORTANT: Views can only be created, destroyed or updated in three ways: from
the Layout function within managers, from keybinding callbacks or via
*Gui.Update(). The reason for this is that it allows gocui to be
concurrent-safe. So, if you want to update your GUI from a goroutine, you must
use *Gui.Update(). For example:
g.Update(func(g *gocui.Gui) error {
v, err := g.View("viewname")
if err != nil {
// handle error
}
v.Clear()
fmt.Fprintln(v, "Writing from different goroutines")
return nil
})
By default, gocui provides a basic edition mode. This mode can be extended
and customized creating a new Editor and assigning it to *View.Editor:
type Editor interface {
Edit(v *View, key Key, ch rune, mod Modifier)
}
DefaultEditor can be taken as example to create your own custom Editor:
var DefaultEditor Editor = EditorFunc(simpleEditor)
func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
switch {
case ch != 0 && mod == 0:
v.EditWrite(ch)
case key == KeySpace:
v.EditWrite(' ')
case key == KeyBackspace || key == KeyBackspace2:
v.EditDelete(true)
// ...
}
}
Colored text:
Views allow to add colored text using ANSI colors. For example:
fmt.Fprintln(v, "\x1b[0;31mHello world")
For more information, see the examples in folder "_examples/".
*/
package gocui

@ -0,0 +1,344 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gocui
import "errors"
const maxInt = int(^uint(0) >> 1)
// Editor interface must be satisfied by gocui editors.
type Editor interface {
Edit(v *View, key Key, ch rune, mod Modifier)
}
// The EditorFunc type is an adapter to allow the use of ordinary functions as
// Editors. If f is a function with the appropriate signature, EditorFunc(f)
// is an Editor object that calls f.
type EditorFunc func(v *View, key Key, ch rune, mod Modifier)
// Edit calls f(v, key, ch, mod)
func (f EditorFunc) Edit(v *View, key Key, ch rune, mod Modifier) {
f(v, key, ch, mod)
}
// DefaultEditor is the default editor.
var DefaultEditor Editor = EditorFunc(simpleEditor)
// simpleEditor is used as the default gocui editor.
func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
switch {
case ch != 0 && mod == 0:
v.EditWrite(ch)
case key == KeySpace:
v.EditWrite(' ')
case key == KeyBackspace || key == KeyBackspace2:
v.EditDelete(true)
case key == KeyDelete:
v.EditDelete(false)
case key == KeyInsert:
v.Overwrite = !v.Overwrite
case key == KeyEnter:
v.EditNewLine()
case key == KeyArrowDown:
v.MoveCursor(0, 1, false)
case key == KeyArrowUp:
v.MoveCursor(0, -1, false)
case key == KeyArrowLeft:
v.MoveCursor(-1, 0, false)
case key == KeyArrowRight:
v.MoveCursor(1, 0, false)
}
}
// EditWrite writes a rune at the cursor position.
func (v *View) EditWrite(ch rune) {
v.writeRune(v.cx, v.cy, ch)
v.MoveCursor(1, 0, true)
}
// EditDelete deletes a rune at the cursor position. back determines the
// direction.
func (v *View) EditDelete(back bool) {
x, y := v.ox+v.cx, v.oy+v.cy
if y < 0 {
return
} else if y >= len(v.viewLines) {
v.MoveCursor(-1, 0, true)
return
}
maxX, _ := v.Size()
if back {
if x == 0 { // start of the line
if y < 1 {
return
}
var maxPrevWidth int
if v.Wrap {
maxPrevWidth = maxX
} else {
maxPrevWidth = maxInt
}
if v.viewLines[y].linesX == 0 { // regular line
v.mergeLines(v.cy - 1)
if len(v.viewLines[y-1].line) < maxPrevWidth {
v.MoveCursor(-1, 0, true)
}
} else { // wrapped line
v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1)
v.MoveCursor(-1, 0, true)
}
} else { // middle/end of the line
v.deleteRune(v.cx-1, v.cy)
v.MoveCursor(-1, 0, true)
}
} else {
if x == len(v.viewLines[y].line) { // end of the line
v.mergeLines(v.cy)
} else { // start/middle of the line
v.deleteRune(v.cx, v.cy)
}
}
}
// EditNewLine inserts a new line under the cursor.
func (v *View) EditNewLine() {
v.breakLine(v.cx, v.cy)
v.ox = 0
v.cx = 0
v.MoveCursor(0, 1, true)
}
// MoveCursor moves the cursor taking into account the width of the line/view,
// displacing the origin if necessary.
func (v *View) MoveCursor(dx, dy int, writeMode bool) {
maxX, maxY := v.Size()
cx, cy := v.cx+dx, v.cy+dy
x, y := v.ox+cx, v.oy+cy
var curLineWidth, prevLineWidth int
// get the width of the current line
if writeMode {
if v.Wrap {
curLineWidth = maxX - 1
} else {
curLineWidth = maxInt
}
} else {
if y >= 0 && y < len(v.viewLines) {
curLineWidth = len(v.viewLines[y].line)
if v.Wrap && curLineWidth >= maxX {
curLineWidth = maxX - 1
}
} else {
curLineWidth = 0
}
}
// get the width of the previous line
if y-1 >= 0 && y-1 < len(v.viewLines) {
prevLineWidth = len(v.viewLines[y-1].line)
} else {
prevLineWidth = 0
}
// adjust cursor's x position and view's x origin
if x > curLineWidth { // move to next line
if dx > 0 { // horizontal movement
cy++
if writeMode || v.oy+cy < len(v.viewLines) {
if !v.Wrap {
v.ox = 0
}
v.cx = 0
}
} else { // vertical movement
if curLineWidth > 0 { // move cursor to the EOL
if v.Wrap {
v.cx = curLineWidth
} else {
ncx := curLineWidth - v.ox
if ncx < 0 {
v.ox += ncx
if v.ox < 0 {
v.ox = 0
}
v.cx = 0
} else {
v.cx = ncx
}
}
} else {
if writeMode || v.oy+cy < len(v.viewLines) {
if !v.Wrap {
v.ox = 0
}
v.cx = 0
}
}
}
} else if cx < 0 {
if !v.Wrap && v.ox > 0 { // move origin to the left
v.ox += cx
v.cx = 0
} else { // move to previous line
cy--
if prevLineWidth > 0 {
if !v.Wrap { // set origin so the EOL is visible
nox := prevLineWidth - maxX + 1
if nox < 0 {
v.ox = 0
} else {
v.ox = nox
}
}
v.cx = prevLineWidth
} else {
if !v.Wrap {
v.ox = 0
}
v.cx = 0
}
}
} else { // stay on the same line
if v.Wrap {
v.cx = cx
} else {
if cx >= maxX {
v.ox += cx - maxX + 1
v.cx = maxX
} else {
v.cx = cx
}
}
}
// adjust cursor's y position and view's y origin
if cy < 0 {
if v.oy > 0 {
v.oy--
}
} else if writeMode || v.oy+cy < len(v.viewLines) {
if cy >= maxY {
v.oy++
} else {
v.cy = cy
}
}
}
// writeRune writes a rune into the view's internal buffer, at the
// position corresponding to the point (x, y). The length of the internal
// buffer is increased if the point is out of bounds. Overwrite mode is
// governed by the value of View.overwrite.
func (v *View) writeRune(x, y int, ch rune) error {
v.tainted = true
x, y, err := v.realPosition(x, y)
if err != nil {
return err
}
if x < 0 || y < 0 {
return errors.New("invalid point")
}
if y >= len(v.lines) {
s := make([][]cell, y-len(v.lines)+1)
v.lines = append(v.lines, s...)
}
olen := len(v.lines[y])
var s []cell
if x >= len(v.lines[y]) {
s = make([]cell, x-len(v.lines[y])+1)
} else if !v.Overwrite {
s = make([]cell, 1)
}
v.lines[y] = append(v.lines[y], s...)
if !v.Overwrite || (v.Overwrite && x >= olen-1) {
copy(v.lines[y][x+1:], v.lines[y][x:])
}
v.lines[y][x] = cell{
fgColor: v.FgColor,
bgColor: v.BgColor,
chr: ch,
}
return nil
}
// deleteRune removes a rune from the view's internal buffer, at the
// position corresponding to the point (x, y).
func (v *View) deleteRune(x, y int) error {
v.tainted = true
x, y, err := v.realPosition(x, y)
if err != nil {
return err
}
if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
return errors.New("invalid point")
}
v.lines[y] = append(v.lines[y][:x], v.lines[y][x+1:]...)
return nil
}
// mergeLines merges the lines "y" and "y+1" if possible.
func (v *View) mergeLines(y int) error {
v.tainted = true
_, y, err := v.realPosition(0, y)
if err != nil {
return err
}
if y < 0 || y >= len(v.lines) {
return errors.New("invalid point")
}
if y < len(v.lines)-1 { // otherwise we don't need to merge anything
v.lines[y] = append(v.lines[y], v.lines[y+1]...)
v.lines = append(v.lines[:y+1], v.lines[y+2:]...)
}
return nil
}
// breakLine breaks a line of the internal buffer at the position corresponding
// to the point (x, y).
func (v *View) breakLine(x, y int) error {
v.tainted = true
x, y, err := v.realPosition(x, y)
if err != nil {
return err
}
if y < 0 || y >= len(v.lines) {
return errors.New("invalid point")
}
var left, right []cell
if x < len(v.lines[y]) { // break line
left = make([]cell, len(v.lines[y][:x]))
copy(left, v.lines[y][:x])
right = make([]cell, len(v.lines[y][x:]))
copy(right, v.lines[y][x:])
} else { // new empty line
left = v.lines[y]
}
lines := make([][]cell, len(v.lines)+1)
lines[y] = left
lines[y+1] = right
copy(lines, v.lines[:y])
copy(lines[y+2:], v.lines[y+1:])
v.lines = lines
return nil
}

@ -0,0 +1,229 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gocui
import (
"errors"
"strconv"
)
type escapeInterpreter struct {
state escapeState
curch rune
csiParam []string
curFgColor, curBgColor Attribute
mode OutputMode
}
type escapeState int
const (
stateNone escapeState = iota
stateEscape
stateCSI
stateParams
)
var (
errNotCSI = errors.New("Not a CSI escape sequence")
errCSIParseError = errors.New("CSI escape sequence parsing error")
errCSITooLong = errors.New("CSI escape sequence is too long")
)
// runes in case of error will output the non-parsed runes as a string.
func (ei *escapeInterpreter) runes() []rune {
switch ei.state {
case stateNone:
return []rune{0x1b}
case stateEscape:
return []rune{0x1b, ei.curch}
case stateCSI:
return []rune{0x1b, '[', ei.curch}
case stateParams:
ret := []rune{0x1b, '['}
for _, s := range ei.csiParam {
ret = append(ret, []rune(s)...)
ret = append(ret, ';')
}
return append(ret, ei.curch)
}
return nil
}
// newEscapeInterpreter returns an escapeInterpreter that will be able to parse
// terminal escape sequences.
func newEscapeInterpreter(mode OutputMode) *escapeInterpreter {
ei := &escapeInterpreter{
state: stateNone,
curFgColor: ColorDefault,
curBgColor: ColorDefault,
mode: mode,
}
return ei
}
// reset sets the escapeInterpreter in initial state.
func (ei *escapeInterpreter) reset() {
ei.state = stateNone
ei.curFgColor = ColorDefault
ei.curBgColor = ColorDefault
ei.csiParam = nil
}
// parseOne parses a rune. If isEscape is true, it means that the rune is part
// of an escape sequence, and as such should not be printed verbatim. Otherwise,
// it's not an escape sequence.
func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
// Sanity checks
if len(ei.csiParam) > 20 {
return false, errCSITooLong
}
if len(ei.csiParam) > 0 && len(ei.csiParam[len(ei.csiParam)-1]) > 255 {
return false, errCSITooLong
}
ei.curch = ch
switch ei.state {
case stateNone:
if ch == 0x1b {
ei.state = stateEscape
return true, nil
}
return false, nil
case stateEscape:
if ch == '[' {
ei.state = stateCSI
return true, nil
}
return false, errNotCSI
case stateCSI:
switch {
case ch >= '0' && ch <= '9':
ei.csiParam = append(ei.csiParam, "")
case ch == 'm':
ei.csiParam = append(ei.csiParam, "0")
default:
return false, errCSIParseError
}
ei.state = stateParams
fallthrough
case stateParams:
switch {
case ch >= '0' && ch <= '9':
ei.csiParam[len(ei.csiParam)-1] += string(ch)
return true, nil
case ch == ';':
ei.csiParam = append(ei.csiParam, "")
return true, nil
case ch == 'm':
var err error
switch ei.mode {
case OutputNormal:
err = ei.outputNormal()
case Output256:
err = ei.output256()
}
if err != nil {
return false, errCSIParseError
}
ei.state = stateNone
ei.csiParam = nil
return true, nil
default:
return false, errCSIParseError
}
}
return false, nil
}
// outputNormal provides 8 different colors:
// black, red, green, yellow, blue, magenta, cyan, white
func (ei *escapeInterpreter) outputNormal() error {
for _, param := range ei.csiParam {
p, err := strconv.Atoi(param)
if err != nil {
return errCSIParseError
}
switch {
case p >= 30 && p <= 37:
ei.curFgColor = Attribute(p - 30 + 1)
case p == 39:
ei.curFgColor = ColorDefault
case p >= 40 && p <= 47:
ei.curBgColor = Attribute(p - 40 + 1)
case p == 49:
ei.curBgColor = ColorDefault
case p == 1:
ei.curFgColor |= AttrBold
case p == 4:
ei.curFgColor |= AttrUnderline
case p == 7:
ei.curFgColor |= AttrReverse
case p == 0:
ei.curFgColor = ColorDefault
ei.curBgColor = ColorDefault
}
}
return nil
}
// output256 allows you to leverage the 256-colors terminal mode:
// 0x01 - 0x08: the 8 colors as in OutputNormal
// 0x09 - 0x10: Color* | AttrBold
// 0x11 - 0xe8: 216 different colors
// 0xe9 - 0x1ff: 24 different shades of grey
func (ei *escapeInterpreter) output256() error {
if len(ei.csiParam) < 3 {
return ei.outputNormal()
}
mode, err := strconv.Atoi(ei.csiParam[1])
if err != nil {
return errCSIParseError
}
if mode != 5 {
return ei.outputNormal()
}
fgbg, err := strconv.Atoi(ei.csiParam[0])
if err != nil {
return errCSIParseError
}
color, err := strconv.Atoi(ei.csiParam[2])
if err != nil {
return errCSIParseError
}
switch fgbg {
case 38:
ei.curFgColor = Attribute(color + 1)
for _, param := range ei.csiParam[3:] {
p, err := strconv.Atoi(param)
if err != nil {
return errCSIParseError
}
switch {
case p == 1:
ei.curFgColor |= AttrBold
case p == 4:
ei.curFgColor |= AttrUnderline
case p == 7:
ei.curFgColor |= AttrReverse
}
}
case 48:
ei.curBgColor = Attribute(color + 1)
default:
return errCSIParseError
}
return nil
}

@ -0,0 +1,636 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gocui
import (
"errors"
"github.com/nsf/termbox-go"
)
var (
// ErrQuit is used to decide if the MainLoop finished successfully.
ErrQuit = errors.New("quit")
// ErrUnknownView allows to assert if a View must be initialized.
ErrUnknownView = errors.New("unknown view")
)
// OutputMode represents the terminal's output mode (8 or 256 colors).
type OutputMode termbox.OutputMode
const (
// OutputNormal provides 8-colors terminal mode.
OutputNormal = OutputMode(termbox.OutputNormal)
// Output256 provides 256-colors terminal mode.
Output256 = OutputMode(termbox.Output256)
)
// Gui represents the whole User Interface, including the views, layouts
// and keybindings.
type Gui struct {
tbEvents chan termbox.Event
userEvents chan userEvent
views []*View
currentView *View
managers []Manager
keybindings []*keybinding
maxX, maxY int
outputMode OutputMode
// BgColor and FgColor allow to configure the background and foreground
// colors of the GUI.
BgColor, FgColor Attribute
// SelBgColor and SelFgColor allow to configure the background and
// foreground colors of the frame of the current view.
SelBgColor, SelFgColor Attribute
// If Highlight is true, Sel{Bg,Fg}Colors will be used to draw the
// frame of the current view.
Highlight bool
// If Cursor is true then the cursor is enabled.
Cursor bool
// If Mouse is true then mouse events will be enabled.
Mouse bool
// If InputEsc is true, when ESC sequence is in the buffer and it doesn't
// match any known sequence, ESC means KeyEsc.
InputEsc bool
// If ASCII is true then use ASCII instead of unicode to draw the
// interface. Using ASCII is more portable.
ASCII bool
}
// NewGui returns a new Gui object with a given output mode.
func NewGui(mode OutputMode) (*Gui, error) {
if err := termbox.Init(); err != nil {
return nil, err
}
g := &Gui{}
g.outputMode = mode
termbox.SetOutputMode(termbox.OutputMode(mode))
g.tbEvents = make(chan termbox.Event, 20)
g.userEvents = make(chan userEvent, 20)
g.maxX, g.maxY = termbox.Size()
g.BgColor, g.FgColor = ColorDefault, ColorDefault
g.SelBgColor, g.SelFgColor = ColorDefault, ColorDefault
return g, nil
}
// Close finalizes the library. It should be called after a successful
// initialization and when gocui is not needed anymore.
func (g *Gui) Close() {
termbox.Close()
}
// Size returns the terminal's size.
func (g *Gui) Size() (x, y int) {
return g.maxX, g.maxY
}
// SetRune writes a rune at the given point, relative to the top-left
// corner of the terminal. It checks if the position is valid and applies
// the given colors.
func (g *Gui) SetRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
return errors.New("invalid point")
}
termbox.SetCell(x, y, ch, termbox.Attribute(fgColor), termbox.Attribute(bgColor))
return nil
}
// Rune returns the rune contained in the cell at the given position.
// It checks if the position is valid.
func (g *Gui) Rune(x, y int) (rune, error) {
if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
return ' ', errors.New("invalid point")
}
c := termbox.CellBuffer()[y*g.maxX+x]
return c.Ch, nil
}
// SetView creates a new view with its top-left corner at (x0, y0)
// and the bottom-right one at (x1, y1). If a view with the same name
// already exists, its dimensions are updated; otherwise, the error
// ErrUnknownView is returned, which allows to assert if the View must
// be initialized. It checks if the position is valid.
func (g *Gui) SetView(name string, x0, y0, x1, y1 int) (*View, error) {
if x0 >= x1 || y0 >= y1 {
return nil, errors.New("invalid dimensions")
}
if name == "" {
return nil, errors.New("invalid name")
}
if v, err := g.View(name); err == nil {
v.x0 = x0
v.y0 = y0
v.x1 = x1
v.y1 = y1
v.tainted = true
return v, nil
}
v := newView(name, x0, y0, x1, y1, g.outputMode)
v.BgColor, v.FgColor = g.BgColor, g.FgColor
v.SelBgColor, v.SelFgColor = g.SelBgColor, g.SelFgColor
g.views = append(g.views, v)
return v, ErrUnknownView
}
// SetViewOnTop sets the given view on top of the existing ones.
func (g *Gui) SetViewOnTop(name string) (*View, error) {
for i, v := range g.views {
if v.name == name {
s := append(g.views[:i], g.views[i+1:]...)
g.views = append(s, v)
return v, nil
}
}
return nil, ErrUnknownView
}
// SetViewOnBottom sets the given view on bottom of the existing ones.
func (g *Gui) SetViewOnBottom(name string) (*View, error) {
for i, v := range g.views {
if v.name == name {
s := append(g.views[:i], g.views[i+1:]...)
g.views = append([]*View{v}, s...)
return v, nil
}
}
return nil, ErrUnknownView
}
// Views returns all the views in the GUI.
func (g *Gui) Views() []*View {
return g.views
}
// View returns a pointer to the view with the given name, or error
// ErrUnknownView if a view with that name does not exist.
func (g *Gui) View(name string) (*View, error) {
for _, v := range g.views {
if v.name == name {
return v, nil
}
}
return nil, ErrUnknownView
}
// ViewByPosition returns a pointer to a view matching the given position, or
// error ErrUnknownView if a view in that position does not exist.
func (g *Gui) ViewByPosition(x, y int) (*View, error) {
// traverse views in reverse order checking top views first
for i := len(g.views); i > 0; i-- {
v := g.views[i-1]
if x > v.x0 && x < v.x1 && y > v.y0 && y < v.y1 {
return v, nil
}
}
return nil, ErrUnknownView
}
// ViewPosition returns the coordinates of the view with the given name, or
// error ErrUnknownView if a view with that name does not exist.
func (g *Gui) ViewPosition(name string) (x0, y0, x1, y1 int, err error) {
for _, v := range g.views {
if v.name == name {
return v.x0, v.y0, v.x1, v.y1, nil
}
}
return 0, 0, 0, 0, ErrUnknownView
}
// DeleteView deletes a view by name.
func (g *Gui) DeleteView(name string) error {
for i, v := range g.views {
if v.name == name {
g.views = append(g.views[:i], g.views[i+1:]...)
return nil
}
}
return ErrUnknownView
}
// SetCurrentView gives the focus to a given view.
func (g *Gui) SetCurrentView(name string) (*View, error) {
for _, v := range g.views {
if v.name == name {
g.currentView = v
return v, nil
}
}
return nil, ErrUnknownView
}
// CurrentView returns the currently focused view, or nil if no view
// owns the focus.
func (g *Gui) CurrentView() *View {
return g.currentView
}
// SetKeybinding creates a new keybinding. If viewname equals to ""
// (empty string) then the keybinding will apply to all views. key must
// be a rune or a Key.
func (g *Gui) SetKeybinding(viewname string, key interface{}, mod Modifier, handler func(*Gui, *View) error) error {
var kb *keybinding
k, ch, err := getKey(key)
if err != nil {
return err
}
kb = newKeybinding(viewname, k, ch, mod, handler)
g.keybindings = append(g.keybindings, kb)
return nil
}
// DeleteKeybinding deletes a keybinding.
func (g *Gui) DeleteKeybinding(viewname string, key interface{}, mod Modifier) error {
k, ch, err := getKey(key)
if err != nil {
return err
}
for i, kb := range g.keybindings {
if kb.viewName == viewname && kb.ch == ch && kb.key == k && kb.mod == mod {
g.keybindings = append(g.keybindings[:i], g.keybindings[i+1:]...)
return nil
}
}
return errors.New("keybinding not found")
}
// DeleteKeybindings deletes all keybindings of view.
func (g *Gui) DeleteKeybindings(viewname string) {
var s []*keybinding
for _, kb := range g.keybindings {
if kb.viewName != viewname {
s = append(s, kb)
}
}
g.keybindings = s
}
// getKey takes an empty interface with a key and returns the corresponding
// typed Key or rune.
func getKey(key interface{}) (Key, rune, error) {
switch t := key.(type) {
case Key:
return t, 0, nil
case rune:
return 0, t, nil
default:
return 0, 0, errors.New("unknown type")
}
}
// userEvent represents an event triggered by the user.
type userEvent struct {
f func(*Gui) error
}
// Update executes the passed function. This method can be called safely from a
// goroutine in order to update the GUI. It is important to note that the
// passed function won't be executed immediately, instead it will be added to
// the user events queue. Given that Update spawns a goroutine, the order in
// which the user events will be handled is not guaranteed.
func (g *Gui) Update(f func(*Gui) error) {
go func() { g.userEvents <- userEvent{f: f} }()
}
// A Manager is in charge of GUI's layout and can be used to build widgets.
type Manager interface {
// Layout is called every time the GUI is redrawn, it must contain the
// base views and its initializations.
Layout(*Gui) error
}
// The ManagerFunc type is an adapter to allow the use of ordinary functions as
// Managers. If f is a function with the appropriate signature, ManagerFunc(f)
// is an Manager object that calls f.
type ManagerFunc func(*Gui) error
// Layout calls f(g)
func (f ManagerFunc) Layout(g *Gui) error {
return f(g)
}
// SetManager sets the given GUI managers. It deletes all views and
// keybindings.
func (g *Gui) SetManager(managers ...Manager) {
g.managers = managers
g.currentView = nil
g.views = nil
g.keybindings = nil
go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }()
}
// SetManagerFunc sets the given manager function. It deletes all views and
// keybindings.
func (g *Gui) SetManagerFunc(manager func(*Gui) error) {
g.SetManager(ManagerFunc(manager))
}
// MainLoop runs the main loop until an error is returned. A successful
// finish should return ErrQuit.
func (g *Gui) MainLoop() error {
go func() {
for {
g.tbEvents <- termbox.PollEvent()
}
}()
inputMode := termbox.InputAlt
if g.InputEsc {
inputMode = termbox.InputEsc
}
if g.Mouse {
inputMode |= termbox.InputMouse
}
termbox.SetInputMode(inputMode)
if err := g.flush(); err != nil {
return err
}
for {
select {
case ev := <-g.tbEvents:
if err := g.handleEvent(&ev); err != nil {
return err
}
case ev := <-g.userEvents:
if err := ev.f(g); err != nil {
return err
}
}
if err := g.consumeevents(); err != nil {
return err
}
if err := g.flush(); err != nil {
return err
}
}
}
// consumeevents handles the remaining events in the events pool.
func (g *Gui) consumeevents() error {
for {
select {
case ev := <-g.tbEvents:
if err := g.handleEvent(&ev); err != nil {
return err
}
case ev := <-g.userEvents:
if err := ev.f(g); err != nil {
return err
}
default:
return nil
}
}
}
// handleEvent handles an event, based on its type (key-press, error,
// etc.)
func (g *Gui) handleEvent(ev *termbox.Event) error {
switch ev.Type {
case termbox.EventKey, termbox.EventMouse:
return g.onKey(ev)
case termbox.EventError:
return ev.Err
default:
return nil
}
}
// flush updates the gui, re-drawing frames and buffers.
func (g *Gui) flush() error {
termbox.Clear(termbox.Attribute(g.FgColor), termbox.Attribute(g.BgColor))
maxX, maxY := termbox.Size()
// if GUI's size has changed, we need to redraw all views
if maxX != g.maxX || maxY != g.maxY {
for _, v := range g.views {
v.tainted = true
}
}
g.maxX, g.maxY = maxX, maxY
for _, m := range g.managers {
if err := m.Layout(g); err != nil {
return err
}
}
for _, v := range g.views {
if v.Frame {
var fgColor, bgColor Attribute
if g.Highlight && v == g.currentView {
fgColor = g.SelFgColor
bgColor = g.SelBgColor
} else {
fgColor = g.FgColor
bgColor = g.BgColor
}
if err := g.drawFrameEdges(v, fgColor, bgColor); err != nil {
return err
}
if err := g.drawFrameCorners(v, fgColor, bgColor); err != nil {
return err
}
if v.Title != "" {
if err := g.drawTitle(v, fgColor, bgColor); err != nil {
return err
}
}
}
if err := g.draw(v); err != nil {
return err
}
}
termbox.Flush()
return nil
}
// drawFrameEdges draws the horizontal and vertical edges of a view.
func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error {
runeH, runeV := '─', '│'
if g.ASCII {
runeH, runeV = '-', '|'
}
for x := v.x0 + 1; x < v.x1 && x < g.maxX; x++ {
if x < 0 {
continue
}
if v.y0 > -1 && v.y0 < g.maxY {
if err := g.SetRune(x, v.y0, runeH, fgColor, bgColor); err != nil {
return err
}
}
if v.y1 > -1 && v.y1 < g.maxY {
if err := g.SetRune(x, v.y1, runeH, fgColor, bgColor); err != nil {
return err
}
}
}
for y := v.y0 + 1; y < v.y1 && y < g.maxY; y++ {
if y < 0 {
continue
}
if v.x0 > -1 && v.x0 < g.maxX {
if err := g.SetRune(v.x0, y, runeV, fgColor, bgColor); err != nil {
return err
}
}
if v.x1 > -1 && v.x1 < g.maxX {
if err := g.SetRune(v.x1, y, runeV, fgColor, bgColor); err != nil {
return err
}
}
}
return nil
}
// drawFrameCorners draws the corners of the view.
func (g *Gui) drawFrameCorners(v *View, fgColor, bgColor Attribute) error {
runeTL, runeTR, runeBL, runeBR := '┌', '┐', '└', '┘'
if g.ASCII {
runeTL, runeTR, runeBL, runeBR = '+', '+', '+', '+'
}
corners := []struct {
x, y int
ch rune
}{{v.x0, v.y0, runeTL}, {v.x1, v.y0, runeTR}, {v.x0, v.y1, runeBL}, {v.x1, v.y1, runeBR}}
for _, c := range corners {
if c.x >= 0 && c.y >= 0 && c.x < g.maxX && c.y < g.maxY {
if err := g.SetRune(c.x, c.y, c.ch, fgColor, bgColor); err != nil {
return err
}
}
}
return nil
}
// drawTitle draws the title of the view.
func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error {
if v.y0 < 0 || v.y0 >= g.maxY {
return nil
}
for i, ch := range v.Title {
x := v.x0 + i + 2
if x < 0 {
continue
} else if x > v.x1-2 || x >= g.maxX {
break
}
if err := g.SetRune(x, v.y0, ch, fgColor, bgColor); err != nil {
return err
}
}
return nil
}
// draw manages the cursor and calls the draw function of a view.
func (g *Gui) draw(v *View) error {
if g.Cursor {
if curview := g.currentView; curview != nil {
vMaxX, vMaxY := curview.Size()
if curview.cx < 0 {
curview.cx = 0
} else if curview.cx >= vMaxX {
curview.cx = vMaxX - 1
}
if curview.cy < 0 {
curview.cy = 0
} else if curview.cy >= vMaxY {
curview.cy = vMaxY - 1
}
gMaxX, gMaxY := g.Size()
cx, cy := curview.x0+curview.cx+1, curview.y0+curview.cy+1
if cx >= 0 && cx < gMaxX && cy >= 0 && cy < gMaxY {
termbox.SetCursor(cx, cy)
} else {
termbox.HideCursor()
}
}
} else {
termbox.HideCursor()
}
v.clearRunes()
if err := v.draw(); err != nil {
return err
}
return nil
}
// onKey manages key-press events. A keybinding handler is called when
// a key-press or mouse event satisfies a configured keybinding. Furthermore,
// currentView's internal buffer is modified if currentView.Editable is true.
func (g *Gui) onKey(ev *termbox.Event) error {
switch ev.Type {
case termbox.EventKey:
matched, err := g.execKeybindings(g.currentView, ev)
if err != nil {
return err
}
if matched {
break
}
if g.currentView != nil && g.currentView.Editable && g.currentView.Editor != nil {
g.currentView.Editor.Edit(g.currentView, Key(ev.Key), ev.Ch, Modifier(ev.Mod))
}
case termbox.EventMouse:
mx, my := ev.MouseX, ev.MouseY
v, err := g.ViewByPosition(mx, my)
if err != nil {
break
}
if err := v.SetCursor(mx-v.x0-1, my-v.y0-1); err != nil {
return err
}
if _, err := g.execKeybindings(v, ev); err != nil {
return err
}
}
return nil
}
// execKeybindings executes the keybinding handlers that match the passed view
// and event. The value of matched is true if there is a match and no errors.
func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err error) {
matched = false
for _, kb := range g.keybindings {
if kb.handler == nil {
continue
}
if kb.matchKeypress(Key(ev.Key), ev.Ch, Modifier(ev.Mod)) && kb.matchView(v) {
if err := kb.handler(g, v); err != nil {
return false, err
}
matched = true
}
}
return matched, nil
}

@ -0,0 +1,137 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gocui
import "github.com/nsf/termbox-go"
// Keybidings are used to link a given key-press event with a handler.
type keybinding struct {
viewName string
key Key
ch rune
mod Modifier
handler func(*Gui, *View) error
}
// newKeybinding returns a new Keybinding object.
func newKeybinding(viewname string, key Key, ch rune, mod Modifier, handler func(*Gui, *View) error) (kb *keybinding) {
kb = &keybinding{
viewName: viewname,
key: key,
ch: ch,
mod: mod,
handler: handler,
}
return kb
}
// matchKeypress returns if the keybinding matches the keypress.
func (kb *keybinding) matchKeypress(key Key, ch rune, mod Modifier) bool {
return kb.key == key && kb.ch == ch && kb.mod == mod
}
// matchView returns if the keybinding matches the current view.
func (kb *keybinding) matchView(v *View) bool {
if kb.viewName == "" {
return true
}
return v != nil && kb.viewName == v.name
}
// Key represents special keys or keys combinations.
type Key termbox.Key
// Special keys.
const (
KeyF1 Key = Key(termbox.KeyF1)
KeyF2 = Key(termbox.KeyF2)
KeyF3 = Key(termbox.KeyF3)
KeyF4 = Key(termbox.KeyF4)
KeyF5 = Key(termbox.KeyF5)
KeyF6 = Key(termbox.KeyF6)
KeyF7 = Key(termbox.KeyF7)
KeyF8 = Key(termbox.KeyF8)
KeyF9 = Key(termbox.KeyF9)
KeyF10 = Key(termbox.KeyF10)
KeyF11 = Key(termbox.KeyF11)
KeyF12 = Key(termbox.KeyF12)
KeyInsert = Key(termbox.KeyInsert)
KeyDelete = Key(termbox.KeyDelete)
KeyHome = Key(termbox.KeyHome)
KeyEnd = Key(termbox.KeyEnd)
KeyPgup = Key(termbox.KeyPgup)
KeyPgdn = Key(termbox.KeyPgdn)
KeyArrowUp = Key(termbox.KeyArrowUp)
KeyArrowDown = Key(termbox.KeyArrowDown)
KeyArrowLeft = Key(termbox.KeyArrowLeft)
KeyArrowRight = Key(termbox.KeyArrowRight)
MouseLeft = Key(termbox.MouseLeft)
MouseMiddle = Key(termbox.MouseMiddle)
MouseRight = Key(termbox.MouseRight)
MouseRelease = Key(termbox.MouseRelease)
MouseWheelUp = Key(termbox.MouseWheelUp)
MouseWheelDown = Key(termbox.MouseWheelDown)
)
// Keys combinations.
const (
KeyCtrlTilde Key = Key(termbox.KeyCtrlTilde)
KeyCtrl2 = Key(termbox.KeyCtrl2)
KeyCtrlSpace = Key(termbox.KeyCtrlSpace)
KeyCtrlA = Key(termbox.KeyCtrlA)
KeyCtrlB = Key(termbox.KeyCtrlB)
KeyCtrlC = Key(termbox.KeyCtrlC)
KeyCtrlD = Key(termbox.KeyCtrlD)
KeyCtrlE = Key(termbox.KeyCtrlE)
KeyCtrlF = Key(termbox.KeyCtrlF)
KeyCtrlG = Key(termbox.KeyCtrlG)
KeyBackspace = Key(termbox.KeyBackspace)
KeyCtrlH = Key(termbox.KeyCtrlH)
KeyTab = Key(termbox.KeyTab)
KeyCtrlI = Key(termbox.KeyCtrlI)
KeyCtrlJ = Key(termbox.KeyCtrlJ)
KeyCtrlK = Key(termbox.KeyCtrlK)
KeyCtrlL = Key(termbox.KeyCtrlL)
KeyEnter = Key(termbox.KeyEnter)
KeyCtrlM = Key(termbox.KeyCtrlM)
KeyCtrlN = Key(termbox.KeyCtrlN)
KeyCtrlO = Key(termbox.KeyCtrlO)
KeyCtrlP = Key(termbox.KeyCtrlP)
KeyCtrlQ = Key(termbox.KeyCtrlQ)
KeyCtrlR = Key(termbox.KeyCtrlR)
KeyCtrlS = Key(termbox.KeyCtrlS)
KeyCtrlT = Key(termbox.KeyCtrlT)
KeyCtrlU = Key(termbox.KeyCtrlU)
KeyCtrlV = Key(termbox.KeyCtrlV)
KeyCtrlW = Key(termbox.KeyCtrlW)
KeyCtrlX = Key(termbox.KeyCtrlX)
KeyCtrlY = Key(termbox.KeyCtrlY)
KeyCtrlZ = Key(termbox.KeyCtrlZ)
KeyEsc = Key(termbox.KeyEsc)
KeyCtrlLsqBracket = Key(termbox.KeyCtrlLsqBracket)
KeyCtrl3 = Key(termbox.KeyCtrl3)
KeyCtrl4 = Key(termbox.KeyCtrl4)
KeyCtrlBackslash = Key(termbox.KeyCtrlBackslash)
KeyCtrl5 = Key(termbox.KeyCtrl5)
KeyCtrlRsqBracket = Key(termbox.KeyCtrlRsqBracket)
KeyCtrl6 = Key(termbox.KeyCtrl6)
KeyCtrl7 = Key(termbox.KeyCtrl7)
KeyCtrlSlash = Key(termbox.KeyCtrlSlash)
KeyCtrlUnderscore = Key(termbox.KeyCtrlUnderscore)
KeySpace = Key(termbox.KeySpace)
KeyBackspace2 = Key(termbox.KeyBackspace2)
KeyCtrl8 = Key(termbox.KeyCtrl8)
)
// Modifier allows to define special keys combinations. They can be used
// in combination with Keys or Runes when a new keybinding is defined.
type Modifier termbox.Modifier
// Modifiers.
const (
ModNone Modifier = Modifier(0)
ModAlt = Modifier(termbox.ModAlt)
)

@ -0,0 +1,503 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gocui
import (
"bytes"
"errors"
"io"
"strings"
"github.com/nsf/termbox-go"
)
// A View is a window. It maintains its own internal buffer and cursor
// position.
type View struct {
name string
x0, y0, x1, y1 int
ox, oy int
cx, cy int
lines [][]cell
readOffset int
readCache string
tainted bool // marks if the viewBuffer must be updated
viewLines []viewLine // internal representation of the view's buffer
ei *escapeInterpreter // used to decode ESC sequences on Write
// BgColor and FgColor allow to configure the background and foreground
// colors of the View.
BgColor, FgColor Attribute
// SelBgColor and SelFgColor are used to configure the background and
// foreground colors of the selected line, when it is highlighted.
SelBgColor, SelFgColor Attribute
// If Editable is true, keystrokes will be added to the view's internal
// buffer at the cursor position.
Editable bool
// Editor allows to define the editor that manages the edition mode,
// including keybindings or cursor behaviour. DefaultEditor is used by
// default.
Editor Editor
// Overwrite enables or disables the overwrite mode of the view.
Overwrite bool
// If Highlight is true, Sel{Bg,Fg}Colors will be used
// for the line under the cursor position.
Highlight bool
// If Frame is true, a border will be drawn around the view.
Frame bool
// If Wrap is true, the content that is written to this View is
// automatically wrapped when it is longer than its width. If true the
// view's x-origin will be ignored.
Wrap bool
// If Autoscroll is true, the View will automatically scroll down when the
// text overflows. If true the view's y-origin will be ignored.
Autoscroll bool
// If Frame is true, Title allows to configure a title for the view.
Title string
// If Mask is true, the View will display the mask instead of the real
// content
Mask rune
}
type viewLine struct {
linesX, linesY int // coordinates relative to v.lines
line []cell
}
type cell struct {
chr rune
bgColor, fgColor Attribute
}
type lineType []cell
// String returns a string from a given cell slice.
func (l lineType) String() string {
str := ""
for _, c := range l {
str += string(c.chr)
}
return str
}
// newView returns a new View object.
func newView(name string, x0, y0, x1, y1 int, mode OutputMode) *View {
v := &View{
name: name,
x0: x0,
y0: y0,
x1: x1,
y1: y1,
Frame: true,
Editor: DefaultEditor,
tainted: true,
ei: newEscapeInterpreter(mode),
}
return v
}
// Size returns the number of visible columns and rows in the View.
func (v *View) Size() (x, y int) {
return v.x1 - v.x0 - 1, v.y1 - v.y0 - 1
}
// Name returns the name of the view.
func (v *View) Name() string {
return v.name
}
// setRune sets a rune at the given point relative to the view. It applies the
// specified colors, taking into account if the cell must be highlighted. Also,
// it checks if the position is valid.
func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
maxX, maxY := v.Size()
if x < 0 || x >= maxX || y < 0 || y >= maxY {
return errors.New("invalid point")
}
var (
ry, rcy int
err error
)
if v.Highlight {
_, ry, err = v.realPosition(x, y)
if err != nil {
return err
}
_, rcy, err = v.realPosition(v.cx, v.cy)
if err != nil {
return err
}
}
if v.Mask != 0 {
fgColor = v.FgColor
bgColor = v.BgColor
ch = v.Mask
} else if v.Highlight && ry == rcy {
fgColor = v.SelFgColor
bgColor = v.SelBgColor
}
termbox.SetCell(v.x0+x+1, v.y0+y+1, ch,
termbox.Attribute(fgColor), termbox.Attribute(bgColor))
return nil
}
// SetCursor sets the cursor position of the view at the given point,
// relative to the view. It checks if the position is valid.
func (v *View) SetCursor(x, y int) error {
maxX, maxY := v.Size()
if x < 0 || x >= maxX || y < 0 || y >= maxY {
return errors.New("invalid point")
}
v.cx = x
v.cy = y
return nil
}
// Cursor returns the cursor position of the view.
func (v *View) Cursor() (x, y int) {
return v.cx, v.cy
}
// SetOrigin sets the origin position of the view's internal buffer,
// so the buffer starts to be printed from this point, which means that
// it is linked with the origin point of view. It can be used to
// implement Horizontal and Vertical scrolling with just incrementing
// or decrementing ox and oy.
func (v *View) SetOrigin(x, y int) error {
if x < 0 || y < 0 {
return errors.New("invalid point")
}
v.ox = x
v.oy = y
return nil
}
// Origin returns the origin position of the view.
func (v *View) Origin() (x, y int) {
return v.ox, v.oy
}
// Write appends a byte slice into the view's internal buffer. Because
// View implements the io.Writer interface, it can be passed as parameter
// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
// be called to clear the view's buffer.
func (v *View) Write(p []byte) (n int, err error) {
v.tainted = true
for _, ch := range bytes.Runes(p) {
switch ch {
case '\n':
v.lines = append(v.lines, nil)
case '\r':
nl := len(v.lines)
if nl > 0 {
v.lines[nl-1] = nil
} else {
v.lines = make([][]cell, 1)
}
default:
cells := v.parseInput(ch)
if cells == nil {
continue
}
nl := len(v.lines)
if nl > 0 {
v.lines[nl-1] = append(v.lines[nl-1], cells...)
} else {
v.lines = append(v.lines, cells)
}
}
}
return len(p), nil
}
// parseInput parses char by char the input written to the View. It returns nil
// while processing ESC sequences. Otherwise, it returns a cell slice that
// contains the processed data.
func (v *View) parseInput(ch rune) []cell {
cells := []cell{}
isEscape, err := v.ei.parseOne(ch)
if err != nil {
for _, r := range v.ei.runes() {
c := cell{
fgColor: v.FgColor,
bgColor: v.BgColor,
chr: r,
}
cells = append(cells, c)
}
v.ei.reset()
} else {
if isEscape {
return nil
}
c := cell{
fgColor: v.ei.curFgColor,
bgColor: v.ei.curBgColor,
chr: ch,
}
cells = append(cells, c)
}
return cells
}
// Read reads data into p. It returns the number of bytes read into p.
// At EOF, err will be io.EOF. Calling Read() after Rewind() makes the
// cache to be refreshed with the contents of the view.
func (v *View) Read(p []byte) (n int, err error) {
if v.readOffset == 0 {
v.readCache = v.Buffer()
}
if v.readOffset < len(v.readCache) {
n = copy(p, v.readCache[v.readOffset:])
v.readOffset += n
} else {
err = io.EOF
}
return
}
// Rewind sets the offset for the next Read to 0, which also refresh the
// read cache.
func (v *View) Rewind() {
v.readOffset = 0
}
// draw re-draws the view's contents.
func (v *View) draw() error {
maxX, maxY := v.Size()
if v.Wrap {
if maxX == 0 {
return errors.New("X size of the view cannot be 0")
}
v.ox = 0
}
if v.tainted {
v.viewLines = nil
for i, line := range v.lines {
if v.Wrap {
if len(line) < maxX {
vline := viewLine{linesX: 0, linesY: i, line: line}
v.viewLines = append(v.viewLines, vline)
continue
} else {
for n := 0; n <= len(line); n += maxX {
if len(line[n:]) <= maxX {
vline := viewLine{linesX: n, linesY: i, line: line[n:]}
v.viewLines = append(v.viewLines, vline)
} else {
vline := viewLine{linesX: n, linesY: i, line: line[n : n+maxX]}
v.viewLines = append(v.viewLines, vline)
}
}
}
} else {
vline := viewLine{linesX: 0, linesY: i, line: line}
v.viewLines = append(v.viewLines, vline)
}
}
v.tainted = false
}
if v.Autoscroll && len(v.viewLines) > maxY {
v.oy = len(v.viewLines) - maxY
}
y := 0
for i, vline := range v.viewLines {
if i < v.oy {
continue
}
if y >= maxY {
break
}
x := 0
for j, c := range vline.line {
if j < v.ox {
continue
}
if x >= maxX {
break
}
fgColor := c.fgColor
if fgColor == ColorDefault {
fgColor = v.FgColor
}
bgColor := c.bgColor
if bgColor == ColorDefault {
bgColor = v.BgColor
}
if err := v.setRune(x, y, c.chr, fgColor, bgColor); err != nil {
return err
}
x++
}
y++
}
return nil
}
// realPosition returns the position in the internal buffer corresponding to the
// point (x, y) of the view.
func (v *View) realPosition(vx, vy int) (x, y int, err error) {
vx = v.ox + vx
vy = v.oy + vy
if vx < 0 || vy < 0 {
return 0, 0, errors.New("invalid point")
}
if len(v.viewLines) == 0 {
return vx, vy, nil
}
if vy < len(v.viewLines) {
vline := v.viewLines[vy]
x = vline.linesX + vx
y = vline.linesY
} else {
vline := v.viewLines[len(v.viewLines)-1]
x = vx
y = vline.linesY + vy - len(v.viewLines) + 1
}
return x, y, nil
}
// Clear empties the view's internal buffer.
func (v *View) Clear() {
v.tainted = true
v.lines = nil
v.viewLines = nil
v.readOffset = 0
v.clearRunes()
}
// clearRunes erases all the cells in the view.
func (v *View) clearRunes() {
maxX, maxY := v.Size()
for x := 0; x < maxX; x++ {
for y := 0; y < maxY; y++ {
termbox.SetCell(v.x0+x+1, v.y0+y+1, ' ',
termbox.Attribute(v.FgColor), termbox.Attribute(v.BgColor))
}
}
}
// BufferLines returns the lines in the view's internal
// buffer.
func (v *View) BufferLines() []string {
lines := make([]string, len(v.lines))
for i, l := range v.lines {
str := lineType(l).String()
str = strings.Replace(str, "\x00", " ", -1)
lines[i] = str
}
return lines
}
// Buffer returns a string with the contents of the view's internal
// buffer.
func (v *View) Buffer() string {
str := ""
for _, l := range v.lines {
str += lineType(l).String() + "\n"
}
return strings.Replace(str, "\x00", " ", -1)
}
// ViewBufferLines returns the lines in the view's internal
// buffer that is shown to the user.
func (v *View) ViewBufferLines() []string {
lines := make([]string, len(v.viewLines))
for i, l := range v.viewLines {
str := lineType(l.line).String()
str = strings.Replace(str, "\x00", " ", -1)
lines[i] = str
}
return lines
}
// ViewBuffer returns a string with the contents of the view's buffer that is
// shown to the user.
func (v *View) ViewBuffer() string {
str := ""
for _, l := range v.viewLines {
str += lineType(l.line).String() + "\n"
}
return strings.Replace(str, "\x00", " ", -1)
}
// Line returns a string with the line of the view's internal buffer
// at the position corresponding to the point (x, y).
func (v *View) Line(y int) (string, error) {
_, y, err := v.realPosition(0, y)
if err != nil {
return "", err
}
if y < 0 || y >= len(v.lines) {
return "", errors.New("invalid point")
}
return lineType(v.lines[y]).String(), nil
}
// Word returns a string with the word of the view's internal buffer
// at the position corresponding to the point (x, y).
func (v *View) Word(x, y int) (string, error) {
x, y, err := v.realPosition(x, y)
if err != nil {
return "", err
}
if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
return "", errors.New("invalid point")
}
str := lineType(v.lines[y]).String()
nl := strings.LastIndexFunc(str[:x], indexFunc)
if nl == -1 {
nl = 0
} else {
nl = nl + 1
}
nr := strings.IndexFunc(str[x:], indexFunc)
if nr == -1 {
nr = len(str)
} else {
nr = nr + x
}
return string(str[nl:nr]), nil
}
// indexFunc allows to split lines by words taking into account spaces
// and 0.
func indexFunc(r rune) bool {
return r == ' ' || r == 0
}

@ -0,0 +1,8 @@
language: go
go:
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Yasuhiro Matsumoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,27 @@
go-runewidth
============
[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth)
[![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD)
[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth)
[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth)
Provides functions to get fixed width of the character or string.
Usage
-----
```go
runewidth.StringWidth("つのだ☆HIRO") == 12
```
Author
------
Yasuhiro Matsumoto
License
-------
under the MIT License: http://mattn.mit-license.org/2013

@ -0,0 +1,977 @@
package runewidth
import (
"os"
)
var (
// EastAsianWidth will be set true if the current locale is CJK
EastAsianWidth bool
// ZeroWidthJoiner is flag to set to use UTR#51 ZWJ
ZeroWidthJoiner bool
// DefaultCondition is a condition in current locale
DefaultCondition = &Condition{}
)
func init() {
handleEnv()
}
func handleEnv() {
env := os.Getenv("RUNEWIDTH_EASTASIAN")
if env == "" {
EastAsianWidth = IsEastAsian()
} else {
EastAsianWidth = env == "1"
}
// update DefaultCondition
DefaultCondition.EastAsianWidth = EastAsianWidth
DefaultCondition.ZeroWidthJoiner = ZeroWidthJoiner
}
type interval struct {
first rune
last rune
}
type table []interval
func inTables(r rune, ts ...table) bool {
for _, t := range ts {
if inTable(r, t) {
return true
}
}
return false
}
func inTable(r rune, t table) bool {
// func (t table) IncludesRune(r rune) bool {
if r < t[0].first {
return false
}
bot := 0
top := len(t) - 1
for top >= bot {
mid := (bot + top) >> 1
switch {
case t[mid].last < r:
bot = mid + 1
case t[mid].first > r:
top = mid - 1
default:
return true
}
}
return false
}
var private = table{
{0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD},
}
var nonprint = table{
{0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD},
{0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F},
{0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF},
{0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF},
}
var combining = table{
{0x0300, 0x036F}, {0x0483, 0x0489}, {0x0591, 0x05BD},
{0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5},
{0x05C7, 0x05C7}, {0x0610, 0x061A}, {0x064B, 0x065F},
{0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DF, 0x06E4},
{0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x0711, 0x0711},
{0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3},
{0x0816, 0x0819}, {0x081B, 0x0823}, {0x0825, 0x0827},
{0x0829, 0x082D}, {0x0859, 0x085B}, {0x08D4, 0x08E1},
{0x08E3, 0x0903}, {0x093A, 0x093C}, {0x093E, 0x094F},
{0x0951, 0x0957}, {0x0962, 0x0963}, {0x0981, 0x0983},
{0x09BC, 0x09BC}, {0x09BE, 0x09C4}, {0x09C7, 0x09C8},
{0x09CB, 0x09CD}, {0x09D7, 0x09D7}, {0x09E2, 0x09E3},
{0x0A01, 0x0A03}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42},
{0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51},
{0x0A70, 0x0A71}, {0x0A75, 0x0A75}, {0x0A81, 0x0A83},
{0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9},
{0x0ACB, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0B01, 0x0B03},
{0x0B3C, 0x0B3C}, {0x0B3E, 0x0B44}, {0x0B47, 0x0B48},
{0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, {0x0B62, 0x0B63},
{0x0B82, 0x0B82}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8},
{0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, {0x0C00, 0x0C03},
{0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D},
{0x0C55, 0x0C56}, {0x0C62, 0x0C63}, {0x0C81, 0x0C83},
{0x0CBC, 0x0CBC}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8},
{0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CE2, 0x0CE3},
{0x0D01, 0x0D03}, {0x0D3E, 0x0D44}, {0x0D46, 0x0D48},
{0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, {0x0D62, 0x0D63},
{0x0D82, 0x0D83}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4},
{0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DF2, 0x0DF3},
{0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E},
{0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC},
{0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35},
{0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F3E, 0x0F3F},
{0x0F71, 0x0F84}, {0x0F86, 0x0F87}, {0x0F8D, 0x0F97},
{0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102B, 0x103E},
{0x1056, 0x1059}, {0x105E, 0x1060}, {0x1062, 0x1064},
{0x1067, 0x106D}, {0x1071, 0x1074}, {0x1082, 0x108D},
{0x108F, 0x108F}, {0x109A, 0x109D}, {0x135D, 0x135F},
{0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753},
{0x1772, 0x1773}, {0x17B4, 0x17D3}, {0x17DD, 0x17DD},
{0x180B, 0x180D}, {0x1885, 0x1886}, {0x18A9, 0x18A9},
{0x1920, 0x192B}, {0x1930, 0x193B}, {0x1A17, 0x1A1B},
{0x1A55, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A7F},
{0x1AB0, 0x1ABE}, {0x1B00, 0x1B04}, {0x1B34, 0x1B44},
{0x1B6B, 0x1B73}, {0x1B80, 0x1B82}, {0x1BA1, 0x1BAD},
{0x1BE6, 0x1BF3}, {0x1C24, 0x1C37}, {0x1CD0, 0x1CD2},
{0x1CD4, 0x1CE8}, {0x1CED, 0x1CED}, {0x1CF2, 0x1CF4},
{0x1CF8, 0x1CF9}, {0x1DC0, 0x1DF5}, {0x1DFB, 0x1DFF},
{0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F},
{0x2DE0, 0x2DFF}, {0x302A, 0x302F}, {0x3099, 0x309A},
{0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F},
{0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806},
{0xA80B, 0xA80B}, {0xA823, 0xA827}, {0xA880, 0xA881},
{0xA8B4, 0xA8C5}, {0xA8E0, 0xA8F1}, {0xA926, 0xA92D},
{0xA947, 0xA953}, {0xA980, 0xA983}, {0xA9B3, 0xA9C0},
{0xA9E5, 0xA9E5}, {0xAA29, 0xAA36}, {0xAA43, 0xAA43},
{0xAA4C, 0xAA4D}, {0xAA7B, 0xAA7D}, {0xAAB0, 0xAAB0},
{0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF},
{0xAAC1, 0xAAC1}, {0xAAEB, 0xAAEF}, {0xAAF5, 0xAAF6},
{0xABE3, 0xABEA}, {0xABEC, 0xABED}, {0xFB1E, 0xFB1E},
{0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD},
{0x102E0, 0x102E0}, {0x10376, 0x1037A}, {0x10A01, 0x10A03},
{0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, {0x10A38, 0x10A3A},
{0x10A3F, 0x10A3F}, {0x10AE5, 0x10AE6}, {0x11000, 0x11002},
{0x11038, 0x11046}, {0x1107F, 0x11082}, {0x110B0, 0x110BA},
{0x11100, 0x11102}, {0x11127, 0x11134}, {0x11173, 0x11173},
{0x11180, 0x11182}, {0x111B3, 0x111C0}, {0x111CA, 0x111CC},
{0x1122C, 0x11237}, {0x1123E, 0x1123E}, {0x112DF, 0x112EA},
{0x11300, 0x11303}, {0x1133C, 0x1133C}, {0x1133E, 0x11344},
{0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11357, 0x11357},
{0x11362, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374},
{0x11435, 0x11446}, {0x114B0, 0x114C3}, {0x115AF, 0x115B5},
{0x115B8, 0x115C0}, {0x115DC, 0x115DD}, {0x11630, 0x11640},
{0x116AB, 0x116B7}, {0x1171D, 0x1172B}, {0x11C2F, 0x11C36},
{0x11C38, 0x11C3F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6},
{0x16AF0, 0x16AF4}, {0x16B30, 0x16B36}, {0x16F51, 0x16F7E},
{0x16F8F, 0x16F92}, {0x1BC9D, 0x1BC9E}, {0x1D165, 0x1D169},
{0x1D16D, 0x1D172}, {0x1D17B, 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},
{0xE0100, 0xE01EF},
}
var doublewidth = table{
{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, 0x312D}, {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, 0x16FE0}, {0x17000, 0x187EC},
{0x18800, 0x18AF2}, {0x1B000, 0x1B001}, {0x1F004, 0x1F004},
{0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A},
{0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248},
{0x1F250, 0x1F251}, {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, 0x1F6F6},
{0x1F910, 0x1F91E}, {0x1F920, 0x1F927}, {0x1F930, 0x1F930},
{0x1F933, 0x1F93E}, {0x1F940, 0x1F94B}, {0x1F950, 0x1F95E},
{0x1F980, 0x1F991}, {0x1F9C0, 0x1F9C0}, {0x20000, 0x2FFFD},
{0x30000, 0x3FFFD},
}
var ambiguous = table{
{0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8},
{0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4},
{0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6},
{0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1},
{0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED},
{0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA},
{0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101},
{0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B},
{0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133},
{0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144},
{0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153},
{0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE},
{0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4},
{0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA},
{0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261},
{0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB},
{0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB},
{0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F},
{0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1},
{0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F},
{0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016},
{0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022},
{0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033},
{0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E},
{0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084},
{0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105},
{0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116},
{0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B},
{0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B},
{0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199},
{0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4},
{0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203},
{0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F},
{0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A},
{0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225},
{0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237},
{0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C},
{0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267},
{0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283},
{0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299},
{0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312},
{0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573},
{0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1},
{0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7},
{0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8},
{0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5},
{0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609},
{0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E},
{0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661},
{0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D},
{0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF},
{0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1},
{0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1},
{0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC},
{0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F},
{0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF},
{0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A},
{0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D},
{0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF},
{0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD},
}
var emoji = table{
{0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122},
{0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA},
{0x231A, 0x231B}, {0x2328, 0x2328}, {0x23CF, 0x23CF},
{0x23E9, 0x23F3}, {0x23F8, 0x23FA}, {0x24C2, 0x24C2},
{0x25AA, 0x25AB}, {0x25B6, 0x25B6}, {0x25C0, 0x25C0},
{0x25FB, 0x25FE}, {0x2600, 0x2604}, {0x260E, 0x260E},
{0x2611, 0x2611}, {0x2614, 0x2615}, {0x2618, 0x2618},
{0x261D, 0x261D}, {0x2620, 0x2620}, {0x2622, 0x2623},
{0x2626, 0x2626}, {0x262A, 0x262A}, {0x262E, 0x262F},
{0x2638, 0x263A}, {0x2640, 0x2640}, {0x2642, 0x2642},
{0x2648, 0x2653}, {0x265F, 0x2660}, {0x2663, 0x2663},
{0x2665, 0x2666}, {0x2668, 0x2668}, {0x267B, 0x267B},
{0x267E, 0x267F}, {0x2692, 0x2697}, {0x2699, 0x2699},
{0x269B, 0x269C}, {0x26A0, 0x26A1}, {0x26AA, 0x26AB},
{0x26B0, 0x26B1}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5},
{0x26C8, 0x26C8}, {0x26CE, 0x26CF}, {0x26D1, 0x26D1},
{0x26D3, 0x26D4}, {0x26E9, 0x26EA}, {0x26F0, 0x26F5},
{0x26F7, 0x26FA}, {0x26FD, 0x26FD}, {0x2702, 0x2702},
{0x2705, 0x2705}, {0x2708, 0x270D}, {0x270F, 0x270F},
{0x2712, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716},
{0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728},
{0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747},
{0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755},
{0x2757, 0x2757}, {0x2763, 0x2764}, {0x2795, 0x2797},
{0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF},
{0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C},
{0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030},
{0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299},
{0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F170, 0x1F171},
{0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A},
{0x1F1E6, 0x1F1FF}, {0x1F201, 0x1F202}, {0x1F21A, 0x1F21A},
{0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, {0x1F250, 0x1F251},
{0x1F300, 0x1F321}, {0x1F324, 0x1F393}, {0x1F396, 0x1F397},
{0x1F399, 0x1F39B}, {0x1F39E, 0x1F3F0}, {0x1F3F3, 0x1F3F5},
{0x1F3F7, 0x1F4FD}, {0x1F4FF, 0x1F53D}, {0x1F549, 0x1F54E},
{0x1F550, 0x1F567}, {0x1F56F, 0x1F570}, {0x1F573, 0x1F57A},
{0x1F587, 0x1F587}, {0x1F58A, 0x1F58D}, {0x1F590, 0x1F590},
{0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A5}, {0x1F5A8, 0x1F5A8},
{0x1F5B1, 0x1F5B2}, {0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4},
{0x1F5D1, 0x1F5D3}, {0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1},
{0x1F5E3, 0x1F5E3}, {0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF},
{0x1F5F3, 0x1F5F3}, {0x1F5FA, 0x1F64F}, {0x1F680, 0x1F6C5},
{0x1F6CB, 0x1F6D2}, {0x1F6E0, 0x1F6E5}, {0x1F6E9, 0x1F6E9},
{0x1F6EB, 0x1F6EC}, {0x1F6F0, 0x1F6F0}, {0x1F6F3, 0x1F6F9},
{0x1F910, 0x1F93A}, {0x1F93C, 0x1F93E}, {0x1F940, 0x1F945},
{0x1F947, 0x1F970}, {0x1F973, 0x1F976}, {0x1F97A, 0x1F97A},
{0x1F97C, 0x1F9A2}, {0x1F9B0, 0x1F9B9}, {0x1F9C0, 0x1F9C2},
{0x1F9D0, 0x1F9FF},
}
var notassigned = table{
{0x0378, 0x0379}, {0x0380, 0x0383}, {0x038B, 0x038B},
{0x038D, 0x038D}, {0x03A2, 0x03A2}, {0x0530, 0x0530},
{0x0557, 0x0558}, {0x0560, 0x0560}, {0x0588, 0x0588},
{0x058B, 0x058C}, {0x0590, 0x0590}, {0x05C8, 0x05CF},
{0x05EB, 0x05EF}, {0x05F5, 0x05FF}, {0x061D, 0x061D},
{0x070E, 0x070E}, {0x074B, 0x074C}, {0x07B2, 0x07BF},
{0x07FB, 0x07FF}, {0x082E, 0x082F}, {0x083F, 0x083F},
{0x085C, 0x085D}, {0x085F, 0x089F}, {0x08B5, 0x08B5},
{0x08BE, 0x08D3}, {0x0984, 0x0984}, {0x098D, 0x098E},
{0x0991, 0x0992}, {0x09A9, 0x09A9}, {0x09B1, 0x09B1},
{0x09B3, 0x09B5}, {0x09BA, 0x09BB}, {0x09C5, 0x09C6},
{0x09C9, 0x09CA}, {0x09CF, 0x09D6}, {0x09D8, 0x09DB},
{0x09DE, 0x09DE}, {0x09E4, 0x09E5}, {0x09FC, 0x0A00},
{0x0A04, 0x0A04}, {0x0A0B, 0x0A0E}, {0x0A11, 0x0A12},
{0x0A29, 0x0A29}, {0x0A31, 0x0A31}, {0x0A34, 0x0A34},
{0x0A37, 0x0A37}, {0x0A3A, 0x0A3B}, {0x0A3D, 0x0A3D},
{0x0A43, 0x0A46}, {0x0A49, 0x0A4A}, {0x0A4E, 0x0A50},
{0x0A52, 0x0A58}, {0x0A5D, 0x0A5D}, {0x0A5F, 0x0A65},
{0x0A76, 0x0A80}, {0x0A84, 0x0A84}, {0x0A8E, 0x0A8E},
{0x0A92, 0x0A92}, {0x0AA9, 0x0AA9}, {0x0AB1, 0x0AB1},
{0x0AB4, 0x0AB4}, {0x0ABA, 0x0ABB}, {0x0AC6, 0x0AC6},
{0x0ACA, 0x0ACA}, {0x0ACE, 0x0ACF}, {0x0AD1, 0x0ADF},
{0x0AE4, 0x0AE5}, {0x0AF2, 0x0AF8}, {0x0AFA, 0x0B00},
{0x0B04, 0x0B04}, {0x0B0D, 0x0B0E}, {0x0B11, 0x0B12},
{0x0B29, 0x0B29}, {0x0B31, 0x0B31}, {0x0B34, 0x0B34},
{0x0B3A, 0x0B3B}, {0x0B45, 0x0B46}, {0x0B49, 0x0B4A},
{0x0B4E, 0x0B55}, {0x0B58, 0x0B5B}, {0x0B5E, 0x0B5E},
{0x0B64, 0x0B65}, {0x0B78, 0x0B81}, {0x0B84, 0x0B84},
{0x0B8B, 0x0B8D}, {0x0B91, 0x0B91}, {0x0B96, 0x0B98},
{0x0B9B, 0x0B9B}, {0x0B9D, 0x0B9D}, {0x0BA0, 0x0BA2},
{0x0BA5, 0x0BA7}, {0x0BAB, 0x0BAD}, {0x0BBA, 0x0BBD},
{0x0BC3, 0x0BC5}, {0x0BC9, 0x0BC9}, {0x0BCE, 0x0BCF},
{0x0BD1, 0x0BD6}, {0x0BD8, 0x0BE5}, {0x0BFB, 0x0BFF},
{0x0C04, 0x0C04}, {0x0C0D, 0x0C0D}, {0x0C11, 0x0C11},
{0x0C29, 0x0C29}, {0x0C3A, 0x0C3C}, {0x0C45, 0x0C45},
{0x0C49, 0x0C49}, {0x0C4E, 0x0C54}, {0x0C57, 0x0C57},
{0x0C5B, 0x0C5F}, {0x0C64, 0x0C65}, {0x0C70, 0x0C77},
{0x0C84, 0x0C84}, {0x0C8D, 0x0C8D}, {0x0C91, 0x0C91},
{0x0CA9, 0x0CA9}, {0x0CB4, 0x0CB4}, {0x0CBA, 0x0CBB},
{0x0CC5, 0x0CC5}, {0x0CC9, 0x0CC9}, {0x0CCE, 0x0CD4},
{0x0CD7, 0x0CDD}, {0x0CDF, 0x0CDF}, {0x0CE4, 0x0CE5},
{0x0CF0, 0x0CF0}, {0x0CF3, 0x0D00}, {0x0D04, 0x0D04},
{0x0D0D, 0x0D0D}, {0x0D11, 0x0D11}, {0x0D3B, 0x0D3C},
{0x0D45, 0x0D45}, {0x0D49, 0x0D49}, {0x0D50, 0x0D53},
{0x0D64, 0x0D65}, {0x0D80, 0x0D81}, {0x0D84, 0x0D84},
{0x0D97, 0x0D99}, {0x0DB2, 0x0DB2}, {0x0DBC, 0x0DBC},
{0x0DBE, 0x0DBF}, {0x0DC7, 0x0DC9}, {0x0DCB, 0x0DCE},
{0x0DD5, 0x0DD5}, {0x0DD7, 0x0DD7}, {0x0DE0, 0x0DE5},
{0x0DF0, 0x0DF1}, {0x0DF5, 0x0E00}, {0x0E3B, 0x0E3E},
{0x0E5C, 0x0E80}, {0x0E83, 0x0E83}, {0x0E85, 0x0E86},
{0x0E89, 0x0E89}, {0x0E8B, 0x0E8C}, {0x0E8E, 0x0E93},
{0x0E98, 0x0E98}, {0x0EA0, 0x0EA0}, {0x0EA4, 0x0EA4},
{0x0EA6, 0x0EA6}, {0x0EA8, 0x0EA9}, {0x0EAC, 0x0EAC},
{0x0EBA, 0x0EBA}, {0x0EBE, 0x0EBF}, {0x0EC5, 0x0EC5},
{0x0EC7, 0x0EC7}, {0x0ECE, 0x0ECF}, {0x0EDA, 0x0EDB},
{0x0EE0, 0x0EFF}, {0x0F48, 0x0F48}, {0x0F6D, 0x0F70},
{0x0F98, 0x0F98}, {0x0FBD, 0x0FBD}, {0x0FCD, 0x0FCD},
{0x0FDB, 0x0FFF}, {0x10C6, 0x10C6}, {0x10C8, 0x10CC},
{0x10CE, 0x10CF}, {0x1249, 0x1249}, {0x124E, 0x124F},
{0x1257, 0x1257}, {0x1259, 0x1259}, {0x125E, 0x125F},
{0x1289, 0x1289}, {0x128E, 0x128F}, {0x12B1, 0x12B1},
{0x12B6, 0x12B7}, {0x12BF, 0x12BF}, {0x12C1, 0x12C1},
{0x12C6, 0x12C7}, {0x12D7, 0x12D7}, {0x1311, 0x1311},
{0x1316, 0x1317}, {0x135B, 0x135C}, {0x137D, 0x137F},
{0x139A, 0x139F}, {0x13F6, 0x13F7}, {0x13FE, 0x13FF},
{0x169D, 0x169F}, {0x16F9, 0x16FF}, {0x170D, 0x170D},
{0x1715, 0x171F}, {0x1737, 0x173F}, {0x1754, 0x175F},
{0x176D, 0x176D}, {0x1771, 0x1771}, {0x1774, 0x177F},
{0x17DE, 0x17DF}, {0x17EA, 0x17EF}, {0x17FA, 0x17FF},
{0x180F, 0x180F}, {0x181A, 0x181F}, {0x1878, 0x187F},
{0x18AB, 0x18AF}, {0x18F6, 0x18FF}, {0x191F, 0x191F},
{0x192C, 0x192F}, {0x193C, 0x193F}, {0x1941, 0x1943},
{0x196E, 0x196F}, {0x1975, 0x197F}, {0x19AC, 0x19AF},
{0x19CA, 0x19CF}, {0x19DB, 0x19DD}, {0x1A1C, 0x1A1D},
{0x1A5F, 0x1A5F}, {0x1A7D, 0x1A7E}, {0x1A8A, 0x1A8F},
{0x1A9A, 0x1A9F}, {0x1AAE, 0x1AAF}, {0x1ABF, 0x1AFF},
{0x1B4C, 0x1B4F}, {0x1B7D, 0x1B7F}, {0x1BF4, 0x1BFB},
{0x1C38, 0x1C3A}, {0x1C4A, 0x1C4C}, {0x1C89, 0x1CBF},
{0x1CC8, 0x1CCF}, {0x1CF7, 0x1CF7}, {0x1CFA, 0x1CFF},
{0x1DF6, 0x1DFA}, {0x1F16, 0x1F17}, {0x1F1E, 0x1F1F},
{0x1F46, 0x1F47}, {0x1F4E, 0x1F4F}, {0x1F58, 0x1F58},
{0x1F5A, 0x1F5A}, {0x1F5C, 0x1F5C}, {0x1F5E, 0x1F5E},
{0x1F7E, 0x1F7F}, {0x1FB5, 0x1FB5}, {0x1FC5, 0x1FC5},
{0x1FD4, 0x1FD5}, {0x1FDC, 0x1FDC}, {0x1FF0, 0x1FF1},
{0x1FF5, 0x1FF5}, {0x1FFF, 0x1FFF}, {0x2065, 0x2065},
{0x2072, 0x2073}, {0x208F, 0x208F}, {0x209D, 0x209F},
{0x20BF, 0x20CF}, {0x20F1, 0x20FF}, {0x218C, 0x218F},
{0x23FF, 0x23FF}, {0x2427, 0x243F}, {0x244B, 0x245F},
{0x2B74, 0x2B75}, {0x2B96, 0x2B97}, {0x2BBA, 0x2BBC},
{0x2BC9, 0x2BC9}, {0x2BD2, 0x2BEB}, {0x2BF0, 0x2BFF},
{0x2C2F, 0x2C2F}, {0x2C5F, 0x2C5F}, {0x2CF4, 0x2CF8},
{0x2D26, 0x2D26}, {0x2D28, 0x2D2C}, {0x2D2E, 0x2D2F},
{0x2D68, 0x2D6E}, {0x2D71, 0x2D7E}, {0x2D97, 0x2D9F},
{0x2DA7, 0x2DA7}, {0x2DAF, 0x2DAF}, {0x2DB7, 0x2DB7},
{0x2DBF, 0x2DBF}, {0x2DC7, 0x2DC7}, {0x2DCF, 0x2DCF},
{0x2DD7, 0x2DD7}, {0x2DDF, 0x2DDF}, {0x2E45, 0x2E7F},
{0x2E9A, 0x2E9A}, {0x2EF4, 0x2EFF}, {0x2FD6, 0x2FEF},
{0x2FFC, 0x2FFF}, {0x3040, 0x3040}, {0x3097, 0x3098},
{0x3100, 0x3104}, {0x312E, 0x3130}, {0x318F, 0x318F},
{0x31BB, 0x31BF}, {0x31E4, 0x31EF}, {0x321F, 0x321F},
{0x32FF, 0x32FF}, {0x4DB6, 0x4DBF}, {0x9FD6, 0x9FFF},
{0xA48D, 0xA48F}, {0xA4C7, 0xA4CF}, {0xA62C, 0xA63F},
{0xA6F8, 0xA6FF}, {0xA7AF, 0xA7AF}, {0xA7B8, 0xA7F6},
{0xA82C, 0xA82F}, {0xA83A, 0xA83F}, {0xA878, 0xA87F},
{0xA8C6, 0xA8CD}, {0xA8DA, 0xA8DF}, {0xA8FE, 0xA8FF},
{0xA954, 0xA95E}, {0xA97D, 0xA97F}, {0xA9CE, 0xA9CE},
{0xA9DA, 0xA9DD}, {0xA9FF, 0xA9FF}, {0xAA37, 0xAA3F},
{0xAA4E, 0xAA4F}, {0xAA5A, 0xAA5B}, {0xAAC3, 0xAADA},
{0xAAF7, 0xAB00}, {0xAB07, 0xAB08}, {0xAB0F, 0xAB10},
{0xAB17, 0xAB1F}, {0xAB27, 0xAB27}, {0xAB2F, 0xAB2F},
{0xAB66, 0xAB6F}, {0xABEE, 0xABEF}, {0xABFA, 0xABFF},
{0xD7A4, 0xD7AF}, {0xD7C7, 0xD7CA}, {0xD7FC, 0xD7FF},
{0xFA6E, 0xFA6F}, {0xFADA, 0xFAFF}, {0xFB07, 0xFB12},
{0xFB18, 0xFB1C}, {0xFB37, 0xFB37}, {0xFB3D, 0xFB3D},
{0xFB3F, 0xFB3F}, {0xFB42, 0xFB42}, {0xFB45, 0xFB45},
{0xFBC2, 0xFBD2}, {0xFD40, 0xFD4F}, {0xFD90, 0xFD91},
{0xFDC8, 0xFDEF}, {0xFDFE, 0xFDFF}, {0xFE1A, 0xFE1F},
{0xFE53, 0xFE53}, {0xFE67, 0xFE67}, {0xFE6C, 0xFE6F},
{0xFE75, 0xFE75}, {0xFEFD, 0xFEFE}, {0xFF00, 0xFF00},
{0xFFBF, 0xFFC1}, {0xFFC8, 0xFFC9}, {0xFFD0, 0xFFD1},
{0xFFD8, 0xFFD9}, {0xFFDD, 0xFFDF}, {0xFFE7, 0xFFE7},
{0xFFEF, 0xFFF8}, {0xFFFE, 0xFFFF}, {0x1000C, 0x1000C},
{0x10027, 0x10027}, {0x1003B, 0x1003B}, {0x1003E, 0x1003E},
{0x1004E, 0x1004F}, {0x1005E, 0x1007F}, {0x100FB, 0x100FF},
{0x10103, 0x10106}, {0x10134, 0x10136}, {0x1018F, 0x1018F},
{0x1019C, 0x1019F}, {0x101A1, 0x101CF}, {0x101FE, 0x1027F},
{0x1029D, 0x1029F}, {0x102D1, 0x102DF}, {0x102FC, 0x102FF},
{0x10324, 0x1032F}, {0x1034B, 0x1034F}, {0x1037B, 0x1037F},
{0x1039E, 0x1039E}, {0x103C4, 0x103C7}, {0x103D6, 0x103FF},
{0x1049E, 0x1049F}, {0x104AA, 0x104AF}, {0x104D4, 0x104D7},
{0x104FC, 0x104FF}, {0x10528, 0x1052F}, {0x10564, 0x1056E},
{0x10570, 0x105FF}, {0x10737, 0x1073F}, {0x10756, 0x1075F},
{0x10768, 0x107FF}, {0x10806, 0x10807}, {0x10809, 0x10809},
{0x10836, 0x10836}, {0x10839, 0x1083B}, {0x1083D, 0x1083E},
{0x10856, 0x10856}, {0x1089F, 0x108A6}, {0x108B0, 0x108DF},
{0x108F3, 0x108F3}, {0x108F6, 0x108FA}, {0x1091C, 0x1091E},
{0x1093A, 0x1093E}, {0x10940, 0x1097F}, {0x109B8, 0x109BB},
{0x109D0, 0x109D1}, {0x10A04, 0x10A04}, {0x10A07, 0x10A0B},
{0x10A14, 0x10A14}, {0x10A18, 0x10A18}, {0x10A34, 0x10A37},
{0x10A3B, 0x10A3E}, {0x10A48, 0x10A4F}, {0x10A59, 0x10A5F},
{0x10AA0, 0x10ABF}, {0x10AE7, 0x10AEA}, {0x10AF7, 0x10AFF},
{0x10B36, 0x10B38}, {0x10B56, 0x10B57}, {0x10B73, 0x10B77},
{0x10B92, 0x10B98}, {0x10B9D, 0x10BA8}, {0x10BB0, 0x10BFF},
{0x10C49, 0x10C7F}, {0x10CB3, 0x10CBF}, {0x10CF3, 0x10CF9},
{0x10D00, 0x10E5F}, {0x10E7F, 0x10FFF}, {0x1104E, 0x11051},
{0x11070, 0x1107E}, {0x110C2, 0x110CF}, {0x110E9, 0x110EF},
{0x110FA, 0x110FF}, {0x11135, 0x11135}, {0x11144, 0x1114F},
{0x11177, 0x1117F}, {0x111CE, 0x111CF}, {0x111E0, 0x111E0},
{0x111F5, 0x111FF}, {0x11212, 0x11212}, {0x1123F, 0x1127F},
{0x11287, 0x11287}, {0x11289, 0x11289}, {0x1128E, 0x1128E},
{0x1129E, 0x1129E}, {0x112AA, 0x112AF}, {0x112EB, 0x112EF},
{0x112FA, 0x112FF}, {0x11304, 0x11304}, {0x1130D, 0x1130E},
{0x11311, 0x11312}, {0x11329, 0x11329}, {0x11331, 0x11331},
{0x11334, 0x11334}, {0x1133A, 0x1133B}, {0x11345, 0x11346},
{0x11349, 0x1134A}, {0x1134E, 0x1134F}, {0x11351, 0x11356},
{0x11358, 0x1135C}, {0x11364, 0x11365}, {0x1136D, 0x1136F},
{0x11375, 0x113FF}, {0x1145A, 0x1145A}, {0x1145C, 0x1145C},
{0x1145E, 0x1147F}, {0x114C8, 0x114CF}, {0x114DA, 0x1157F},
{0x115B6, 0x115B7}, {0x115DE, 0x115FF}, {0x11645, 0x1164F},
{0x1165A, 0x1165F}, {0x1166D, 0x1167F}, {0x116B8, 0x116BF},
{0x116CA, 0x116FF}, {0x1171A, 0x1171C}, {0x1172C, 0x1172F},
{0x11740, 0x1189F}, {0x118F3, 0x118FE}, {0x11900, 0x11ABF},
{0x11AF9, 0x11BFF}, {0x11C09, 0x11C09}, {0x11C37, 0x11C37},
{0x11C46, 0x11C4F}, {0x11C6D, 0x11C6F}, {0x11C90, 0x11C91},
{0x11CA8, 0x11CA8}, {0x11CB7, 0x11FFF}, {0x1239A, 0x123FF},
{0x1246F, 0x1246F}, {0x12475, 0x1247F}, {0x12544, 0x12FFF},
{0x1342F, 0x143FF}, {0x14647, 0x167FF}, {0x16A39, 0x16A3F},
{0x16A5F, 0x16A5F}, {0x16A6A, 0x16A6D}, {0x16A70, 0x16ACF},
{0x16AEE, 0x16AEF}, {0x16AF6, 0x16AFF}, {0x16B46, 0x16B4F},
{0x16B5A, 0x16B5A}, {0x16B62, 0x16B62}, {0x16B78, 0x16B7C},
{0x16B90, 0x16EFF}, {0x16F45, 0x16F4F}, {0x16F7F, 0x16F8E},
{0x16FA0, 0x16FDF}, {0x16FE1, 0x16FFF}, {0x187ED, 0x187FF},
{0x18AF3, 0x1AFFF}, {0x1B002, 0x1BBFF}, {0x1BC6B, 0x1BC6F},
{0x1BC7D, 0x1BC7F}, {0x1BC89, 0x1BC8F}, {0x1BC9A, 0x1BC9B},
{0x1BCA4, 0x1CFFF}, {0x1D0F6, 0x1D0FF}, {0x1D127, 0x1D128},
{0x1D1E9, 0x1D1FF}, {0x1D246, 0x1D2FF}, {0x1D357, 0x1D35F},
{0x1D372, 0x1D3FF}, {0x1D455, 0x1D455}, {0x1D49D, 0x1D49D},
{0x1D4A0, 0x1D4A1}, {0x1D4A3, 0x1D4A4}, {0x1D4A7, 0x1D4A8},
{0x1D4AD, 0x1D4AD}, {0x1D4BA, 0x1D4BA}, {0x1D4BC, 0x1D4BC},
{0x1D4C4, 0x1D4C4}, {0x1D506, 0x1D506}, {0x1D50B, 0x1D50C},
{0x1D515, 0x1D515}, {0x1D51D, 0x1D51D}, {0x1D53A, 0x1D53A},
{0x1D53F, 0x1D53F}, {0x1D545, 0x1D545}, {0x1D547, 0x1D549},
{0x1D551, 0x1D551}, {0x1D6A6, 0x1D6A7}, {0x1D7CC, 0x1D7CD},
{0x1DA8C, 0x1DA9A}, {0x1DAA0, 0x1DAA0}, {0x1DAB0, 0x1DFFF},
{0x1E007, 0x1E007}, {0x1E019, 0x1E01A}, {0x1E022, 0x1E022},
{0x1E025, 0x1E025}, {0x1E02B, 0x1E7FF}, {0x1E8C5, 0x1E8C6},
{0x1E8D7, 0x1E8FF}, {0x1E94B, 0x1E94F}, {0x1E95A, 0x1E95D},
{0x1E960, 0x1EDFF}, {0x1EE04, 0x1EE04}, {0x1EE20, 0x1EE20},
{0x1EE23, 0x1EE23}, {0x1EE25, 0x1EE26}, {0x1EE28, 0x1EE28},
{0x1EE33, 0x1EE33}, {0x1EE38, 0x1EE38}, {0x1EE3A, 0x1EE3A},
{0x1EE3C, 0x1EE41}, {0x1EE43, 0x1EE46}, {0x1EE48, 0x1EE48},
{0x1EE4A, 0x1EE4A}, {0x1EE4C, 0x1EE4C}, {0x1EE50, 0x1EE50},
{0x1EE53, 0x1EE53}, {0x1EE55, 0x1EE56}, {0x1EE58, 0x1EE58},
{0x1EE5A, 0x1EE5A}, {0x1EE5C, 0x1EE5C}, {0x1EE5E, 0x1EE5E},
{0x1EE60, 0x1EE60}, {0x1EE63, 0x1EE63}, {0x1EE65, 0x1EE66},
{0x1EE6B, 0x1EE6B}, {0x1EE73, 0x1EE73}, {0x1EE78, 0x1EE78},
{0x1EE7D, 0x1EE7D}, {0x1EE7F, 0x1EE7F}, {0x1EE8A, 0x1EE8A},
{0x1EE9C, 0x1EEA0}, {0x1EEA4, 0x1EEA4}, {0x1EEAA, 0x1EEAA},
{0x1EEBC, 0x1EEEF}, {0x1EEF2, 0x1EFFF}, {0x1F02C, 0x1F02F},
{0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0},
{0x1F0D0, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F10D, 0x1F10F},
{0x1F12F, 0x1F12F}, {0x1F16C, 0x1F16F}, {0x1F1AD, 0x1F1E5},
{0x1F203, 0x1F20F}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F24F},
{0x1F252, 0x1F2FF}, {0x1F6D3, 0x1F6DF}, {0x1F6ED, 0x1F6EF},
{0x1F6F7, 0x1F6FF}, {0x1F774, 0x1F77F}, {0x1F7D5, 0x1F7FF},
{0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F},
{0x1F888, 0x1F88F}, {0x1F8AE, 0x1F90F}, {0x1F91F, 0x1F91F},
{0x1F928, 0x1F92F}, {0x1F931, 0x1F932}, {0x1F93F, 0x1F93F},
{0x1F94C, 0x1F94F}, {0x1F95F, 0x1F97F}, {0x1F992, 0x1F9BF},
{0x1F9C1, 0x1FFFF}, {0x2A6D7, 0x2A6FF}, {0x2B735, 0x2B73F},
{0x2B81E, 0x2B81F}, {0x2CEA2, 0x2F7FF}, {0x2FA1E, 0xE0000},
{0xE0002, 0xE001F}, {0xE0080, 0xE00FF}, {0xE01F0, 0xEFFFF},
{0xFFFFE, 0xFFFFF},
}
var neutral = table{
{0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9},
{0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB},
{0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6},
{0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7},
{0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1},
{0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD},
{0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112},
{0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A},
{0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E},
{0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C},
{0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A},
{0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1},
{0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7},
{0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250},
{0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6},
{0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF},
{0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE},
{0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F},
{0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390},
{0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400},
{0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F},
{0x0531, 0x0556}, {0x0559, 0x055F}, {0x0561, 0x0587},
{0x0589, 0x058A}, {0x058D, 0x058F}, {0x0591, 0x05C7},
{0x05D0, 0x05EA}, {0x05F0, 0x05F4}, {0x0600, 0x061C},
{0x061E, 0x070D}, {0x070F, 0x074A}, {0x074D, 0x07B1},
{0x07C0, 0x07FA}, {0x0800, 0x082D}, {0x0830, 0x083E},
{0x0840, 0x085B}, {0x085E, 0x085E}, {0x08A0, 0x08B4},
{0x08B6, 0x08BD}, {0x08D4, 0x0983}, {0x0985, 0x098C},
{0x098F, 0x0990}, {0x0993, 0x09A8}, {0x09AA, 0x09B0},
{0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09BC, 0x09C4},
{0x09C7, 0x09C8}, {0x09CB, 0x09CE}, {0x09D7, 0x09D7},
{0x09DC, 0x09DD}, {0x09DF, 0x09E3}, {0x09E6, 0x09FB},
{0x0A01, 0x0A03}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10},
{0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, {0x0A32, 0x0A33},
{0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C},
{0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
{0x0A51, 0x0A51}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E},
{0x0A66, 0x0A75}, {0x0A81, 0x0A83}, {0x0A85, 0x0A8D},
{0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0},
{0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5},
{0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0},
{0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AF9},
{0x0B01, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10},
{0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33},
{0x0B35, 0x0B39}, {0x0B3C, 0x0B44}, {0x0B47, 0x0B48},
{0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, {0x0B5C, 0x0B5D},
{0x0B5F, 0x0B63}, {0x0B66, 0x0B77}, {0x0B82, 0x0B83},
{0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95},
{0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F},
{0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9},
{0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD},
{0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA},
{0x0C00, 0x0C03}, {0x0C05, 0x0C0C}, {0x0C0E, 0x0C10},
{0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, {0x0C3D, 0x0C44},
{0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56},
{0x0C58, 0x0C5A}, {0x0C60, 0x0C63}, {0x0C66, 0x0C6F},
{0x0C78, 0x0C83}, {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90},
{0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9},
{0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD},
{0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE3},
{0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, {0x0D01, 0x0D03},
{0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D3A},
{0x0D3D, 0x0D44}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4F},
{0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, {0x0D82, 0x0D83},
{0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB},
{0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA},
{0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF},
{0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, {0x0E01, 0x0E3A},
{0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84},
{0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, {0x0E8D, 0x0E8D},
{0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, {0x0EA1, 0x0EA3},
{0x0EA5, 0x0EA5}, {0x0EA7, 0x0EA7}, {0x0EAA, 0x0EAB},
{0x0EAD, 0x0EB9}, {0x0EBB, 0x0EBD}, {0x0EC0, 0x0EC4},
{0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9},
{0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C},
{0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC},
{0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7},
{0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248},
{0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258},
{0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D},
{0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE},
{0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6},
{0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A},
{0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5},
{0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8},
{0x1700, 0x170C}, {0x170E, 0x1714}, {0x1720, 0x1736},
{0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770},
{0x1772, 0x1773}, {0x1780, 0x17DD}, {0x17E0, 0x17E9},
{0x17F0, 0x17F9}, {0x1800, 0x180E}, {0x1810, 0x1819},
{0x1820, 0x1877}, {0x1880, 0x18AA}, {0x18B0, 0x18F5},
{0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B},
{0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974},
{0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA},
{0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C},
{0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD},
{0x1AB0, 0x1ABE}, {0x1B00, 0x1B4B}, {0x1B50, 0x1B7C},
{0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49},
{0x1C4D, 0x1C88}, {0x1CC0, 0x1CC7}, {0x1CD0, 0x1CF6},
{0x1CF8, 0x1CF9}, {0x1D00, 0x1DF5}, {0x1DFB, 0x1F15},
{0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D},
{0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B},
{0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4},
{0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB},
{0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE},
{0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017},
{0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023},
{0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034},
{0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064},
{0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080},
{0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8},
{0x20AA, 0x20AB}, {0x20AD, 0x20BE}, {0x20D0, 0x20F0},
{0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108},
{0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120},
{0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152},
{0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F},
{0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7},
{0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6},
{0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206},
{0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210},
{0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C},
{0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226},
{0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B},
{0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251},
{0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269},
{0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285},
{0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4},
{0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319},
{0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF},
{0x23F1, 0x23F2}, {0x23F4, 0x23FE}, {0x2400, 0x2426},
{0x2440, 0x244A}, {0x24EA, 0x24EA}, {0x254C, 0x254F},
{0x2574, 0x257F}, {0x2590, 0x2591}, {0x2596, 0x259F},
{0x25A2, 0x25A2}, {0x25AA, 0x25B1}, {0x25B4, 0x25B5},
{0x25B8, 0x25BB}, {0x25BE, 0x25BF}, {0x25C2, 0x25C5},
{0x25C9, 0x25CA}, {0x25CC, 0x25CD}, {0x25D2, 0x25E1},
{0x25E6, 0x25EE}, {0x25F0, 0x25FC}, {0x25FF, 0x2604},
{0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613},
{0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x263F},
{0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F},
{0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B},
{0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2692},
{0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9},
{0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2},
{0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709},
{0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B},
{0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756},
{0x2758, 0x2775}, {0x2780, 0x2794}, {0x2798, 0x27AF},
{0x27B1, 0x27BE}, {0x27C0, 0x27E5}, {0x27EE, 0x2984},
{0x2987, 0x2B1A}, {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54},
{0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B98, 0x2BB9},
{0x2BBD, 0x2BC8}, {0x2BCA, 0x2BD1}, {0x2BEC, 0x2BEF},
{0x2C00, 0x2C2E}, {0x2C30, 0x2C5E}, {0x2C60, 0x2CF3},
{0x2CF9, 0x2D25}, {0x2D27, 0x2D27}, {0x2D2D, 0x2D2D},
{0x2D30, 0x2D67}, {0x2D6F, 0x2D70}, {0x2D7F, 0x2D96},
{0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6},
{0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE},
{0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2DE0, 0x2E44},
{0x303F, 0x303F}, {0x4DC0, 0x4DFF}, {0xA4D0, 0xA62B},
{0xA640, 0xA6F7}, {0xA700, 0xA7AE}, {0xA7B0, 0xA7B7},
{0xA7F7, 0xA82B}, {0xA830, 0xA839}, {0xA840, 0xA877},
{0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, {0xA8E0, 0xA8FD},
{0xA900, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD},
{0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36},
{0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2},
{0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E},
{0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E},
{0xAB30, 0xAB65}, {0xAB70, 0xABED}, {0xABF0, 0xABF9},
{0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF},
{0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36},
{0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41},
{0xFB43, 0xFB44}, {0xFB46, 0xFBC1}, {0xFBD3, 0xFD3F},
{0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDF0, 0xFDFD},
{0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC},
{0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B},
{0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D},
{0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA},
{0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E},
{0x10190, 0x1019B}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD},
{0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB},
{0x10300, 0x10323}, {0x10330, 0x1034A}, {0x10350, 0x1037A},
{0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5},
{0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3},
{0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563},
{0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755},
{0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808},
{0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C},
{0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF},
{0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x1091B},
{0x1091F, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x109B7},
{0x109BC, 0x109CF}, {0x109D2, 0x10A03}, {0x10A05, 0x10A06},
{0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A33},
{0x10A38, 0x10A3A}, {0x10A3F, 0x10A47}, {0x10A50, 0x10A58},
{0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6},
{0x10B00, 0x10B35}, {0x10B39, 0x10B55}, {0x10B58, 0x10B72},
{0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF},
{0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2},
{0x10CFA, 0x10CFF}, {0x10E60, 0x10E7E}, {0x11000, 0x1104D},
{0x11052, 0x1106F}, {0x1107F, 0x110C1}, {0x110D0, 0x110E8},
{0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11143},
{0x11150, 0x11176}, {0x11180, 0x111CD}, {0x111D0, 0x111DF},
{0x111E1, 0x111F4}, {0x11200, 0x11211}, {0x11213, 0x1123E},
{0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D},
{0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA},
{0x112F0, 0x112F9}, {0x11300, 0x11303}, {0x11305, 0x1130C},
{0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330},
{0x11332, 0x11333}, {0x11335, 0x11339}, {0x1133C, 0x11344},
{0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350},
{0x11357, 0x11357}, {0x1135D, 0x11363}, {0x11366, 0x1136C},
{0x11370, 0x11374}, {0x11400, 0x11459}, {0x1145B, 0x1145B},
{0x1145D, 0x1145D}, {0x11480, 0x114C7}, {0x114D0, 0x114D9},
{0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644},
{0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B7},
{0x116C0, 0x116C9}, {0x11700, 0x11719}, {0x1171D, 0x1172B},
{0x11730, 0x1173F}, {0x118A0, 0x118F2}, {0x118FF, 0x118FF},
{0x11AC0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C36},
{0x11C38, 0x11C45}, {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F},
{0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, {0x12000, 0x12399},
{0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543},
{0x13000, 0x1342E}, {0x14400, 0x14646}, {0x16800, 0x16A38},
{0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16A6F},
{0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, {0x16B00, 0x16B45},
{0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, {0x16B63, 0x16B77},
{0x16B7D, 0x16B8F}, {0x16F00, 0x16F44}, {0x16F50, 0x16F7E},
{0x16F8F, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C},
{0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3},
{0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D1E8},
{0x1D200, 0x1D245}, {0x1D300, 0x1D356}, {0x1D360, 0x1D371},
{0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F},
{0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC},
{0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3},
{0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514},
{0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E},
{0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550},
{0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B},
{0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006},
{0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024},
{0x1E026, 0x1E02A}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6},
{0x1E900, 0x1E94A}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F},
{0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22},
{0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32},
{0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B},
{0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49},
{0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52},
{0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59},
{0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F},
{0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A},
{0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C},
{0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B},
{0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB},
{0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003}, {0x1F005, 0x1F02B},
{0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF},
{0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10C},
{0x1F12E, 0x1F12E}, {0x1F16A, 0x1F16B}, {0x1F1E6, 0x1F1FF},
{0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D},
{0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF},
{0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F},
{0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A},
{0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594},
{0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F},
{0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6E0, 0x1F6EA},
{0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D4},
{0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859},
{0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0xE0001, 0xE0001},
{0xE0020, 0xE007F},
}
// Condition have flag EastAsianWidth whether the current locale is CJK or not.
type Condition struct {
EastAsianWidth bool
ZeroWidthJoiner bool
}
// NewCondition return new instance of Condition which is current locale.
func NewCondition() *Condition {
return &Condition{
EastAsianWidth: EastAsianWidth,
ZeroWidthJoiner: ZeroWidthJoiner,
}
}
// RuneWidth returns the number of cells in r.
// See http://www.unicode.org/reports/tr11/
func (c *Condition) RuneWidth(r rune) int {
switch {
case r < 0 || r > 0x10FFFF ||
inTables(r, nonprint, combining, notassigned):
return 0
case (c.EastAsianWidth && IsAmbiguousWidth(r)) ||
inTables(r, doublewidth, emoji):
return 2
default:
return 1
}
}
func (c *Condition) stringWidth(s string) (width int) {
for _, r := range []rune(s) {
width += c.RuneWidth(r)
}
return width
}
func (c *Condition) stringWidthZeroJoiner(s string) (width int) {
r1, r2 := rune(0), rune(0)
for _, r := range []rune(s) {
if r == 0xFE0E || r == 0xFE0F {
continue
}
w := c.RuneWidth(r)
if r2 == 0x200D && inTables(r, emoji) && inTables(r1, emoji) {
w = 0
}
width += w
r1, r2 = r2, r
}
return width
}
// StringWidth return width as you can see
func (c *Condition) StringWidth(s string) (width int) {
if c.ZeroWidthJoiner {
return c.stringWidthZeroJoiner(s)
}
return c.stringWidth(s)
}
// Truncate return string truncated with w cells
func (c *Condition) Truncate(s string, w int, tail string) string {
if c.StringWidth(s) <= w {
return s
}
r := []rune(s)
tw := c.StringWidth(tail)
w -= tw
width := 0
i := 0
for ; i < len(r); i++ {
cw := c.RuneWidth(r[i])
if width+cw > w {
break
}
width += cw
}
return string(r[0:i]) + tail
}
// Wrap return string wrapped with w cells
func (c *Condition) Wrap(s string, w int) string {
width := 0
out := ""
for _, r := range []rune(s) {
cw := RuneWidth(r)
if r == '\n' {
out += string(r)
width = 0
continue
} else if width+cw > w {
out += "\n"
width = 0
out += string(r)
width += cw
continue
}
out += string(r)
width += cw
}
return out
}
// FillLeft return string filled in left by spaces in w cells
func (c *Condition) FillLeft(s string, w int) string {
width := c.StringWidth(s)
count := w - width
if count > 0 {
b := make([]byte, count)
for i := range b {
b[i] = ' '
}
return string(b) + s
}
return s
}
// FillRight return string filled in left by spaces in w cells
func (c *Condition) FillRight(s string, w int) string {
width := c.StringWidth(s)
count := w - width
if count > 0 {
b := make([]byte, count)
for i := range b {
b[i] = ' '
}
return s + string(b)
}
return s
}
// RuneWidth returns the number of cells in r.
// See http://www.unicode.org/reports/tr11/
func RuneWidth(r rune) int {
return DefaultCondition.RuneWidth(r)
}
// IsAmbiguousWidth returns whether is ambiguous width or not.
func IsAmbiguousWidth(r rune) bool {
return inTables(r, private, ambiguous)
}
// IsNeutralWidth returns whether is neutral width or not.
func IsNeutralWidth(r rune) bool {
return inTable(r, neutral)
}
// StringWidth return width as you can see
func StringWidth(s string) (width int) {
return DefaultCondition.StringWidth(s)
}
// Truncate return string truncated with w cells
func Truncate(s string, w int, tail string) string {
return DefaultCondition.Truncate(s, w, tail)
}
// Wrap return string wrapped with w cells
func Wrap(s string, w int) string {
return DefaultCondition.Wrap(s, w)
}
// FillLeft return string filled in left by spaces in w cells
func FillLeft(s string, w int) string {
return DefaultCondition.FillLeft(s, w)
}
// FillRight return string filled in left by spaces in w cells
func FillRight(s string, w int) string {
return DefaultCondition.FillRight(s, w)
}

@ -0,0 +1,8 @@
// +build appengine
package runewidth
// IsEastAsian return true if the current locale is CJK
func IsEastAsian() bool {
return false
}

@ -0,0 +1,9 @@
// +build js
// +build !appengine
package runewidth
func IsEastAsian() bool {
// TODO: Implement this for the web. Detect east asian in a compatible way, and return true.
return false
}

@ -0,0 +1,79 @@
// +build !windows
// +build !js
// +build !appengine
package runewidth
import (
"os"
"regexp"
"strings"
)
var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
var mblenTable = map[string]int{
"utf-8": 6,
"utf8": 6,
"jis": 8,
"eucjp": 3,
"euckr": 2,
"euccn": 2,
"sjis": 2,
"cp932": 2,
"cp51932": 2,
"cp936": 2,
"cp949": 2,
"cp950": 2,
"big5": 2,
"gbk": 2,
"gb2312": 2,
}
func isEastAsian(locale string) bool {
charset := strings.ToLower(locale)
r := reLoc.FindStringSubmatch(locale)
if len(r) == 2 {
charset = strings.ToLower(r[1])
}
if strings.HasSuffix(charset, "@cjk_narrow") {
return false
}
for pos, b := range []byte(charset) {
if b == '@' {
charset = charset[:pos]
break
}
}
max := 1
if m, ok := mblenTable[charset]; ok {
max = m
}
if max > 1 && (charset[0] != 'u' ||
strings.HasPrefix(locale, "ja") ||
strings.HasPrefix(locale, "ko") ||
strings.HasPrefix(locale, "zh")) {
return true
}
return false
}
// IsEastAsian return true if the current locale is CJK
func IsEastAsian() bool {
locale := os.Getenv("LC_CTYPE")
if locale == "" {
locale = os.Getenv("LANG")
}
// ignore C locale
if locale == "POSIX" || locale == "C" {
return false
}
if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
return false
}
return isEastAsian(locale)
}

@ -0,0 +1,28 @@
// +build windows
// +build !appengine
package runewidth
import (
"syscall"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32")
procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
)
// IsEastAsian return true if the current locale is CJK
func IsEastAsian() bool {
r1, _, _ := procGetConsoleOutputCP.Call()
if r1 == 0 {
return false
}
switch int(r1) {
case 932, 51932, 936, 949, 950:
return true
}
return false
}

@ -0,0 +1,4 @@
# Please keep this file sorted.
Georg Reinke <guelfey@googlemail.com>
nsf <no.smile.face@gmail.com>

@ -0,0 +1,19 @@
Copyright (C) 2012 termbox-go authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -0,0 +1,45 @@
[![GoDoc](https://godoc.org/github.com/nsf/termbox-go?status.svg)](http://godoc.org/github.com/nsf/termbox-go)
## Termbox
Termbox is a library that provides a minimalistic API which allows the programmer to write text-based user interfaces. The library is crossplatform and has both terminal-based implementations on *nix operating systems and a winapi console based implementation for windows operating systems. The basic idea is an abstraction of the greatest common subset of features available on all major terminals and other terminal-like APIs in a minimalistic fashion. Small API means it is easy to implement, test, maintain and learn it, that's what makes the termbox a distinct library in its area.
### Installation
Install and update this go package with `go get -u github.com/nsf/termbox-go`
### Examples
For examples of what can be done take a look at demos in the _demos directory. You can try them with go run: `go run _demos/keyboard.go`
There are also some interesting projects using termbox-go:
- [godit](https://github.com/nsf/godit) is an emacsish lightweight text editor written using termbox.
- [gotetris](https://github.com/jjinux/gotetris) is an implementation of Tetris.
- [sokoban-go](https://github.com/rn2dy/sokoban-go) is an implementation of sokoban game.
- [hecate](https://github.com/evanmiller/hecate) is a hex editor designed by Satan.
- [httopd](https://github.com/verdverm/httopd) is top for httpd logs.
- [mop](https://github.com/mop-tracker/mop) is stock market tracker for hackers.
- [termui](https://github.com/gizak/termui) is a terminal dashboard.
- [termdash](https://github.com/mum4k/termdash) is a terminal dashboard.
- [termloop](https://github.com/JoelOtter/termloop) is a terminal game engine.
- [xterm-color-chart](https://github.com/kutuluk/xterm-color-chart) is a XTerm 256 color chart.
- [gocui](https://github.com/jroimartin/gocui) is a minimalist Go library aimed at creating console user interfaces.
- [dry](https://github.com/moncho/dry) is an interactive cli to manage Docker containers.
- [pxl](https://github.com/ichinaski/pxl) displays images in the terminal.
- [snake-game](https://github.com/DyegoCosta/snake-game) is an implementation of the Snake game.
- [gone](https://github.com/guillaumebreton/gone) is a CLI pomodoro® timer.
- [Spoof.go](https://github.com/sabey/spoofgo) controllable movement spoofing from the cli
- [lf](https://github.com/gokcehan/lf) is a terminal file manager
- [rat](https://github.com/ericfreese/rat) lets you compose shell commands to build terminal applications.
- [httplab](https://github.com/gchaincl/httplab) An interactive web server.
- [tetris](https://github.com/MichaelS11/tetris) Go Tetris with AI option
- [wot](https://github.com/kyu-suke/wot) Wait time during command is completed.
- [2048-go](https://github.com/1984weed/2048-go) is 2048 in Go
- [jv](https://github.com/maxzender/jv) helps you view JSON on the command-line.
- [pinger](https://github.com/hirose31/pinger) helps you to monitor numerous hosts using ICMP ECHO_REQUEST.
- [vixl44](https://github.com/sebashwa/vixl44) lets you create pixel art inside your terminal using vim movements
- [zterm](https://github.com/varunrau/zterm) is a typing game inspired by http://zty.pe/
- [gotypist](https://github.com/pb-/gotypist) is a fun touch-typing tutor following Steve Yegge's method.
- [cointop](https://github.com/miguelmota/cointop) is an interactive terminal based UI application for tracking cryptocurrencies.
- [pexpo](https://github.com/nnao45/pexpo) is a terminal sending ping tool written in Go.
- [jid](https://github.com/simeji/jid) is an interactive JSON drill down tool using filtering queries like jq.
### API reference
[godoc.org/github.com/nsf/termbox-go](http://godoc.org/github.com/nsf/termbox-go)

@ -0,0 +1,497 @@
// +build !windows
package termbox
import "github.com/mattn/go-runewidth"
import "fmt"
import "os"
import "os/signal"
import "syscall"
import "runtime"
import "time"
// public API
// Initializes termbox library. This function should be called before any other functions.
// After successful initialization, the library must be finalized using 'Close' function.
//
// Example usage:
// err := termbox.Init()
// if err != nil {
// panic(err)
// }
// defer termbox.Close()
func Init() error {
var err error
if runtime.GOOS == "openbsd" {
out, err = os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return err
}
in = int(out.Fd())
} else {
out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0)
if err != nil {
return err
}
in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
if err != nil {
return err
}
}
err = setup_term()
if err != nil {
return fmt.Errorf("termbox: error while reading terminfo data: %v", err)
}
signal.Notify(sigwinch, syscall.SIGWINCH)
signal.Notify(sigio, syscall.SIGIO)
_, err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK)
if err != nil {
return err
}
_, err = fcntl(in, syscall.F_SETOWN, syscall.Getpid())
if runtime.GOOS != "darwin" && err != nil {
return err
}
err = tcgetattr(out.Fd(), &orig_tios)
if err != nil {
return err
}
tios := orig_tios
tios.Iflag &^= syscall_IGNBRK | syscall_BRKINT | syscall_PARMRK |
syscall_ISTRIP | syscall_INLCR | syscall_IGNCR |
syscall_ICRNL | syscall_IXON
tios.Lflag &^= syscall_ECHO | syscall_ECHONL | syscall_ICANON |
syscall_ISIG | syscall_IEXTEN
tios.Cflag &^= syscall_CSIZE | syscall_PARENB
tios.Cflag |= syscall_CS8
tios.Cc[syscall_VMIN] = 1
tios.Cc[syscall_VTIME] = 0
err = tcsetattr(out.Fd(), &tios)
if err != nil {
return err
}
out.WriteString(funcs[t_enter_ca])
out.WriteString(funcs[t_enter_keypad])
out.WriteString(funcs[t_hide_cursor])
out.WriteString(funcs[t_clear_screen])
termw, termh = get_term_size(out.Fd())
back_buffer.init(termw, termh)
front_buffer.init(termw, termh)
back_buffer.clear()
front_buffer.clear()
go func() {
buf := make([]byte, 128)
for {
select {
case <-sigio:
for {
n, err := syscall.Read(in, buf)
if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
break
}
select {
case input_comm <- input_event{buf[:n], err}:
ie := <-input_comm
buf = ie.data[:128]
case <-quit:
return
}
}
case <-quit:
return
}
}
}()
IsInit = true
return nil
}
// Interrupt an in-progress call to PollEvent by causing it to return
// EventInterrupt. Note that this function will block until the PollEvent
// function has successfully been interrupted.
func Interrupt() {
interrupt_comm <- struct{}{}
}
// Finalizes termbox library, should be called after successful initialization
// when termbox's functionality isn't required anymore.
func Close() {
quit <- 1
out.WriteString(funcs[t_show_cursor])
out.WriteString(funcs[t_sgr0])
out.WriteString(funcs[t_clear_screen])
out.WriteString(funcs[t_exit_ca])
out.WriteString(funcs[t_exit_keypad])
out.WriteString(funcs[t_exit_mouse])
tcsetattr(out.Fd(), &orig_tios)
out.Close()
syscall.Close(in)
// reset the state, so that on next Init() it will work again
termw = 0
termh = 0
input_mode = InputEsc
out = nil
in = 0
lastfg = attr_invalid
lastbg = attr_invalid
lastx = coord_invalid
lasty = coord_invalid
cursor_x = cursor_hidden
cursor_y = cursor_hidden
foreground = ColorDefault
background = ColorDefault
IsInit = false
}
// Synchronizes the internal back buffer with the terminal.
func Flush() error {
// invalidate cursor position
lastx = coord_invalid
lasty = coord_invalid
update_size_maybe()
for y := 0; y < front_buffer.height; y++ {
line_offset := y * front_buffer.width
for x := 0; x < front_buffer.width; {
cell_offset := line_offset + x
back := &back_buffer.cells[cell_offset]
front := &front_buffer.cells[cell_offset]
if back.Ch < ' ' {
back.Ch = ' '
}
w := runewidth.RuneWidth(back.Ch)
if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) {
w = 1
}
if *back == *front {
x += w
continue
}
*front = *back
send_attr(back.Fg, back.Bg)
if w == 2 && x == front_buffer.width-1 {
// there's not enough space for 2-cells rune,
// let's just put a space in there
send_char(x, y, ' ')
} else {
send_char(x, y, back.Ch)
if w == 2 {
next := cell_offset + 1
front_buffer.cells[next] = Cell{
Ch: 0,
Fg: back.Fg,
Bg: back.Bg,
}
}
}
x += w
}
}
if !is_cursor_hidden(cursor_x, cursor_y) {
write_cursor(cursor_x, cursor_y)
}
return flush()
}
// Sets the position of the cursor. See also HideCursor().
func SetCursor(x, y int) {
if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
outbuf.WriteString(funcs[t_show_cursor])
}
if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
outbuf.WriteString(funcs[t_hide_cursor])
}
cursor_x, cursor_y = x, y
if !is_cursor_hidden(cursor_x, cursor_y) {
write_cursor(cursor_x, cursor_y)
}
}
// The shortcut for SetCursor(-1, -1).
func HideCursor() {
SetCursor(cursor_hidden, cursor_hidden)
}
// Changes cell's parameters in the internal back buffer at the specified
// position.
func SetCell(x, y int, ch rune, fg, bg Attribute) {
if x < 0 || x >= back_buffer.width {
return
}
if y < 0 || y >= back_buffer.height {
return
}
back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
}
// Returns a slice into the termbox's back buffer. You can get its dimensions
// using 'Size' function. The slice remains valid as long as no 'Clear' or
// 'Flush' function calls were made after call to this function.
func CellBuffer() []Cell {
return back_buffer.cells
}
// After getting a raw event from PollRawEvent function call, you can parse it
// again into an ordinary one using termbox logic. That is parse an event as
// termbox would do it. Returned event in addition to usual Event struct fields
// sets N field to the amount of bytes used within 'data' slice. If the length
// of 'data' slice is zero or event cannot be parsed for some other reason, the
// function will return a special event type: EventNone.
//
// IMPORTANT: EventNone may contain a non-zero N, which means you should skip
// these bytes, because termbox cannot recognize them.
//
// NOTE: This API is experimental and may change in future.
func ParseEvent(data []byte) Event {
event := Event{Type: EventKey}
status := extract_event(data, &event, false)
if status != event_extracted {
return Event{Type: EventNone, N: event.N}
}
return event
}
// Wait for an event and return it. This is a blocking function call. Instead
// of EventKey and EventMouse it returns EventRaw events. Raw event is written
// into `data` slice and Event's N field is set to the amount of bytes written.
// The minimum required length of the 'data' slice is 1. This requirement may
// vary on different platforms.
//
// NOTE: This API is experimental and may change in future.
func PollRawEvent(data []byte) Event {
if len(data) == 0 {
panic("len(data) >= 1 is a requirement")
}
var event Event
if extract_raw_event(data, &event) {
return event
}
for {
select {
case ev := <-input_comm:
if ev.err != nil {
return Event{Type: EventError, Err: ev.err}
}
inbuf = append(inbuf, ev.data...)
input_comm <- ev
if extract_raw_event(data, &event) {
return event
}
case <-interrupt_comm:
event.Type = EventInterrupt
return event
case <-sigwinch:
event.Type = EventResize
event.Width, event.Height = get_term_size(out.Fd())
return event
}
}
}
// Wait for an event and return it. This is a blocking function call.
func PollEvent() Event {
// Constant governing macOS specific behavior. See https://github.com/nsf/termbox-go/issues/132
// This is an arbitrary delay which hopefully will be enough time for any lagging
// partial escape sequences to come through.
const esc_wait_delay = 100 * time.Millisecond
var event Event
var esc_wait_timer *time.Timer
var esc_timeout <-chan time.Time
// try to extract event from input buffer, return on success
event.Type = EventKey
status := extract_event(inbuf, &event, true)
if event.N != 0 {
copy(inbuf, inbuf[event.N:])
inbuf = inbuf[:len(inbuf)-event.N]
}
if status == event_extracted {
return event
} else if status == esc_wait {
esc_wait_timer = time.NewTimer(esc_wait_delay)
esc_timeout = esc_wait_timer.C
}
for {
select {
case ev := <-input_comm:
if esc_wait_timer != nil {
if !esc_wait_timer.Stop() {
<-esc_wait_timer.C
}
esc_wait_timer = nil
}
if ev.err != nil {
return Event{Type: EventError, Err: ev.err}
}
inbuf = append(inbuf, ev.data...)
input_comm <- ev
status := extract_event(inbuf, &event, true)
if event.N != 0 {
copy(inbuf, inbuf[event.N:])
inbuf = inbuf[:len(inbuf)-event.N]
}
if status == event_extracted {
return event
} else if status == esc_wait {
esc_wait_timer = time.NewTimer(esc_wait_delay)
esc_timeout = esc_wait_timer.C
}
case <-esc_timeout:
esc_wait_timer = nil
status := extract_event(inbuf, &event, false)
if event.N != 0 {
copy(inbuf, inbuf[event.N:])
inbuf = inbuf[:len(inbuf)-event.N]
}
if status == event_extracted {
return event
}
case <-interrupt_comm:
event.Type = EventInterrupt
return event
case <-sigwinch:
event.Type = EventResize
event.Width, event.Height = get_term_size(out.Fd())
return event
}
}
}
// Returns the size of the internal back buffer (which is mostly the same as
// terminal's window size in characters). But it doesn't always match the size
// of the terminal window, after the terminal size has changed, the internal
// back buffer will get in sync only after Clear or Flush function calls.
func Size() (width int, height int) {
return termw, termh
}
// Clears the internal back buffer.
func Clear(fg, bg Attribute) error {
foreground, background = fg, bg
err := update_size_maybe()
back_buffer.clear()
return err
}
// Sets termbox input mode. Termbox has two input modes:
//
// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC means KeyEsc. This is the default input mode.
//
// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
//
// Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will
// enable mouse button press/release and drag events.
//
// If 'mode' is InputCurrent, returns the current input mode. See also Input*
// constants.
func SetInputMode(mode InputMode) InputMode {
if mode == InputCurrent {
return input_mode
}
if mode&(InputEsc|InputAlt) == 0 {
mode |= InputEsc
}
if mode&(InputEsc|InputAlt) == InputEsc|InputAlt {
mode &^= InputAlt
}
if mode&InputMouse != 0 {
out.WriteString(funcs[t_enter_mouse])
} else {
out.WriteString(funcs[t_exit_mouse])
}
input_mode = mode
return input_mode
}
// Sets the termbox output mode. Termbox has four output options:
//
// 1. OutputNormal => [1..8]
// This mode provides 8 different colors:
// black, red, green, yellow, blue, magenta, cyan, white
// Shortcut: ColorBlack, ColorRed, ...
// Attributes: AttrBold, AttrUnderline, AttrReverse
//
// Example usage:
// SetCell(x, y, '@', ColorBlack | AttrBold, ColorRed);
//
// 2. Output256 => [1..256]
// In this mode you can leverage the 256 terminal mode:
// 0x01 - 0x08: the 8 colors as in OutputNormal
// 0x09 - 0x10: Color* | AttrBold
// 0x11 - 0xe8: 216 different colors
// 0xe9 - 0x1ff: 24 different shades of grey
//
// Example usage:
// SetCell(x, y, '@', 184, 240);
// SetCell(x, y, '@', 0xb8, 0xf0);
//
// 3. Output216 => [1..216]
// This mode supports the 3rd range of the 256 mode only.
// But you don't need to provide an offset.
//
// 4. OutputGrayscale => [1..26]
// This mode supports the 4th range of the 256 mode
// and black and white colors from 3th range of the 256 mode
// But you don't need to provide an offset.
//
// In all modes, 0x00 represents the default color.
//
// `go run _demos/output.go` to see its impact on your terminal.
//
// If 'mode' is OutputCurrent, it returns the current output mode.
//
// Note that this may return a different OutputMode than the one requested,
// as the requested mode may not be available on the target platform.
func SetOutputMode(mode OutputMode) OutputMode {
if mode == OutputCurrent {
return output_mode
}
output_mode = mode
return output_mode
}
// Sync comes handy when something causes desync between termbox's understanding
// of a terminal buffer and the reality. Such as a third party process. Sync
// forces a complete resync between the termbox and a terminal, it may not be
// visually pretty though.
func Sync() error {
front_buffer.clear()
err := send_clear()
if err != nil {
return err
}
return Flush()
}

@ -0,0 +1,187 @@
// termbox is a library for creating cross-platform text-based interfaces
package termbox
// public API, common OS agnostic part
type (
InputMode int
OutputMode int
EventType uint8
Modifier uint8
Key uint16
Attribute uint16
)
// This type represents a termbox event. The 'Mod', 'Key' and 'Ch' fields are
// valid if 'Type' is EventKey. The 'Width' and 'Height' fields are valid if
// 'Type' is EventResize. The 'Err' field is valid if 'Type' is EventError.
type Event struct {
Type EventType // one of Event* constants
Mod Modifier // one of Mod* constants or 0
Key Key // one of Key* constants, invalid if 'Ch' is not 0
Ch rune // a unicode character
Width int // width of the screen
Height int // height of the screen
Err error // error in case if input failed
MouseX int // x coord of mouse
MouseY int // y coord of mouse
N int // number of bytes written when getting a raw event
}
// A cell, single conceptual entity on the screen. The screen is basically a 2d
// array of cells. 'Ch' is a unicode character, 'Fg' and 'Bg' are foreground
// and background attributes respectively.
type Cell struct {
Ch rune
Fg Attribute
Bg Attribute
}
// To know if termbox has been initialized or not
var (
IsInit bool = false
)
// Key constants, see Event.Key field.
const (
KeyF1 Key = 0xFFFF - iota
KeyF2
KeyF3
KeyF4
KeyF5
KeyF6
KeyF7
KeyF8
KeyF9
KeyF10
KeyF11
KeyF12
KeyInsert
KeyDelete
KeyHome
KeyEnd
KeyPgup
KeyPgdn
KeyArrowUp
KeyArrowDown
KeyArrowLeft
KeyArrowRight
key_min // see terminfo
MouseLeft
MouseMiddle
MouseRight
MouseRelease
MouseWheelUp
MouseWheelDown
)
const (
KeyCtrlTilde Key = 0x00
KeyCtrl2 Key = 0x00
KeyCtrlSpace Key = 0x00
KeyCtrlA Key = 0x01
KeyCtrlB Key = 0x02
KeyCtrlC Key = 0x03
KeyCtrlD Key = 0x04
KeyCtrlE Key = 0x05
KeyCtrlF Key = 0x06
KeyCtrlG Key = 0x07
KeyBackspace Key = 0x08
KeyCtrlH Key = 0x08
KeyTab Key = 0x09
KeyCtrlI Key = 0x09
KeyCtrlJ Key = 0x0A
KeyCtrlK Key = 0x0B
KeyCtrlL Key = 0x0C
KeyEnter Key = 0x0D
KeyCtrlM Key = 0x0D
KeyCtrlN Key = 0x0E
KeyCtrlO Key = 0x0F
KeyCtrlP Key = 0x10
KeyCtrlQ Key = 0x11
KeyCtrlR Key = 0x12
KeyCtrlS Key = 0x13
KeyCtrlT Key = 0x14
KeyCtrlU Key = 0x15
KeyCtrlV Key = 0x16
KeyCtrlW Key = 0x17
KeyCtrlX Key = 0x18
KeyCtrlY Key = 0x19
KeyCtrlZ Key = 0x1A
KeyEsc Key = 0x1B
KeyCtrlLsqBracket Key = 0x1B
KeyCtrl3 Key = 0x1B
KeyCtrl4 Key = 0x1C
KeyCtrlBackslash Key = 0x1C
KeyCtrl5 Key = 0x1D
KeyCtrlRsqBracket Key = 0x1D
KeyCtrl6 Key = 0x1E
KeyCtrl7 Key = 0x1F
KeyCtrlSlash Key = 0x1F
KeyCtrlUnderscore Key = 0x1F
KeySpace Key = 0x20
KeyBackspace2 Key = 0x7F
KeyCtrl8 Key = 0x7F
)
// Alt modifier constant, see Event.Mod field and SetInputMode function.
const (
ModAlt Modifier = 1 << iota
ModMotion
)
// Cell colors, you can combine a color with multiple attributes using bitwise
// OR ('|').
const (
ColorDefault Attribute = iota
ColorBlack
ColorRed
ColorGreen
ColorYellow
ColorBlue
ColorMagenta
ColorCyan
ColorWhite
)
// Cell attributes, it is possible to use multiple attributes by combining them
// using bitwise OR ('|'). Although, colors cannot be combined. But you can
// combine attributes and a single color.
//
// It's worth mentioning that some platforms don't support certain attributes.
// For example windows console doesn't support AttrUnderline. And on some
// terminals applying AttrBold to background may result in blinking text. Use
// them with caution and test your code on various terminals.
const (
AttrBold Attribute = 1 << (iota + 9)
AttrUnderline
AttrReverse
)
// Input mode. See SetInputMode function.
const (
InputEsc InputMode = 1 << iota
InputAlt
InputMouse
InputCurrent InputMode = 0
)
// Output mode. See SetOutputMode function.
const (
OutputCurrent OutputMode = iota
OutputNormal
Output256
Output216
OutputGrayscale
)
// Event type. See Event.Type field.
const (
EventKey EventType = iota
EventResize
EventMouse
EventError
EventInterrupt
EventRaw
EventNone
)

@ -0,0 +1,245 @@
package termbox
import (
"syscall"
)
// public API
// Initializes termbox library. This function should be called before any other functions.
// After successful initialization, the library must be finalized using 'Close' function.
//
// Example usage:
// err := termbox.Init()
// if err != nil {
// panic(err)
// }
// defer termbox.Close()
func Init() error {
var err error
interrupt, err = create_event()
if err != nil {
return err
}
in, err = syscall.Open("CONIN$", syscall.O_RDWR, 0)
if err != nil {
return err
}
out, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0)
if err != nil {
return err
}
err = get_console_mode(in, &orig_mode)
if err != nil {
return err
}
err = set_console_mode(in, enable_window_input)
if err != nil {
return err
}
orig_size, orig_window = get_term_size(out)
win_size := get_win_size(out)
err = set_console_screen_buffer_size(out, win_size)
if err != nil {
return err
}
err = fix_win_size(out, win_size)
if err != nil {
return err
}
err = get_console_cursor_info(out, &orig_cursor_info)
if err != nil {
return err
}
show_cursor(false)
term_size, _ = get_term_size(out)
back_buffer.init(int(term_size.x), int(term_size.y))
front_buffer.init(int(term_size.x), int(term_size.y))
back_buffer.clear()
front_buffer.clear()
clear()
diffbuf = make([]diff_msg, 0, 32)
go input_event_producer()
IsInit = true
return nil
}
// Finalizes termbox library, should be called after successful initialization
// when termbox's functionality isn't required anymore.
func Close() {
// we ignore errors here, because we can't really do anything about them
Clear(0, 0)
Flush()
// stop event producer
cancel_comm <- true
set_event(interrupt)
select {
case <-input_comm:
default:
}
<-cancel_done_comm
set_console_screen_buffer_size(out, orig_size)
set_console_window_info(out, &orig_window)
set_console_cursor_info(out, &orig_cursor_info)
set_console_cursor_position(out, coord{})
set_console_mode(in, orig_mode)
syscall.Close(in)
syscall.Close(out)
syscall.Close(interrupt)
IsInit = false
}
// Interrupt an in-progress call to PollEvent by causing it to return
// EventInterrupt. Note that this function will block until the PollEvent
// function has successfully been interrupted.
func Interrupt() {
interrupt_comm <- struct{}{}
}
// Synchronizes the internal back buffer with the terminal.
func Flush() error {
update_size_maybe()
prepare_diff_messages()
for _, diff := range diffbuf {
r := small_rect{
left: 0,
top: diff.pos,
right: term_size.x - 1,
bottom: diff.pos + diff.lines - 1,
}
write_console_output(out, diff.chars, r)
}
if !is_cursor_hidden(cursor_x, cursor_y) {
move_cursor(cursor_x, cursor_y)
}
return nil
}
// Sets the position of the cursor. See also HideCursor().
func SetCursor(x, y int) {
if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
show_cursor(true)
}
if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
show_cursor(false)
}
cursor_x, cursor_y = x, y
if !is_cursor_hidden(cursor_x, cursor_y) {
move_cursor(cursor_x, cursor_y)
}
}
// The shortcut for SetCursor(-1, -1).
func HideCursor() {
SetCursor(cursor_hidden, cursor_hidden)
}
// Changes cell's parameters in the internal back buffer at the specified
// position.
func SetCell(x, y int, ch rune, fg, bg Attribute) {
if x < 0 || x >= back_buffer.width {
return
}
if y < 0 || y >= back_buffer.height {
return
}
back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
}
// Returns a slice into the termbox's back buffer. You can get its dimensions
// using 'Size' function. The slice remains valid as long as no 'Clear' or
// 'Flush' function calls were made after call to this function.
func CellBuffer() []Cell {
return back_buffer.cells
}
// Wait for an event and return it. This is a blocking function call.
func PollEvent() Event {
select {
case ev := <-input_comm:
return ev
case <-interrupt_comm:
return Event{Type: EventInterrupt}
}
}
// Returns the size of the internal back buffer (which is mostly the same as
// console's window size in characters). But it doesn't always match the size
// of the console window, after the console size has changed, the internal back
// buffer will get in sync only after Clear or Flush function calls.
func Size() (int, int) {
return int(term_size.x), int(term_size.y)
}
// Clears the internal back buffer.
func Clear(fg, bg Attribute) error {
foreground, background = fg, bg
update_size_maybe()
back_buffer.clear()
return nil
}
// Sets termbox input mode. Termbox has two input modes:
//
// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC means KeyEsc. This is the default input mode.
//
// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
//
// Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will
// enable mouse button press/release and drag events.
//
// If 'mode' is InputCurrent, returns the current input mode. See also Input*
// constants.
func SetInputMode(mode InputMode) InputMode {
if mode == InputCurrent {
return input_mode
}
if mode&InputMouse != 0 {
err := set_console_mode(in, enable_window_input|enable_mouse_input|enable_extended_flags)
if err != nil {
panic(err)
}
} else {
err := set_console_mode(in, enable_window_input)
if err != nil {
panic(err)
}
}
input_mode = mode
return input_mode
}
// Sets the termbox output mode.
//
// Windows console does not support extra colour modes,
// so this will always set and return OutputNormal.
func SetOutputMode(mode OutputMode) OutputMode {
return OutputNormal
}
// Sync comes handy when something causes desync between termbox's understanding
// of a terminal buffer and the reality. Such as a third party process. Sync
// forces a complete resync between the termbox and a terminal, it may not be
// visually pretty though. At the moment on Windows it does nothing.
func Sync() error {
return nil
}

@ -0,0 +1,110 @@
#!/usr/bin/env python
import sys, os, subprocess
def escaped(s):
return repr(s)[1:-1]
def tput(term, name):
try:
return subprocess.check_output(['tput', '-T%s' % term, name]).decode()
except subprocess.CalledProcessError as e:
return e.output.decode()
def w(s):
if s == None:
return
sys.stdout.write(s)
terminals = {
'xterm' : 'xterm',
'rxvt-256color' : 'rxvt_256color',
'rxvt-unicode' : 'rxvt_unicode',
'linux' : 'linux',
'Eterm' : 'eterm',
'screen' : 'screen'
}
keys = [
"F1", "kf1",
"F2", "kf2",
"F3", "kf3",
"F4", "kf4",
"F5", "kf5",
"F6", "kf6",
"F7", "kf7",
"F8", "kf8",
"F9", "kf9",
"F10", "kf10",
"F11", "kf11",
"F12", "kf12",
"INSERT", "kich1",
"DELETE", "kdch1",
"HOME", "khome",
"END", "kend",
"PGUP", "kpp",
"PGDN", "knp",
"KEY_UP", "kcuu1",
"KEY_DOWN", "kcud1",
"KEY_LEFT", "kcub1",
"KEY_RIGHT", "kcuf1"
]
funcs = [
"T_ENTER_CA", "smcup",
"T_EXIT_CA", "rmcup",
"T_SHOW_CURSOR", "cnorm",
"T_HIDE_CURSOR", "civis",
"T_CLEAR_SCREEN", "clear",
"T_SGR0", "sgr0",
"T_UNDERLINE", "smul",
"T_BOLD", "bold",
"T_BLINK", "blink",
"T_REVERSE", "rev",
"T_ENTER_KEYPAD", "smkx",
"T_EXIT_KEYPAD", "rmkx"
]
def iter_pairs(iterable):
iterable = iter(iterable)
while True:
yield (next(iterable), next(iterable))
def do_term(term, nick):
w("// %s\n" % term)
w("var %s_keys = []string{\n\t" % nick)
for k, v in iter_pairs(keys):
w('"')
w(escaped(tput(term, v)))
w('",')
w("\n}\n")
w("var %s_funcs = []string{\n\t" % nick)
for k,v in iter_pairs(funcs):
w('"')
if v == "sgr":
w("\\033[3%d;4%dm")
elif v == "cup":
w("\\033[%d;%dH")
else:
w(escaped(tput(term, v)))
w('", ')
w("\n}\n\n")
def do_terms(d):
w("var terms = []struct {\n")
w("\tname string\n")
w("\tkeys []string\n")
w("\tfuncs []string\n")
w("}{\n")
for k, v in d.items():
w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v))
w("}\n\n")
w("// +build !windows\n\npackage termbox\n\n")
for k,v in terminals.items():
do_term(k, v)
do_terms(terminals)

@ -0,0 +1,11 @@
// +build !darwin
package termbox
// On all systems other than macOS, disable behavior which will wait before
// deciding that the escape key was pressed, to account for partially send
// escape sequences, especially with regard to lengthy mouse sequences.
// See https://github.com/nsf/termbox-go/issues/132
func enable_wait_for_escape_sequence() bool {
return false
}

@ -0,0 +1,9 @@
package termbox
// On macOS, enable behavior which will wait before deciding that the escape
// key was pressed, to account for partially send escape sequences, especially
// with regard to lengthy mouse sequences.
// See https://github.com/nsf/termbox-go/issues/132
func enable_wait_for_escape_sequence() bool {
return true
}

@ -0,0 +1,39 @@
// +build ignore
package termbox
/*
#include <termios.h>
#include <sys/ioctl.h>
*/
import "C"
type syscall_Termios C.struct_termios
const (
syscall_IGNBRK = C.IGNBRK
syscall_BRKINT = C.BRKINT
syscall_PARMRK = C.PARMRK
syscall_ISTRIP = C.ISTRIP
syscall_INLCR = C.INLCR
syscall_IGNCR = C.IGNCR
syscall_ICRNL = C.ICRNL
syscall_IXON = C.IXON
syscall_OPOST = C.OPOST
syscall_ECHO = C.ECHO
syscall_ECHONL = C.ECHONL
syscall_ICANON = C.ICANON
syscall_ISIG = C.ISIG
syscall_IEXTEN = C.IEXTEN
syscall_CSIZE = C.CSIZE
syscall_PARENB = C.PARENB
syscall_CS8 = C.CS8
syscall_VMIN = C.VMIN
syscall_VTIME = C.VTIME
// on darwin change these to (on *bsd too?):
// C.TIOCGETA
// C.TIOCSETA
syscall_TCGETS = C.TCGETS
syscall_TCSETS = C.TCSETS
)

@ -0,0 +1,41 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs syscalls.go
// +build !amd64
package termbox
type syscall_Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]uint8
Ispeed uint32
Ospeed uint32
}
const (
syscall_IGNBRK = 0x1
syscall_BRKINT = 0x2
syscall_PARMRK = 0x8
syscall_ISTRIP = 0x20
syscall_INLCR = 0x40
syscall_IGNCR = 0x80
syscall_ICRNL = 0x100
syscall_IXON = 0x200
syscall_OPOST = 0x1
syscall_ECHO = 0x8
syscall_ECHONL = 0x10
syscall_ICANON = 0x100
syscall_ISIG = 0x80
syscall_IEXTEN = 0x400
syscall_CSIZE = 0x300
syscall_PARENB = 0x1000
syscall_CS8 = 0x300
syscall_VMIN = 0x10
syscall_VTIME = 0x11
syscall_TCGETS = 0x402c7413
syscall_TCSETS = 0x802c7414
)

@ -0,0 +1,40 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs syscalls.go
package termbox
type syscall_Termios struct {
Iflag uint64
Oflag uint64
Cflag uint64
Lflag uint64
Cc [20]uint8
Pad_cgo_0 [4]byte
Ispeed uint64
Ospeed uint64
}
const (
syscall_IGNBRK = 0x1
syscall_BRKINT = 0x2
syscall_PARMRK = 0x8
syscall_ISTRIP = 0x20
syscall_INLCR = 0x40
syscall_IGNCR = 0x80
syscall_ICRNL = 0x100
syscall_IXON = 0x200
syscall_OPOST = 0x1
syscall_ECHO = 0x8
syscall_ECHONL = 0x10
syscall_ICANON = 0x100
syscall_ISIG = 0x80
syscall_IEXTEN = 0x400
syscall_CSIZE = 0x300
syscall_PARENB = 0x1000
syscall_CS8 = 0x300
syscall_VMIN = 0x10
syscall_VTIME = 0x11
syscall_TCGETS = 0x40487413
syscall_TCSETS = 0x80487414
)

@ -0,0 +1,39 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs syscalls.go
package termbox
type syscall_Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]uint8
Ispeed uint32
Ospeed uint32
}
const (
syscall_IGNBRK = 0x1
syscall_BRKINT = 0x2
syscall_PARMRK = 0x8
syscall_ISTRIP = 0x20
syscall_INLCR = 0x40
syscall_IGNCR = 0x80
syscall_ICRNL = 0x100
syscall_IXON = 0x200
syscall_OPOST = 0x1
syscall_ECHO = 0x8
syscall_ECHONL = 0x10
syscall_ICANON = 0x100
syscall_ISIG = 0x80
syscall_IEXTEN = 0x400
syscall_CSIZE = 0x300
syscall_PARENB = 0x1000
syscall_CS8 = 0x300
syscall_VMIN = 0x10
syscall_VTIME = 0x11
syscall_TCGETS = 0x402c7413
syscall_TCSETS = 0x802c7414
)

@ -0,0 +1,39 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs syscalls.go
package termbox
type syscall_Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]uint8
Ispeed uint32
Ospeed uint32
}
const (
syscall_IGNBRK = 0x1
syscall_BRKINT = 0x2
syscall_PARMRK = 0x8
syscall_ISTRIP = 0x20
syscall_INLCR = 0x40
syscall_IGNCR = 0x80
syscall_ICRNL = 0x100
syscall_IXON = 0x200
syscall_OPOST = 0x1
syscall_ECHO = 0x8
syscall_ECHONL = 0x10
syscall_ICANON = 0x100
syscall_ISIG = 0x80
syscall_IEXTEN = 0x400
syscall_CSIZE = 0x300
syscall_PARENB = 0x1000
syscall_CS8 = 0x300
syscall_VMIN = 0x10
syscall_VTIME = 0x11
syscall_TCGETS = 0x402c7413
syscall_TCSETS = 0x802c7414
)

@ -0,0 +1,33 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs syscalls.go
package termbox
import "syscall"
type syscall_Termios syscall.Termios
const (
syscall_IGNBRK = syscall.IGNBRK
syscall_BRKINT = syscall.BRKINT
syscall_PARMRK = syscall.PARMRK
syscall_ISTRIP = syscall.ISTRIP
syscall_INLCR = syscall.INLCR
syscall_IGNCR = syscall.IGNCR
syscall_ICRNL = syscall.ICRNL
syscall_IXON = syscall.IXON
syscall_OPOST = syscall.OPOST
syscall_ECHO = syscall.ECHO
syscall_ECHONL = syscall.ECHONL
syscall_ICANON = syscall.ICANON
syscall_ISIG = syscall.ISIG
syscall_IEXTEN = syscall.IEXTEN
syscall_CSIZE = syscall.CSIZE
syscall_PARENB = syscall.PARENB
syscall_CS8 = syscall.CS8
syscall_VMIN = syscall.VMIN
syscall_VTIME = syscall.VTIME
syscall_TCGETS = syscall.TCGETS
syscall_TCSETS = syscall.TCSETS
)

@ -0,0 +1,39 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs syscalls.go
package termbox
type syscall_Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]uint8
Ispeed int32
Ospeed int32
}
const (
syscall_IGNBRK = 0x1
syscall_BRKINT = 0x2
syscall_PARMRK = 0x8
syscall_ISTRIP = 0x20
syscall_INLCR = 0x40
syscall_IGNCR = 0x80
syscall_ICRNL = 0x100
syscall_IXON = 0x200
syscall_OPOST = 0x1
syscall_ECHO = 0x8
syscall_ECHONL = 0x10
syscall_ICANON = 0x100
syscall_ISIG = 0x80
syscall_IEXTEN = 0x400
syscall_CSIZE = 0x300
syscall_PARENB = 0x1000
syscall_CS8 = 0x300
syscall_VMIN = 0x10
syscall_VTIME = 0x11
syscall_TCGETS = 0x402c7413
syscall_TCSETS = 0x802c7414
)

@ -0,0 +1,39 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs syscalls.go
package termbox
type syscall_Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]uint8
Ispeed int32
Ospeed int32
}
const (
syscall_IGNBRK = 0x1
syscall_BRKINT = 0x2
syscall_PARMRK = 0x8
syscall_ISTRIP = 0x20
syscall_INLCR = 0x40
syscall_IGNCR = 0x80
syscall_ICRNL = 0x100
syscall_IXON = 0x200
syscall_OPOST = 0x1
syscall_ECHO = 0x8
syscall_ECHONL = 0x10
syscall_ICANON = 0x100
syscall_ISIG = 0x80
syscall_IEXTEN = 0x400
syscall_CSIZE = 0x300
syscall_PARENB = 0x1000
syscall_CS8 = 0x300
syscall_VMIN = 0x10
syscall_VTIME = 0x11
syscall_TCGETS = 0x402c7413
syscall_TCSETS = 0x802c7414
)

@ -0,0 +1,61 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -DUNICODE syscalls.go
package termbox
const (
foreground_blue = 0x1
foreground_green = 0x2
foreground_red = 0x4
foreground_intensity = 0x8
background_blue = 0x10
background_green = 0x20
background_red = 0x40
background_intensity = 0x80
std_input_handle = -0xa
std_output_handle = -0xb
key_event = 0x1
mouse_event = 0x2
window_buffer_size_event = 0x4
enable_window_input = 0x8
enable_mouse_input = 0x10
enable_extended_flags = 0x80
vk_f1 = 0x70
vk_f2 = 0x71
vk_f3 = 0x72
vk_f4 = 0x73
vk_f5 = 0x74
vk_f6 = 0x75
vk_f7 = 0x76
vk_f8 = 0x77
vk_f9 = 0x78
vk_f10 = 0x79
vk_f11 = 0x7a
vk_f12 = 0x7b
vk_insert = 0x2d
vk_delete = 0x2e
vk_home = 0x24
vk_end = 0x23
vk_pgup = 0x21
vk_pgdn = 0x22
vk_arrow_up = 0x26
vk_arrow_down = 0x28
vk_arrow_left = 0x25
vk_arrow_right = 0x27
vk_backspace = 0x8
vk_tab = 0x9
vk_enter = 0xd
vk_esc = 0x1b
vk_space = 0x20
left_alt_pressed = 0x2
left_ctrl_pressed = 0x8
right_alt_pressed = 0x1
right_ctrl_pressed = 0x4
shift_pressed = 0x10
generic_read = 0x80000000
generic_write = 0x40000000
console_textmode_buffer = 0x1
)

@ -0,0 +1,529 @@
// +build !windows
package termbox
import "unicode/utf8"
import "bytes"
import "syscall"
import "unsafe"
import "strings"
import "strconv"
import "os"
import "io"
// private API
const (
t_enter_ca = iota
t_exit_ca
t_show_cursor
t_hide_cursor
t_clear_screen
t_sgr0
t_underline
t_bold
t_blink
t_reverse
t_enter_keypad
t_exit_keypad
t_enter_mouse
t_exit_mouse
t_max_funcs
)
const (
coord_invalid = -2
attr_invalid = Attribute(0xFFFF)
)
type input_event struct {
data []byte
err error
}
type extract_event_res int
const (
event_not_extracted extract_event_res = iota
event_extracted
esc_wait
)
var (
// term specific sequences
keys []string
funcs []string
// termbox inner state
orig_tios syscall_Termios
back_buffer cellbuf
front_buffer cellbuf
termw int
termh int
input_mode = InputEsc
output_mode = OutputNormal
out *os.File
in int
lastfg = attr_invalid
lastbg = attr_invalid
lastx = coord_invalid
lasty = coord_invalid
cursor_x = cursor_hidden
cursor_y = cursor_hidden
foreground = ColorDefault
background = ColorDefault
inbuf = make([]byte, 0, 64)
outbuf bytes.Buffer
sigwinch = make(chan os.Signal, 1)
sigio = make(chan os.Signal, 1)
quit = make(chan int)
input_comm = make(chan input_event)
interrupt_comm = make(chan struct{})
intbuf = make([]byte, 0, 16)
// grayscale indexes
grayscale = []Attribute{
0, 17, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 232,
}
)
func write_cursor(x, y int) {
outbuf.WriteString("\033[")
outbuf.Write(strconv.AppendUint(intbuf, uint64(y+1), 10))
outbuf.WriteString(";")
outbuf.Write(strconv.AppendUint(intbuf, uint64(x+1), 10))
outbuf.WriteString("H")
}
func write_sgr_fg(a Attribute) {
switch output_mode {
case Output256, Output216, OutputGrayscale:
outbuf.WriteString("\033[38;5;")
outbuf.Write(strconv.AppendUint(intbuf, uint64(a-1), 10))
outbuf.WriteString("m")
default:
outbuf.WriteString("\033[3")
outbuf.Write(strconv.AppendUint(intbuf, uint64(a-1), 10))
outbuf.WriteString("m")
}
}
func write_sgr_bg(a Attribute) {
switch output_mode {
case Output256, Output216, OutputGrayscale:
outbuf.WriteString("\033[48;5;")
outbuf.Write(strconv.AppendUint(intbuf, uint64(a-1), 10))
outbuf.WriteString("m")
default:
outbuf.WriteString("\033[4")
outbuf.Write(strconv.AppendUint(intbuf, uint64(a-1), 10))
outbuf.WriteString("m")
}
}
func write_sgr(fg, bg Attribute) {
switch output_mode {
case Output256, Output216, OutputGrayscale:
outbuf.WriteString("\033[38;5;")
outbuf.Write(strconv.AppendUint(intbuf, uint64(fg-1), 10))
outbuf.WriteString("m")
outbuf.WriteString("\033[48;5;")
outbuf.Write(strconv.AppendUint(intbuf, uint64(bg-1), 10))
outbuf.WriteString("m")
default:
outbuf.WriteString("\033[3")
outbuf.Write(strconv.AppendUint(intbuf, uint64(fg-1), 10))
outbuf.WriteString(";4")
outbuf.Write(strconv.AppendUint(intbuf, uint64(bg-1), 10))
outbuf.WriteString("m")
}
}
type winsize struct {
rows uint16
cols uint16
xpixels uint16
ypixels uint16
}
func get_term_size(fd uintptr) (int, int) {
var sz winsize
_, _, _ = syscall.Syscall(syscall.SYS_IOCTL,
fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz)))
return int(sz.cols), int(sz.rows)
}
func send_attr(fg, bg Attribute) {
if fg == lastfg && bg == lastbg {
return
}
outbuf.WriteString(funcs[t_sgr0])
var fgcol, bgcol Attribute
switch output_mode {
case Output256:
fgcol = fg & 0x1FF
bgcol = bg & 0x1FF
case Output216:
fgcol = fg & 0xFF
bgcol = bg & 0xFF
if fgcol > 216 {
fgcol = ColorDefault
}
if bgcol > 216 {
bgcol = ColorDefault
}
if fgcol != ColorDefault {
fgcol += 0x10
}
if bgcol != ColorDefault {
bgcol += 0x10
}
case OutputGrayscale:
fgcol = fg & 0x1F
bgcol = bg & 0x1F
if fgcol > 26 {
fgcol = ColorDefault
}
if bgcol > 26 {
bgcol = ColorDefault
}
if fgcol != ColorDefault {
fgcol = grayscale[fgcol]
}
if bgcol != ColorDefault {
bgcol = grayscale[bgcol]
}
default:
fgcol = fg & 0x0F
bgcol = bg & 0x0F
}
if fgcol != ColorDefault {
if bgcol != ColorDefault {
write_sgr(fgcol, bgcol)
} else {
write_sgr_fg(fgcol)
}
} else if bgcol != ColorDefault {
write_sgr_bg(bgcol)
}
if fg&AttrBold != 0 {
outbuf.WriteString(funcs[t_bold])
}
if bg&AttrBold != 0 {
outbuf.WriteString(funcs[t_blink])
}
if fg&AttrUnderline != 0 {
outbuf.WriteString(funcs[t_underline])
}
if fg&AttrReverse|bg&AttrReverse != 0 {
outbuf.WriteString(funcs[t_reverse])
}
lastfg, lastbg = fg, bg
}
func send_char(x, y int, ch rune) {
var buf [8]byte
n := utf8.EncodeRune(buf[:], ch)
if x-1 != lastx || y != lasty {
write_cursor(x, y)
}
lastx, lasty = x, y
outbuf.Write(buf[:n])
}
func flush() error {
_, err := io.Copy(out, &outbuf)
outbuf.Reset()
return err
}
func send_clear() error {
send_attr(foreground, background)
outbuf.WriteString(funcs[t_clear_screen])
if !is_cursor_hidden(cursor_x, cursor_y) {
write_cursor(cursor_x, cursor_y)
}
// we need to invalidate cursor position too and these two vars are
// used only for simple cursor positioning optimization, cursor
// actually may be in the correct place, but we simply discard
// optimization once and it gives us simple solution for the case when
// cursor moved
lastx = coord_invalid
lasty = coord_invalid
return flush()
}
func update_size_maybe() error {
w, h := get_term_size(out.Fd())
if w != termw || h != termh {
termw, termh = w, h
back_buffer.resize(termw, termh)
front_buffer.resize(termw, termh)
front_buffer.clear()
return send_clear()
}
return nil
}
func tcsetattr(fd uintptr, termios *syscall_Termios) error {
r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
fd, uintptr(syscall_TCSETS), uintptr(unsafe.Pointer(termios)))
if r != 0 {
return os.NewSyscallError("SYS_IOCTL", e)
}
return nil
}
func tcgetattr(fd uintptr, termios *syscall_Termios) error {
r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
fd, uintptr(syscall_TCGETS), uintptr(unsafe.Pointer(termios)))
if r != 0 {
return os.NewSyscallError("SYS_IOCTL", e)
}
return nil
}
func parse_mouse_event(event *Event, buf string) (int, bool) {
if strings.HasPrefix(buf, "\033[M") && len(buf) >= 6 {
// X10 mouse encoding, the simplest one
// \033 [ M Cb Cx Cy
b := buf[3] - 32
switch b & 3 {
case 0:
if b&64 != 0 {
event.Key = MouseWheelUp
} else {
event.Key = MouseLeft
}
case 1:
if b&64 != 0 {
event.Key = MouseWheelDown
} else {
event.Key = MouseMiddle
}
case 2:
event.Key = MouseRight
case 3:
event.Key = MouseRelease
default:
return 6, false
}
event.Type = EventMouse // KeyEvent by default
if b&32 != 0 {
event.Mod |= ModMotion
}
// the coord is 1,1 for upper left
event.MouseX = int(buf[4]) - 1 - 32
event.MouseY = int(buf[5]) - 1 - 32
return 6, true
} else if strings.HasPrefix(buf, "\033[<") || strings.HasPrefix(buf, "\033[") {
// xterm 1006 extended mode or urxvt 1015 extended mode
// xterm: \033 [ < Cb ; Cx ; Cy (M or m)
// urxvt: \033 [ Cb ; Cx ; Cy M
// find the first M or m, that's where we stop
mi := strings.IndexAny(buf, "Mm")
if mi == -1 {
return 0, false
}
// whether it's a capital M or not
isM := buf[mi] == 'M'
// whether it's urxvt or not
isU := false
// buf[2] is safe here, because having M or m found means we have at
// least 3 bytes in a string
if buf[2] == '<' {
buf = buf[3:mi]
} else {
isU = true
buf = buf[2:mi]
}
s1 := strings.Index(buf, ";")
s2 := strings.LastIndex(buf, ";")
// not found or only one ';'
if s1 == -1 || s2 == -1 || s1 == s2 {
return 0, false
}
n1, err := strconv.ParseInt(buf[0:s1], 10, 64)
if err != nil {
return 0, false
}
n2, err := strconv.ParseInt(buf[s1+1:s2], 10, 64)
if err != nil {
return 0, false
}
n3, err := strconv.ParseInt(buf[s2+1:], 10, 64)
if err != nil {
return 0, false
}
// on urxvt, first number is encoded exactly as in X10, but we need to
// make it zero-based, on xterm it is zero-based already
if isU {
n1 -= 32
}
switch n1 & 3 {
case 0:
if n1&64 != 0 {
event.Key = MouseWheelUp
} else {
event.Key = MouseLeft
}
case 1:
if n1&64 != 0 {
event.Key = MouseWheelDown
} else {
event.Key = MouseMiddle
}
case 2:
event.Key = MouseRight
case 3:
event.Key = MouseRelease
default:
return mi + 1, false
}
if !isM {
// on xterm mouse release is signaled by lowercase m
event.Key = MouseRelease
}
event.Type = EventMouse // KeyEvent by default
if n1&32 != 0 {
event.Mod |= ModMotion
}
event.MouseX = int(n2) - 1
event.MouseY = int(n3) - 1
return mi + 1, true
}
return 0, false
}
func parse_escape_sequence(event *Event, buf []byte) (int, bool) {
bufstr := string(buf)
for i, key := range keys {
if strings.HasPrefix(bufstr, key) {
event.Ch = 0
event.Key = Key(0xFFFF - i)
return len(key), true
}
}
// if none of the keys match, let's try mouse sequences
return parse_mouse_event(event, bufstr)
}
func extract_raw_event(data []byte, event *Event) bool {
if len(inbuf) == 0 {
return false
}
n := len(data)
if n == 0 {
return false
}
n = copy(data, inbuf)
copy(inbuf, inbuf[n:])
inbuf = inbuf[:len(inbuf)-n]
event.N = n
event.Type = EventRaw
return true
}
func extract_event(inbuf []byte, event *Event, allow_esc_wait bool) extract_event_res {
if len(inbuf) == 0 {
event.N = 0
return event_not_extracted
}
if inbuf[0] == '\033' {
// possible escape sequence
if n, ok := parse_escape_sequence(event, inbuf); n != 0 {
event.N = n
if ok {
return event_extracted
} else {
return event_not_extracted
}
}
// possible partially read escape sequence; trigger a wait if appropriate
if enable_wait_for_escape_sequence() && allow_esc_wait {
event.N = 0
return esc_wait
}
// it's not escape sequence, then it's Alt or Esc, check input_mode
switch {
case input_mode&InputEsc != 0:
// if we're in escape mode, fill Esc event, pop buffer, return success
event.Ch = 0
event.Key = KeyEsc
event.Mod = 0
event.N = 1
return event_extracted
case input_mode&InputAlt != 0:
// if we're in alt mode, set Alt modifier to event and redo parsing
event.Mod = ModAlt
status := extract_event(inbuf[1:], event, false)
if status == event_extracted {
event.N++
} else {
event.N = 0
}
return status
default:
panic("unreachable")
}
}
// if we're here, this is not an escape sequence and not an alt sequence
// so, it's a FUNCTIONAL KEY or a UNICODE character
// first of all check if it's a functional key
if Key(inbuf[0]) <= KeySpace || Key(inbuf[0]) == KeyBackspace2 {
// fill event, pop buffer, return success
event.Ch = 0
event.Key = Key(inbuf[0])
event.N = 1
return event_extracted
}
// the only possible option is utf8 rune
if r, n := utf8.DecodeRune(inbuf); r != utf8.RuneError {
event.Ch = r
event.Key = 0
event.N = n
return event_extracted
}
return event_not_extracted
}
func fcntl(fd int, cmd int, arg int) (val int, err error) {
r, _, e := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), uintptr(cmd),
uintptr(arg))
val = int(r)
if e != 0 {
err = e
}
return
}

@ -0,0 +1,59 @@
package termbox
// private API, common OS agnostic part
type cellbuf struct {
width int
height int
cells []Cell
}
func (this *cellbuf) init(width, height int) {
this.width = width
this.height = height
this.cells = make([]Cell, width*height)
}
func (this *cellbuf) resize(width, height int) {
if this.width == width && this.height == height {
return
}
oldw := this.width
oldh := this.height
oldcells := this.cells
this.init(width, height)
this.clear()
minw, minh := oldw, oldh
if width < minw {
minw = width
}
if height < minh {
minh = height
}
for i := 0; i < minh; i++ {
srco, dsto := i*oldw, i*width
src := oldcells[srco : srco+minw]
dst := this.cells[dsto : dsto+minw]
copy(dst, src)
}
}
func (this *cellbuf) clear() {
for i := range this.cells {
c := &this.cells[i]
c.Ch = ' '
c.Fg = foreground
c.Bg = background
}
}
const cursor_hidden = -1
func is_cursor_hidden(x, y int) bool {
return x == cursor_hidden || y == cursor_hidden
}

@ -0,0 +1,948 @@
package termbox
import "math"
import "syscall"
import "unsafe"
import "unicode/utf16"
import "github.com/mattn/go-runewidth"
type (
wchar uint16
short int16
dword uint32
word uint16
char_info struct {
char wchar
attr word
}
coord struct {
x short
y short
}
small_rect struct {
left short
top short
right short
bottom short
}
console_screen_buffer_info struct {
size coord
cursor_position coord
attributes word
window small_rect
maximum_window_size coord
}
console_cursor_info struct {
size dword
visible int32
}
input_record struct {
event_type word
_ [2]byte
event [16]byte
}
key_event_record struct {
key_down int32
repeat_count word
virtual_key_code word
virtual_scan_code word
unicode_char wchar
control_key_state dword
}
window_buffer_size_record struct {
size coord
}
mouse_event_record struct {
mouse_pos coord
button_state dword
control_key_state dword
event_flags dword
}
console_font_info struct {
font uint32
font_size coord
}
)
const (
mouse_lmb = 0x1
mouse_rmb = 0x2
mouse_mmb = 0x4 | 0x8 | 0x10
SM_CXMIN = 28
SM_CYMIN = 29
)
func (this coord) uintptr() uintptr {
return uintptr(*(*int32)(unsafe.Pointer(&this)))
}
func (this *small_rect) uintptr() uintptr {
return uintptr(unsafe.Pointer(this))
}
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var moduser32 = syscall.NewLazyDLL("user32.dll")
var is_cjk = runewidth.IsEastAsian()
var (
proc_set_console_active_screen_buffer = kernel32.NewProc("SetConsoleActiveScreenBuffer")
proc_set_console_screen_buffer_size = kernel32.NewProc("SetConsoleScreenBufferSize")
proc_set_console_window_info = kernel32.NewProc("SetConsoleWindowInfo")
proc_create_console_screen_buffer = kernel32.NewProc("CreateConsoleScreenBuffer")
proc_get_console_screen_buffer_info = kernel32.NewProc("GetConsoleScreenBufferInfo")
proc_write_console_output = kernel32.NewProc("WriteConsoleOutputW")
proc_write_console_output_character = kernel32.NewProc("WriteConsoleOutputCharacterW")
proc_write_console_output_attribute = kernel32.NewProc("WriteConsoleOutputAttribute")
proc_set_console_cursor_info = kernel32.NewProc("SetConsoleCursorInfo")
proc_set_console_cursor_position = kernel32.NewProc("SetConsoleCursorPosition")
proc_get_console_cursor_info = kernel32.NewProc("GetConsoleCursorInfo")
proc_read_console_input = kernel32.NewProc("ReadConsoleInputW")
proc_get_console_mode = kernel32.NewProc("GetConsoleMode")
proc_set_console_mode = kernel32.NewProc("SetConsoleMode")
proc_fill_console_output_character = kernel32.NewProc("FillConsoleOutputCharacterW")
proc_fill_console_output_attribute = kernel32.NewProc("FillConsoleOutputAttribute")
proc_create_event = kernel32.NewProc("CreateEventW")
proc_wait_for_multiple_objects = kernel32.NewProc("WaitForMultipleObjects")
proc_set_event = kernel32.NewProc("SetEvent")
proc_get_current_console_font = kernel32.NewProc("GetCurrentConsoleFont")
get_system_metrics = moduser32.NewProc("GetSystemMetrics")
)
func set_console_active_screen_buffer(h syscall.Handle) (err error) {
r0, _, e1 := syscall.Syscall(proc_set_console_active_screen_buffer.Addr(),
1, uintptr(h), 0, 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func set_console_screen_buffer_size(h syscall.Handle, size coord) (err error) {
r0, _, e1 := syscall.Syscall(proc_set_console_screen_buffer_size.Addr(),
2, uintptr(h), size.uintptr(), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func set_console_window_info(h syscall.Handle, window *small_rect) (err error) {
var absolute uint32
absolute = 1
r0, _, e1 := syscall.Syscall(proc_set_console_window_info.Addr(),
3, uintptr(h), uintptr(absolute), window.uintptr())
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func create_console_screen_buffer() (h syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall6(proc_create_console_screen_buffer.Addr(),
5, uintptr(generic_read|generic_write), 0, 0, console_textmode_buffer, 0, 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return syscall.Handle(r0), err
}
func get_console_screen_buffer_info(h syscall.Handle, info *console_screen_buffer_info) (err error) {
r0, _, e1 := syscall.Syscall(proc_get_console_screen_buffer_info.Addr(),
2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func write_console_output(h syscall.Handle, chars []char_info, dst small_rect) (err error) {
tmp_coord = coord{dst.right - dst.left + 1, dst.bottom - dst.top + 1}
tmp_rect = dst
r0, _, e1 := syscall.Syscall6(proc_write_console_output.Addr(),
5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), tmp_coord.uintptr(),
tmp_coord0.uintptr(), uintptr(unsafe.Pointer(&tmp_rect)), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func write_console_output_character(h syscall.Handle, chars []wchar, pos coord) (err error) {
r0, _, e1 := syscall.Syscall6(proc_write_console_output_character.Addr(),
5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), uintptr(len(chars)),
pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func write_console_output_attribute(h syscall.Handle, attrs []word, pos coord) (err error) {
r0, _, e1 := syscall.Syscall6(proc_write_console_output_attribute.Addr(),
5, uintptr(h), uintptr(unsafe.Pointer(&attrs[0])), uintptr(len(attrs)),
pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func set_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) {
r0, _, e1 := syscall.Syscall(proc_set_console_cursor_info.Addr(),
2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func get_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) {
r0, _, e1 := syscall.Syscall(proc_get_console_cursor_info.Addr(),
2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func set_console_cursor_position(h syscall.Handle, pos coord) (err error) {
r0, _, e1 := syscall.Syscall(proc_set_console_cursor_position.Addr(),
2, uintptr(h), pos.uintptr(), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func read_console_input(h syscall.Handle, record *input_record) (err error) {
r0, _, e1 := syscall.Syscall6(proc_read_console_input.Addr(),
4, uintptr(h), uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&tmp_arg)), 0, 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func get_console_mode(h syscall.Handle, mode *dword) (err error) {
r0, _, e1 := syscall.Syscall(proc_get_console_mode.Addr(),
2, uintptr(h), uintptr(unsafe.Pointer(mode)), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func set_console_mode(h syscall.Handle, mode dword) (err error) {
r0, _, e1 := syscall.Syscall(proc_set_console_mode.Addr(),
2, uintptr(h), uintptr(mode), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func fill_console_output_character(h syscall.Handle, char wchar, n int) (err error) {
tmp_coord = coord{0, 0}
r0, _, e1 := syscall.Syscall6(proc_fill_console_output_character.Addr(),
5, uintptr(h), uintptr(char), uintptr(n), tmp_coord.uintptr(),
uintptr(unsafe.Pointer(&tmp_arg)), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func fill_console_output_attribute(h syscall.Handle, attr word, n int) (err error) {
tmp_coord = coord{0, 0}
r0, _, e1 := syscall.Syscall6(proc_fill_console_output_attribute.Addr(),
5, uintptr(h), uintptr(attr), uintptr(n), tmp_coord.uintptr(),
uintptr(unsafe.Pointer(&tmp_arg)), 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func create_event() (out syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall6(proc_create_event.Addr(),
4, 0, 0, 0, 0, 0, 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return syscall.Handle(r0), err
}
func wait_for_multiple_objects(objects []syscall.Handle) (err error) {
r0, _, e1 := syscall.Syscall6(proc_wait_for_multiple_objects.Addr(),
4, uintptr(len(objects)), uintptr(unsafe.Pointer(&objects[0])),
0, 0xFFFFFFFF, 0, 0)
if uint32(r0) == 0xFFFFFFFF {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func set_event(ev syscall.Handle) (err error) {
r0, _, e1 := syscall.Syscall(proc_set_event.Addr(),
1, uintptr(ev), 0, 0)
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func get_current_console_font(h syscall.Handle, info *console_font_info) (err error) {
r0, _, e1 := syscall.Syscall(proc_get_current_console_font.Addr(),
3, uintptr(h), 0, uintptr(unsafe.Pointer(info)))
if int(r0) == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
type diff_msg struct {
pos short
lines short
chars []char_info
}
type input_event struct {
event Event
err error
}
var (
orig_cursor_info console_cursor_info
orig_size coord
orig_window small_rect
orig_mode dword
orig_screen syscall.Handle
back_buffer cellbuf
front_buffer cellbuf
term_size coord
input_mode = InputEsc
cursor_x = cursor_hidden
cursor_y = cursor_hidden
foreground = ColorDefault
background = ColorDefault
in syscall.Handle
out syscall.Handle
interrupt syscall.Handle
charbuf []char_info
diffbuf []diff_msg
beg_x = -1
beg_y = -1
beg_i = -1
input_comm = make(chan Event)
interrupt_comm = make(chan struct{})
cancel_comm = make(chan bool, 1)
cancel_done_comm = make(chan bool)
alt_mode_esc = false
// these ones just to prevent heap allocs at all costs
tmp_info console_screen_buffer_info
tmp_arg dword
tmp_coord0 = coord{0, 0}
tmp_coord = coord{0, 0}
tmp_rect = small_rect{0, 0, 0, 0}
tmp_finfo console_font_info
)
func get_cursor_position(out syscall.Handle) coord {
err := get_console_screen_buffer_info(out, &tmp_info)
if err != nil {
panic(err)
}
return tmp_info.cursor_position
}
func get_term_size(out syscall.Handle) (coord, small_rect) {
err := get_console_screen_buffer_info(out, &tmp_info)
if err != nil {
panic(err)
}
return tmp_info.size, tmp_info.window
}
func get_win_min_size(out syscall.Handle) coord {
x, _, err := get_system_metrics.Call(SM_CXMIN)
y, _, err := get_system_metrics.Call(SM_CYMIN)
if x == 0 || y == 0 {
if err != nil {
panic(err)
}
}
err1 := get_current_console_font(out, &tmp_finfo)
if err1 != nil {
panic(err1)
}
return coord{
x: short(math.Ceil(float64(x) / float64(tmp_finfo.font_size.x))),
y: short(math.Ceil(float64(y) / float64(tmp_finfo.font_size.y))),
}
}
func get_win_size(out syscall.Handle) coord {
err := get_console_screen_buffer_info(out, &tmp_info)
if err != nil {
panic(err)
}
min_size := get_win_min_size(out)
size := coord{
x: tmp_info.window.right - tmp_info.window.left + 1,
y: tmp_info.window.bottom - tmp_info.window.top + 1,
}
if size.x < min_size.x {
size.x = min_size.x
}
if size.y < min_size.y {
size.y = min_size.y
}
return size
}
func fix_win_size(out syscall.Handle, size coord) (err error) {
window := small_rect{}
window.top = 0
window.bottom = size.y - 1
window.left = 0
window.right = size.x - 1
return set_console_window_info(out, &window)
}
func update_size_maybe() {
size := get_win_size(out)
if size.x != term_size.x || size.y != term_size.y {
set_console_screen_buffer_size(out, size)
fix_win_size(out, size)
term_size = size
back_buffer.resize(int(size.x), int(size.y))
front_buffer.resize(int(size.x), int(size.y))
front_buffer.clear()
clear()
area := int(size.x) * int(size.y)
if cap(charbuf) < area {
charbuf = make([]char_info, 0, area)
}
}
}
var color_table_bg = []word{
0, // default (black)
0, // black
background_red,
background_green,
background_red | background_green, // yellow
background_blue,
background_red | background_blue, // magenta
background_green | background_blue, // cyan
background_red | background_blue | background_green, // white
}
var color_table_fg = []word{
foreground_red | foreground_blue | foreground_green, // default (white)
0,
foreground_red,
foreground_green,
foreground_red | foreground_green, // yellow
foreground_blue,
foreground_red | foreground_blue, // magenta
foreground_green | foreground_blue, // cyan
foreground_red | foreground_blue | foreground_green, // white
}
const (
replacement_char = '\uFFFD'
max_rune = '\U0010FFFF'
surr1 = 0xd800
surr2 = 0xdc00
surr3 = 0xe000
surr_self = 0x10000
)
func append_diff_line(y int) int {
n := 0
for x := 0; x < front_buffer.width; {
cell_offset := y*front_buffer.width + x
back := &back_buffer.cells[cell_offset]
front := &front_buffer.cells[cell_offset]
attr, char := cell_to_char_info(*back)
charbuf = append(charbuf, char_info{attr: attr, char: char[0]})
*front = *back
n++
w := runewidth.RuneWidth(back.Ch)
if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) {
w = 1
}
x += w
// If not CJK, fill trailing space with whitespace
if !is_cjk && w == 2 {
charbuf = append(charbuf, char_info{attr: attr, char: ' '})
}
}
return n
}
// compares 'back_buffer' with 'front_buffer' and prepares all changes in the form of
// 'diff_msg's in the 'diff_buf'
func prepare_diff_messages() {
// clear buffers
diffbuf = diffbuf[:0]
charbuf = charbuf[:0]
var diff diff_msg
gbeg := 0
for y := 0; y < front_buffer.height; y++ {
same := true
line_offset := y * front_buffer.width
for x := 0; x < front_buffer.width; x++ {
cell_offset := line_offset + x
back := &back_buffer.cells[cell_offset]
front := &front_buffer.cells[cell_offset]
if *back != *front {
same = false
break
}
}
if same && diff.lines > 0 {
diffbuf = append(diffbuf, diff)
diff = diff_msg{}
}
if !same {
beg := len(charbuf)
end := beg + append_diff_line(y)
if diff.lines == 0 {
diff.pos = short(y)
gbeg = beg
}
diff.lines++
diff.chars = charbuf[gbeg:end]
}
}
if diff.lines > 0 {
diffbuf = append(diffbuf, diff)
diff = diff_msg{}
}
}
func get_ct(table []word, idx int) word {
idx = idx & 0x0F
if idx >= len(table) {
idx = len(table) - 1
}
return table[idx]
}
func cell_to_char_info(c Cell) (attr word, wc [2]wchar) {
attr = get_ct(color_table_fg, int(c.Fg)) | get_ct(color_table_bg, int(c.Bg))
if c.Fg&AttrReverse|c.Bg&AttrReverse != 0 {
attr = (attr&0xF0)>>4 | (attr&0x0F)<<4
}
if c.Fg&AttrBold != 0 {
attr |= foreground_intensity
}
if c.Bg&AttrBold != 0 {
attr |= background_intensity
}
r0, r1 := utf16.EncodeRune(c.Ch)
if r0 == 0xFFFD {
wc[0] = wchar(c.Ch)
wc[1] = ' '
} else {
wc[0] = wchar(r0)
wc[1] = wchar(r1)
}
return
}
func move_cursor(x, y int) {
err := set_console_cursor_position(out, coord{short(x), short(y)})
if err != nil {
panic(err)
}
}
func show_cursor(visible bool) {
var v int32
if visible {
v = 1
}
var info console_cursor_info
info.size = 100
info.visible = v
err := set_console_cursor_info(out, &info)
if err != nil {
panic(err)
}
}
func clear() {
var err error
attr, char := cell_to_char_info(Cell{
' ',
foreground,
background,
})
area := int(term_size.x) * int(term_size.y)
err = fill_console_output_attribute(out, attr, area)
if err != nil {
panic(err)
}
err = fill_console_output_character(out, char[0], area)
if err != nil {
panic(err)
}
if !is_cursor_hidden(cursor_x, cursor_y) {
move_cursor(cursor_x, cursor_y)
}
}
func key_event_record_to_event(r *key_event_record) (Event, bool) {
if r.key_down == 0 {
return Event{}, false
}
e := Event{Type: EventKey}
if input_mode&InputAlt != 0 {
if alt_mode_esc {
e.Mod = ModAlt
alt_mode_esc = false
}
if r.control_key_state&(left_alt_pressed|right_alt_pressed) != 0 {
e.Mod = ModAlt
}
}
ctrlpressed := r.control_key_state&(left_ctrl_pressed|right_ctrl_pressed) != 0
if r.virtual_key_code >= vk_f1 && r.virtual_key_code <= vk_f12 {
switch r.virtual_key_code {
case vk_f1:
e.Key = KeyF1
case vk_f2:
e.Key = KeyF2
case vk_f3:
e.Key = KeyF3
case vk_f4:
e.Key = KeyF4
case vk_f5:
e.Key = KeyF5
case vk_f6:
e.Key = KeyF6
case vk_f7:
e.Key = KeyF7
case vk_f8:
e.Key = KeyF8
case vk_f9:
e.Key = KeyF9
case vk_f10:
e.Key = KeyF10
case vk_f11:
e.Key = KeyF11
case vk_f12:
e.Key = KeyF12
default:
panic("unreachable")
}
return e, true
}
if r.virtual_key_code <= vk_delete {
switch r.virtual_key_code {
case vk_insert:
e.Key = KeyInsert
case vk_delete:
e.Key = KeyDelete
case vk_home:
e.Key = KeyHome
case vk_end:
e.Key = KeyEnd
case vk_pgup:
e.Key = KeyPgup
case vk_pgdn:
e.Key = KeyPgdn
case vk_arrow_up:
e.Key = KeyArrowUp
case vk_arrow_down:
e.Key = KeyArrowDown
case vk_arrow_left:
e.Key = KeyArrowLeft
case vk_arrow_right:
e.Key = KeyArrowRight
case vk_backspace:
if ctrlpressed {
e.Key = KeyBackspace2
} else {
e.Key = KeyBackspace
}
case vk_tab:
e.Key = KeyTab
case vk_enter:
e.Key = KeyEnter
case vk_esc:
switch {
case input_mode&InputEsc != 0:
e.Key = KeyEsc
case input_mode&InputAlt != 0:
alt_mode_esc = true
return Event{}, false
}
case vk_space:
if ctrlpressed {
// manual return here, because KeyCtrlSpace is zero
e.Key = KeyCtrlSpace
return e, true
} else {
e.Key = KeySpace
}
}
if e.Key != 0 {
return e, true
}
}
if ctrlpressed {
if Key(r.unicode_char) >= KeyCtrlA && Key(r.unicode_char) <= KeyCtrlRsqBracket {
e.Key = Key(r.unicode_char)
if input_mode&InputAlt != 0 && e.Key == KeyEsc {
alt_mode_esc = true
return Event{}, false
}
return e, true
}
switch r.virtual_key_code {
case 192, 50:
// manual return here, because KeyCtrl2 is zero
e.Key = KeyCtrl2
return e, true
case 51:
if input_mode&InputAlt != 0 {
alt_mode_esc = true
return Event{}, false
}
e.Key = KeyCtrl3
case 52:
e.Key = KeyCtrl4
case 53:
e.Key = KeyCtrl5
case 54:
e.Key = KeyCtrl6
case 189, 191, 55:
e.Key = KeyCtrl7
case 8, 56:
e.Key = KeyCtrl8
}
if e.Key != 0 {
return e, true
}
}
if r.unicode_char != 0 {
e.Ch = rune(r.unicode_char)
return e, true
}
return Event{}, false
}
func input_event_producer() {
var r input_record
var err error
var last_button Key
var last_button_pressed Key
var last_state = dword(0)
var last_x, last_y = -1, -1
handles := []syscall.Handle{in, interrupt}
for {
err = wait_for_multiple_objects(handles)
if err != nil {
input_comm <- Event{Type: EventError, Err: err}
}
select {
case <-cancel_comm:
cancel_done_comm <- true
return
default:
}
err = read_console_input(in, &r)
if err != nil {
input_comm <- Event{Type: EventError, Err: err}
}
switch r.event_type {
case key_event:
kr := (*key_event_record)(unsafe.Pointer(&r.event))
ev, ok := key_event_record_to_event(kr)
if ok {
for i := 0; i < int(kr.repeat_count); i++ {
input_comm <- ev
}
}
case window_buffer_size_event:
sr := *(*window_buffer_size_record)(unsafe.Pointer(&r.event))
input_comm <- Event{
Type: EventResize,
Width: int(sr.size.x),
Height: int(sr.size.y),
}
case mouse_event:
mr := *(*mouse_event_record)(unsafe.Pointer(&r.event))
ev := Event{Type: EventMouse}
switch mr.event_flags {
case 0, 2:
// single or double click
cur_state := mr.button_state
switch {
case last_state&mouse_lmb == 0 && cur_state&mouse_lmb != 0:
last_button = MouseLeft
last_button_pressed = last_button
case last_state&mouse_rmb == 0 && cur_state&mouse_rmb != 0:
last_button = MouseRight
last_button_pressed = last_button
case last_state&mouse_mmb == 0 && cur_state&mouse_mmb != 0:
last_button = MouseMiddle
last_button_pressed = last_button
case last_state&mouse_lmb != 0 && cur_state&mouse_lmb == 0:
last_button = MouseRelease
case last_state&mouse_rmb != 0 && cur_state&mouse_rmb == 0:
last_button = MouseRelease
case last_state&mouse_mmb != 0 && cur_state&mouse_mmb == 0:
last_button = MouseRelease
default:
last_state = cur_state
continue
}
last_state = cur_state
ev.Key = last_button
last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
ev.MouseX = last_x
ev.MouseY = last_y
case 1:
// mouse motion
x, y := int(mr.mouse_pos.x), int(mr.mouse_pos.y)
if last_state != 0 && (last_x != x || last_y != y) {
ev.Key = last_button_pressed
ev.Mod = ModMotion
ev.MouseX = x
ev.MouseY = y
last_x, last_y = x, y
} else {
ev.Type = EventNone
}
case 4:
// mouse wheel
n := int16(mr.button_state >> 16)
if n > 0 {
ev.Key = MouseWheelUp
} else {
ev.Key = MouseWheelDown
}
last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
ev.MouseX = last_x
ev.MouseY = last_y
default:
ev.Type = EventNone
}
if ev.Type != EventNone {
input_comm <- ev
}
}
}
}

@ -0,0 +1,232 @@
// +build !windows
// This file contains a simple and incomplete implementation of the terminfo
// database. Information was taken from the ncurses manpages term(5) and
// terminfo(5). Currently, only the string capabilities for special keys and for
// functions without parameters are actually used. Colors are still done with
// ANSI escape sequences. Other special features that are not (yet?) supported
// are reading from ~/.terminfo, the TERMINFO_DIRS variable, Berkeley database
// format and extended capabilities.
package termbox
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
)
const (
ti_magic = 0432
ti_header_length = 12
ti_mouse_enter = "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
ti_mouse_leave = "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
)
func load_terminfo() ([]byte, error) {
var data []byte
var err error
term := os.Getenv("TERM")
if term == "" {
return nil, fmt.Errorf("termbox: TERM not set")
}
// The following behaviour follows the one described in terminfo(5) as
// distributed by ncurses.
terminfo := os.Getenv("TERMINFO")
if terminfo != "" {
// if TERMINFO is set, no other directory should be searched
return ti_try_path(terminfo)
}
// next, consider ~/.terminfo
home := os.Getenv("HOME")
if home != "" {
data, err = ti_try_path(home + "/.terminfo")
if err == nil {
return data, nil
}
}
// next, TERMINFO_DIRS
dirs := os.Getenv("TERMINFO_DIRS")
if dirs != "" {
for _, dir := range strings.Split(dirs, ":") {
if dir == "" {
// "" -> "/usr/share/terminfo"
dir = "/usr/share/terminfo"
}
data, err = ti_try_path(dir)
if err == nil {
return data, nil
}
}
}
// next, /lib/terminfo
data, err = ti_try_path("/lib/terminfo")
if err == nil {
return data, nil
}
// fall back to /usr/share/terminfo
return ti_try_path("/usr/share/terminfo")
}
func ti_try_path(path string) (data []byte, err error) {
// load_terminfo already made sure it is set
term := os.Getenv("TERM")
// first try, the typical *nix path
terminfo := path + "/" + term[0:1] + "/" + term
data, err = ioutil.ReadFile(terminfo)
if err == nil {
return
}
// fallback to darwin specific dirs structure
terminfo = path + "/" + hex.EncodeToString([]byte(term[:1])) + "/" + term
data, err = ioutil.ReadFile(terminfo)
return
}
func setup_term_builtin() error {
name := os.Getenv("TERM")
if name == "" {
return errors.New("termbox: TERM environment variable not set")
}
for _, t := range terms {
if t.name == name {
keys = t.keys
funcs = t.funcs
return nil
}
}
compat_table := []struct {
partial string
keys []string
funcs []string
}{
{"xterm", xterm_keys, xterm_funcs},
{"rxvt", rxvt_unicode_keys, rxvt_unicode_funcs},
{"linux", linux_keys, linux_funcs},
{"Eterm", eterm_keys, eterm_funcs},
{"screen", screen_keys, screen_funcs},
// let's assume that 'cygwin' is xterm compatible
{"cygwin", xterm_keys, xterm_funcs},
{"st", xterm_keys, xterm_funcs},
}
// try compatibility variants
for _, it := range compat_table {
if strings.Contains(name, it.partial) {
keys = it.keys
funcs = it.funcs
return nil
}
}
return errors.New("termbox: unsupported terminal")
}
func setup_term() (err error) {
var data []byte
var header [6]int16
var str_offset, table_offset int16
data, err = load_terminfo()
if err != nil {
return setup_term_builtin()
}
rd := bytes.NewReader(data)
// 0: magic number, 1: size of names section, 2: size of boolean section, 3:
// size of numbers section (in integers), 4: size of the strings section (in
// integers), 5: size of the string table
err = binary.Read(rd, binary.LittleEndian, header[:])
if err != nil {
return
}
number_sec_len := int16(2)
if header[0] == 542 { // doc says it should be octal 0542, but what I see it terminfo files is 542, learn to program please... thank you..
number_sec_len = 4
}
if (header[1]+header[2])%2 != 0 {
// old quirk to align everything on word boundaries
header[2] += 1
}
str_offset = ti_header_length + header[1] + header[2] + number_sec_len*header[3]
table_offset = str_offset + 2*header[4]
keys = make([]string, 0xFFFF-key_min)
for i, _ := range keys {
keys[i], err = ti_read_string(rd, str_offset+2*ti_keys[i], table_offset)
if err != nil {
return
}
}
funcs = make([]string, t_max_funcs)
// the last two entries are reserved for mouse. because the table offset is
// not there, the two entries have to fill in manually
for i, _ := range funcs[:len(funcs)-2] {
funcs[i], err = ti_read_string(rd, str_offset+2*ti_funcs[i], table_offset)
if err != nil {
return
}
}
funcs[t_max_funcs-2] = ti_mouse_enter
funcs[t_max_funcs-1] = ti_mouse_leave
return nil
}
func ti_read_string(rd *bytes.Reader, str_off, table int16) (string, error) {
var off int16
_, err := rd.Seek(int64(str_off), 0)
if err != nil {
return "", err
}
err = binary.Read(rd, binary.LittleEndian, &off)
if err != nil {
return "", err
}
_, err = rd.Seek(int64(table+off), 0)
if err != nil {
return "", err
}
var bs []byte
for {
b, err := rd.ReadByte()
if err != nil {
return "", err
}
if b == byte(0x00) {
break
}
bs = append(bs, b)
}
return string(bs), nil
}
// "Maps" the function constants from termbox.go to the number of the respective
// string capability in the terminfo file. Taken from (ncurses) term.h.
var ti_funcs = []int16{
28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88,
}
// Same as above for the special keys.
var ti_keys = []int16{
66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69, 70,
71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61, 79, 83,
}

@ -0,0 +1,64 @@
// +build !windows
package termbox
// Eterm
var eterm_keys = []string{
"\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
}
var eterm_funcs = []string{
"\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "",
}
// screen
var screen_keys = []string{
"\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC",
}
var screen_funcs = []string{
"\x1b[?1049h", "\x1b[?1049l", "\x1b[34h\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave,
}
// xterm
var xterm_keys = []string{
"\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1bOH", "\x1bOF", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC",
}
var xterm_funcs = []string{
"\x1b[?1049h", "\x1b[?1049l", "\x1b[?12l\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b(B\x1b[m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave,
}
// rxvt-unicode
var rxvt_unicode_keys = []string{
"\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
}
var rxvt_unicode_funcs = []string{
"\x1b[?1049h", "\x1b[r\x1b[?1049l", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x1b(B", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave,
}
// linux
var linux_keys = []string{
"\x1b[[A", "\x1b[[B", "\x1b[[C", "\x1b[[D", "\x1b[[E", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
}
var linux_funcs = []string{
"", "", "\x1b[?25h\x1b[?0c", "\x1b[?25l\x1b[?1c", "\x1b[H\x1b[J", "\x1b[0;10m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "",
}
// rxvt-256color
var rxvt_256color_keys = []string{
"\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
}
var rxvt_256color_funcs = []string{
"\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave,
}
var terms = []struct {
name string
keys []string
funcs []string
}{
{"Eterm", eterm_keys, eterm_funcs},
{"screen", screen_keys, screen_funcs},
{"xterm", xterm_keys, xterm_funcs},
{"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},
{"linux", linux_keys, linux_funcs},
{"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},
}

@ -35,6 +35,8 @@ github.com/golang/protobuf/protoc-gen-go/plugin
github.com/grpc-ecosystem/grpc-gateway/runtime
github.com/grpc-ecosystem/grpc-gateway/utilities
github.com/grpc-ecosystem/grpc-gateway/internal
# github.com/jroimartin/gocui v0.4.0
github.com/jroimartin/gocui
# github.com/juju/loggo v0.0.0-20190212223446-d976af380377
github.com/juju/loggo
# github.com/lightningnetwork/lnd v0.5.2-beta
@ -42,8 +44,12 @@ github.com/lightningnetwork/lnd/lncfg
github.com/lightningnetwork/lnd/lnrpc
github.com/lightningnetwork/lnd/macaroons
github.com/lightningnetwork/lnd/tor
# github.com/mattn/go-runewidth v0.0.4
github.com/mattn/go-runewidth
# github.com/miekg/dns v1.1.6
github.com/miekg/dns
# github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d
github.com/nsf/termbox-go
# github.com/pkg/errors v0.8.1
github.com/pkg/errors
# github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af

Loading…
Cancel
Save