diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2022-03-06 17:40:35 +0100 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2022-03-06 17:40:35 +0100 |
commit | 29c72fb30bb7d5614c0a8ebb73bee2ac7eca6608 (patch) | |
tree | 8a58c288c11d6e3be5c4de1651d635776c8efb68 /examples | |
parent | 46f68501d575431656b5254a4bda8acb2982ab77 (diff) |
Removed go-dashboard example.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
Diffstat (limited to 'examples')
93 files changed, 0 insertions, 14115 deletions
diff --git a/examples/go-dashboard/main.go b/examples/go-dashboard/main.go deleted file mode 100644 index c4cad3fb1..000000000 --- a/examples/go-dashboard/main.go +++ /dev/null @@ -1,218 +0,0 @@ -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "io" - "log" - "net" - "os" - "strconv" - "strings" - - "ui" -) - -var ( - WarningLogger *log.Logger - InfoLogger *log.Logger - ErrorLogger *log.Logger - - NETWORK_BUFFER_MAX_SIZE uint16 = 13312 - NETWORK_BUFFER_LENGTH_DIGITS uint16 = 5 -) - -type packet_event struct { - ThreadID uint8 `json:"thread_id"` - PacketID uint64 `json:"packet_id"` - - FlowID uint32 `json:"flow_id"` - FlowPacketID uint64 `json:"flow_packet_id"` - - Timestamp uint64 `json:"ts_msec"` - - PacketEventID uint8 `json:"packet_event_id"` - PacketEventName string `json:"packet_event_name"` - PacketOversize bool `json:"pkt_oversize"` - PacketLength uint32 `json:"pkt_len"` - PacketL4Length uint32 `json:"pkt_l4_len"` - Packet string `json:"pkt"` - PacketCaptureLength uint32 `json:"pkt_caplen"` - PacketType uint32 `json:"pkt_type"` - PacketL3Offset uint32 `json:"pkt_l3_offset"` - PacketL4Offset uint32 `json:"pkt_l4_offset"` -} - -type flow_event struct { - ThreadID uint8 `json:"thread_id"` - PacketID uint64 `json:"packet_id"` - - FlowID uint32 `json:"flow_id"` - FlowPacketID uint64 `json:"flow_packets_processed"` - FlowFirstSeen uint64 `json:"flow_first_seen"` - FlowLastSeen uint64 `json:"flow_last_seen"` - FlowTotalLayer4DataLength uint64 `json:"flow_tot_l4_data_len"` - FlowMinLayer4DataLength uint64 `json:"flow_min_l4_data_len"` - FlowMaxLayer4DataLength uint64 `json:"flow_max_l4_data_len"` - FlowAvgLayer4DataLength uint64 `json:"flow_avg_l4_data_len"` - FlowDatalinkLayer uint8 `json:"flow_datalink"` - MaxPackets uint8 `json:"flow_max_packets"` - IsMidstreamFlow uint32 `json:"midstream"` -} - -type basic_event struct { - ThreadID uint8 `json:"thread_id"` - PacketID uint64 `json:"packet_id"` - - BasicEventID uint8 `json:"basic_event_id"` - BasicEventName string `json:"basic_event_name"` -} - -func processJson(jsonStr string) { - jsonMap := make(map[string]interface{}) - err := json.Unmarshal([]byte(jsonStr), &jsonMap) - if err != nil { - ErrorLogger.Printf("BUG: JSON error: %v\n", err) - os.Exit(1) - } - if jsonMap["packet_event_id"] != nil { - pe := packet_event{} - if err := json.Unmarshal([]byte(jsonStr), &pe); err != nil { - ErrorLogger.Printf("BUG: JSON Unmarshal error: %v\n", err) - os.Exit(1) - } - InfoLogger.Printf("PACKET EVENT %v\n", pe) - } else if jsonMap["flow_event_id"] != nil { - fe := flow_event{} - if err := json.Unmarshal([]byte(jsonStr), &fe); err != nil { - ErrorLogger.Printf("BUG: JSON Unmarshal error: %v\n", err) - os.Exit(1) - } - InfoLogger.Printf("FLOW EVENT %v\n", fe) - } else if jsonMap["basic_event_id"] != nil { - be := basic_event{} - if err := json.Unmarshal([]byte(jsonStr), &be); err != nil { - ErrorLogger.Printf("BUG: JSON Unmarshal error: %v\n", err) - os.Exit(1) - } - InfoLogger.Printf("BASIC EVENT %v\n", be) - } else { - ErrorLogger.Printf("BUG: Unknown JSON: %v\n", jsonStr) - os.Exit(1) - } - //InfoLogger.Printf("JSON map: %v\n-------------------------------------------------------\n", jsonMap) -} - -func eventHandler(ui *ui.Tui, wdgts *ui.Widgets, reader chan string) { - for { - select { - case <-ui.MainTicker.C: - if err := wdgts.RawJson.Write(fmt.Sprintf("%s\n", "--- HEARTBEAT ---")); err != nil { - panic(err) - } - - case <-ui.Context.Done(): - return - - case jsonStr := <-reader: - if err := wdgts.RawJson.Write(fmt.Sprintf("%s\n", jsonStr)); err != nil { - panic(err) - } - } - } -} - -func main() { - InfoLogger = log.New(os.Stderr, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) - WarningLogger = log.New(os.Stderr, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile) - ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) - - writer := make(chan string, 256) - - go func(writer chan string) { - con, err := net.Dial("tcp", "127.0.0.1:7000") - if err != nil { - ErrorLogger.Printf("Connection failed: %v\n", err) - os.Exit(1) - } - - buf := make([]byte, NETWORK_BUFFER_MAX_SIZE) - jsonStr := string("") - jsonStrLen := uint16(0) - jsonLen := uint16(0) - brd := bufio.NewReaderSize(con, int(NETWORK_BUFFER_MAX_SIZE)) - - for { - nread, err := brd.Read(buf) - - if err != nil { - if err != io.EOF { - ErrorLogger.Printf("Read Error: %v\n", err) - break - } - } - - if nread == 0 || err == io.EOF { - WarningLogger.Printf("Disconnect from Server\n") - break - } - - jsonStr += string(buf[:nread]) - jsonStrLen += uint16(nread) - - for { - if jsonStrLen < NETWORK_BUFFER_LENGTH_DIGITS+1 { - break - } - - if jsonStr[NETWORK_BUFFER_LENGTH_DIGITS] != '{' { - ErrorLogger.Printf("BUG: JSON invalid opening character at position %d: '%s' (%x)\n", - NETWORK_BUFFER_LENGTH_DIGITS, - string(jsonStr[:NETWORK_BUFFER_LENGTH_DIGITS]), jsonStr[NETWORK_BUFFER_LENGTH_DIGITS]) - os.Exit(1) - } - - if jsonLen == 0 { - var tmp uint64 - if tmp, err = strconv.ParseUint(strings.TrimLeft(jsonStr[:NETWORK_BUFFER_LENGTH_DIGITS], "0"), 10, 16); err != nil { - ErrorLogger.Printf("BUG: Could not parse length of a JSON string: %v\n", err) - os.Exit(1) - } else { - jsonLen = uint16(tmp) - } - } - - if jsonStrLen < jsonLen+NETWORK_BUFFER_LENGTH_DIGITS { - break - } - - if jsonStr[jsonLen+NETWORK_BUFFER_LENGTH_DIGITS-2] != '}' || jsonStr[jsonLen+NETWORK_BUFFER_LENGTH_DIGITS-1] != '\n' { - ErrorLogger.Printf("BUG: JSON invalid closing character at position %d: '%s'\n", - jsonLen+NETWORK_BUFFER_LENGTH_DIGITS, - string(jsonStr[jsonLen+NETWORK_BUFFER_LENGTH_DIGITS-1])) - os.Exit(1) - } - - writer <- jsonStr[NETWORK_BUFFER_LENGTH_DIGITS : NETWORK_BUFFER_LENGTH_DIGITS+jsonLen] - - jsonStr = jsonStr[jsonLen+NETWORK_BUFFER_LENGTH_DIGITS:] - jsonStrLen -= (jsonLen + NETWORK_BUFFER_LENGTH_DIGITS) - jsonLen = 0 - } - } - }(writer) - - tui, wdgts := ui.Init() - go eventHandler(tui, wdgts, writer) - ui.Run(tui) - -/* - for { - select { - case _ = <-writer: - break - } - } -*/ -} diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/.travis.yml b/examples/go-dashboard/src/github.com/mattn/go-runewidth/.travis.yml deleted file mode 100644 index 6a21813a3..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go -sudo: false -go: - - 1.13.x - - tip - -before_install: - - go get -t -v ./... - -script: - - go generate - - git diff --cached --exit-code - - ./go.test.sh - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/LICENSE b/examples/go-dashboard/src/github.com/mattn/go-runewidth/LICENSE deleted file mode 100644 index 91b5cef30..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -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. diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/README.md b/examples/go-dashboard/src/github.com/mattn/go-runewidth/README.md deleted file mode 100644 index aa56ab96c..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/README.md +++ /dev/null @@ -1,27 +0,0 @@ -go-runewidth -============ - -[](https://travis-ci.org/mattn/go-runewidth) -[](https://codecov.io/gh/mattn/go-runewidth) -[](http://godoc.org/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 diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/go.mod b/examples/go-dashboard/src/github.com/mattn/go-runewidth/go.mod deleted file mode 100644 index fa7f4d864..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/mattn/go-runewidth - -go 1.9 diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/go.test.sh b/examples/go-dashboard/src/github.com/mattn/go-runewidth/go.test.sh deleted file mode 100644 index 012162b07..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/go.test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -e -echo "" > coverage.txt - -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic "$d" - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth.go b/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth.go deleted file mode 100644 index 19f8e0449..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth.go +++ /dev/null @@ -1,257 +0,0 @@ -package runewidth - -import ( - "os" -) - -//go:generate go run script/generate.go - -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 { - 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}, -} - -// 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): - 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) { - if width < w { - width = w - } - } else { - 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) -} diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_appengine.go b/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_appengine.go deleted file mode 100644 index 7d99f6e52..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_appengine.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build appengine - -package runewidth - -// IsEastAsian return true if the current locale is CJK -func IsEastAsian() bool { - return false -} diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_js.go b/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_js.go deleted file mode 100644 index c5fdf40ba..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_js.go +++ /dev/null @@ -1,9 +0,0 @@ -// +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 -} diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_posix.go b/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_posix.go deleted file mode 100644 index 480ad7485..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_posix.go +++ /dev/null @@ -1,82 +0,0 @@ -// +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_ALL") - if locale == "" { - 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) -} diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_table.go b/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_table.go deleted file mode 100644 index b27d77d89..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_table.go +++ /dev/null @@ -1,437 +0,0 @@ -// Code generated by script/generate.go. DO NOT EDIT. - -package runewidth - -var combining = table{ - {0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3}, - {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0D00, 0x0D01}, - {0x135D, 0x135F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1AC0}, - {0x1B6B, 0x1B73}, {0x1DC0, 0x1DF9}, {0x1DFB, 0x1DFF}, - {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF}, - {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, - {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1}, - {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A}, - {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x11300, 0x11301}, - {0x1133B, 0x1133C}, {0x11366, 0x1136C}, {0x11370, 0x11374}, - {0x16AF0, 0x16AF4}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, - {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, - {0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, - {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, - {0x1E8D0, 0x1E8D6}, -} - -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, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31E3}, - {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x4DBF}, - {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, - {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, - {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, - {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, - {0x16FF0, 0x16FF1}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, - {0x18D00, 0x18D08}, {0x1B000, 0x1B11E}, {0x1B150, 0x1B152}, - {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, - {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, - {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, - {0x1F250, 0x1F251}, {0x1F260, 0x1F265}, {0x1F300, 0x1F320}, - {0x1F32D, 0x1F335}, {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, - {0x1F3A0, 0x1F3CA}, {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, - {0x1F3F4, 0x1F3F4}, {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, - {0x1F442, 0x1F4FC}, {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, - {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, - {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, - {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D7}, - {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, - {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F978}, - {0x1F97A, 0x1F9CB}, {0x1F9CD, 0x1F9FF}, {0x1FA70, 0x1FA74}, - {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAA8}, - {0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6}, - {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 notassigned = table{ - {0x27E6, 0x27ED}, {0x2985, 0x2986}, -} - -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, 0x058A}, {0x058D, 0x058F}, - {0x0591, 0x05C7}, {0x05D0, 0x05EA}, {0x05EF, 0x05F4}, - {0x0600, 0x061C}, {0x061E, 0x070D}, {0x070F, 0x074A}, - {0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D}, - {0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E}, - {0x0860, 0x086A}, {0x08A0, 0x08B4}, {0x08B6, 0x08C7}, - {0x08D3, 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, 0x09FE}, {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, 0x0A76}, - {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, 0x0AFF}, {0x0B01, 0x0B03}, - {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, - {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39}, - {0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, - {0x0B55, 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, 0x0C0C}, - {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, - {0x0C3D, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, - {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C63}, - {0x0C66, 0x0C6F}, {0x0C77, 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}, {0x0D00, 0x0D0C}, - {0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48}, - {0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, - {0x0D81, 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}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, - {0x0EA5, 0x0EA5}, {0x0EA7, 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, 0x1878}, {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, 0x1AC0}, {0x1B00, 0x1B4B}, {0x1B50, 0x1B7C}, - {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49}, - {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7}, - {0x1CD0, 0x1CFA}, {0x1D00, 0x1DF9}, {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, 0x20BF}, {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, 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}, {0x2B97, 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, 0x2E52}, {0x303F, 0x303F}, {0x4DC0, 0x4DFF}, - {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7BF}, - {0xA7C2, 0xA7CA}, {0xA7F5, 0xA82C}, {0xA830, 0xA839}, - {0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, - {0xA8E0, 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, 0xAB6B}, {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, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD}, - {0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB}, - {0x10300, 0x10323}, {0x1032D, 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, 0x10A35}, - {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {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, 0x10D27}, {0x10D30, 0x10D39}, {0x10E60, 0x10E7E}, - {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, {0x10EB0, 0x10EB1}, - {0x10F00, 0x10F27}, {0x10F30, 0x10F59}, {0x10FB0, 0x10FCB}, - {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x1106F}, - {0x1107F, 0x110C1}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8}, - {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11147}, - {0x11150, 0x11176}, {0x11180, 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}, {0x1133B, 0x11344}, {0x11347, 0x11348}, - {0x1134B, 0x1134D}, {0x11350, 0x11350}, {0x11357, 0x11357}, - {0x1135D, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, - {0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7}, - {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD}, - {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, - {0x11680, 0x116B8}, {0x116C0, 0x116C9}, {0x11700, 0x1171A}, - {0x1171D, 0x1172B}, {0x11730, 0x1173F}, {0x11800, 0x1183B}, - {0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x11909, 0x11909}, - {0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x11935}, - {0x11937, 0x11938}, {0x1193B, 0x11946}, {0x11950, 0x11959}, - {0x119A0, 0x119A7}, {0x119AA, 0x119D7}, {0x119DA, 0x119E4}, - {0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, {0x11AC0, 0x11AF8}, - {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45}, - {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7}, - {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09}, - {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, - {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65}, - {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91}, - {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8}, - {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, - {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, - {0x13000, 0x1342E}, {0x13430, 0x13438}, {0x14400, 0x14646}, - {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, - {0x16A6E, 0x16A6F}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, - {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, - {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A}, - {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, - {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, - {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1D000, 0x1D0F5}, - {0x1D100, 0x1D126}, {0x1D129, 0x1D1E8}, {0x1D200, 0x1D245}, - {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378}, - {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}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D}, - {0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E2C0, 0x1E2F9}, - {0x1E2FF, 0x1E2FF}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, - {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, - {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {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, 0x1F10F}, {0x1F12E, 0x1F12F}, - {0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {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}, {0x1F6D3, 0x1F6D4}, - {0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, - {0x1F780, 0x1F7D8}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, - {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, - {0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B}, - {0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D}, - {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9}, - {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, -} - -var emoji = table{ - {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122}, - {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA}, - {0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388}, - {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, - {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, - {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605}, - {0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705}, - {0x2708, 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, 0x2767}, {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}, - {0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F}, - {0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, - {0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F}, - {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, - {0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D}, - {0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F}, - {0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, - {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF}, - {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF}, - {0x1FC00, 0x1FFFD}, -} diff --git a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_windows.go b/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_windows.go deleted file mode 100644 index d6a61777d..000000000 --- a/examples/go-dashboard/src/github.com/mattn/go-runewidth/runewidth_windows.go +++ /dev/null @@ -1,28 +0,0 @@ -// +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 -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/.gitignore b/examples/go-dashboard/src/github.com/mum4k/termdash/.gitignore deleted file mode 100644 index 97e9bcbaa..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Exclude MacOS attribute files. -.DS_Store diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/.travis.yml b/examples/go-dashboard/src/github.com/mum4k/termdash/.travis.yml deleted file mode 100644 index 7c8b739a0..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: go -go: - - 1.14.x - - 1.15.x - - stable -script: - - go get -t ./... - - go get -u golang.org/x/lint/golint - - go test ./... - - CGO_ENABLED=1 go test -race ./... - - go vet ./... - - diff -u <(echo -n) <(gofmt -d -s .) - - diff -u <(echo -n) <(./internal/scripts/autogen_licences.sh .) - - diff -u <(echo -n) <(golint ./...) -env: - global: - - CGO_ENABLED=0 diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/CHANGELOG.md b/examples/go-dashboard/src/github.com/mum4k/termdash/CHANGELOG.md deleted file mode 100644 index 6889100b4..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/CHANGELOG.md +++ /dev/null @@ -1,361 +0,0 @@ -# Changelog - -All notable changes to this project are documented here. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [0.12.2] - 31-Aug-2020 - -### Fixed - -- advanced the CI Go versions up to Go 1.15. -- fixed the build status badge to correctly point to travis-ci.com instead of - travis-ci.org. - -## [0.12.1] - 20-Jun-2020 - -### Fixed - -- the `tcell` unit test can now pass in headless mode (when TERM="") which - happens under bazel. -- switching coveralls integration to Github application. - -## [0.12.0] - 10-Apr-2020 - -### Added - -- Migrating to [Go modules](https://blog.golang.org/using-go-modules). -- Renamed directory `internal` to `private` so that external widget development - is possible. Noted in - [README.md](https://github.com/mum4k/termdash/blob/master/README.md) that packages in the - `private` directory don't have any API stability guarantee. - -## [0.11.0] - 7-Mar-2020 - -#### Breaking API changes - -- Termdash now requires at least Go version 1.11. - -### Added - -- New [`tcell`](https://github.com/gdamore/tcell) based terminal implementation - which implements the `terminalapi.Terminal` interface. -- tcell implementation supports two initialization `Option`s: - - `ColorMode` the terminal color output mode (defaults to 256 color mode) - - `ClearStyle` the foreground and background color style to use when clearing - the screen (defaults to the global ColorDefault for both foreground and - background) - -### Fixed - -- Improved test coverage of the `Gauge` widget. - -## [0.10.0] - 5-Jun-2019 - -### Added - -- Added `time.Duration` based `ValueFormatter` for the `LineChart` Y-axis labels. -- Added round and suffix `ValueFormatter` for the `LineChart` Y-axis labels. -- Added decimal and suffix `ValueFormatter` for the `LineChart` Y-axis labels. -- Added a `container.SplitOption` that allows fixed size container splits. -- Added `grid` functions that allow fixed size rows and columns. - -### Changed - -- The `LineChart` can format the labels on the Y-axis with a `ValueFormatter`. -- The `SegmentDisplay` can now display dots and colons ('.' and ':'). -- The `Donut` widget now guarantees spacing between the donut and its label. -- The continuous build on Travis CI now builds with cgo explicitly disabled to - ensure both Termdash and its dependencies use pure Go. - -### Fixed - -- Lint issues found on the Go report card. -- An internal library belonging to the `Text` widget was incorrectly passing - `math.MaxUint32` as an int argument. - -## [0.9.1] - 15-May-2019 - -### Fixed - -- Termdash could deadlock when a `Button` or a `TextInput` was configured to - call the `Container.Update` method. - -## [0.9.0] - 28-Apr-2019 - -### Added - -- The `TextInput` widget, an input field allowing interactive text input. -- The `Donut` widget can now display an optional text label under the donut. - -### Changed - -- Widgets now get information whether their container is focused when Draw is - executed. -- The SegmentDisplay widget now has a method that returns the observed character - capacity the last time Draw was called. -- The grid.Builder API now allows users to specify options for intermediate - containers, i.e. containers that don't have widgets, but represent rows and - columns. -- Line chart widget now allows `math.NaN` values to represent "no value" (values - that will not be rendered) in the values slice. - -#### Breaking API changes - -- The widgetapi.Widget.Draw method now accepts a second argument which provides - widgets with additional metadata. This affects all implemented widgets. -- Termdash now requires at least Go version 1.10, which allows us to utilize - `math.Round` instead of our own implementation and `strings.Builder` instead - of `bytes.Buffer`. -- Terminal shortcuts like `Ctrl-A` no longer come as two separate events, - Termdash now mirrors termbox-go and sends these as one event. - -## [0.8.0] - 30-Mar-2019 - -### Added - -- New API for building layouts, a grid.Builder. Allows defining the layout - iteratively as repetitive Elements, Rows and Columns. -- Containers now support margin around them and padding of their content. -- Container now supports dynamic layout changes via the new Update method. - -### Changed - -- The Text widget now supports content wrapping on word boundaries. -- The BarChart and SparkLine widgets now have a method that returns the - observed value capacity the last time Draw was called. -- Moving widgetapi out of the internal directory to allow external users to - develop their own widgets. -- Event delivery to widgets now has a stable defined order and happens when the - container is unlocked so that widgets can trigger dynamic layout changes. - -### Fixed - -- The termdash_test now correctly waits until all subscribers processed events, - not just received them. -- Container focus tracker now correctly tracks focus changes in enlarged areas, - i.e. when the terminal size increased. -- The BarChart, LineChart and SegmentDisplay widgets now protect against - external mutation of the values passed into them by copying the data they - receive. - -## [0.7.2] - 25-Feb-2019 - -### Added - -- Test coverage for data only packages. - -### Changed - -- Refactoring packages that contained a mix of public and internal identifiers. - -#### Breaking API changes - -The following packages were refactored, no impact is expected as the removed -identifiers shouldn't be used externally. - -- Functions align.Text and align.Rectangle were moved to a new - internal/alignfor package. -- Types cell.Cell and cell.Buffer were moved into a new internal/canvas/buffer - package. - -## [0.7.1] - 24-Feb-2019 - -### Fixed - -- Some of the packages that were moved into internal are required externally. - This release makes them available again. - -### Changed - -#### Breaking API changes - -- The draw.LineStyle enum was refactored into its own package - linestyle.LineStyle. Users will have to replace: - - - draw.LineStyleNone -> linestyle.None - - draw.LineStyleLight -> linestyle.Light - - draw.LineStyleDouble -> linestyle.Double - - draw.LineStyleRound -> linestyle.Round - -## [0.7.0] - 24-Feb-2019 - -### Added - -#### New widgets - -- The Button widget. - -#### Improvements to documentation - -- Clearly marked the public API surface by moving private packages into - internal directory. -- Started a GitHub wiki for Termdash. - -#### Improvements to the LineChart widget - -- The LineChart widget can display X axis labels in vertical orientation. -- The LineChart widget allows the user to specify a custom scale for the Y - axis. -- The LineChart widget now has an option that disables scaling of the X axis. - Useful for applications that want to continuously feed data and make them - "roll" through the linechart. -- The LineChart widget now has a method that returns the observed capacity of - the LineChart the last time Draw was called. -- The LineChart widget now supports zoom of the content triggered by mouse - events. - -#### Improvements to the Text widget - -- The Text widget now has a Write option that atomically replaces the entire - text content. - -#### Improvements to the infrastructure - -- A function that draws text vertically. -- A non-blocking event distribution system that can throttle repetitive events. -- Generalized mouse button FSM for use in widgets that need to track mouse - button clicks. - -### Changed - -- Termbox is now initialized in 256 color mode by default. -- The infrastructure now uses the non-blocking event distribution system to - distribute events to subscribers. Each widget is now an individual - subscriber. -- The infrastructure now throttles event driven screen redraw rather than - redrawing for each input event. -- Widgets can now specify the scope at which they want to receive keyboard and - mouse events. - -#### Breaking API changes - -##### High impact - -- The constructors of all the widgets now also return an error so that they - can validate the options. This is a breaking change for the following - widgets: BarChart, Gauge, LineChart, SparkLine, Text. The callers will have - to handle the returned error. - -##### Low impact - -- The container package no longer exports separate methods to receive Keyboard - and Mouse events which were replaced by a Subscribe method for the event - distribution system. This shouldn't affect users as the removed methods - aren't needed by container users. -- The widgetapi.Options struct now uses an enum instead of a boolean when - widget specifies if it wants keyboard or mouse events. This only impacts - development of new widgets. - -### Fixed - -- The LineChart widget now correctly determines the Y axis scale when multiple - series are provided. -- Lint issues in the codebase, and updated Travis configuration so that golint - is executed on every run. -- Termdash now correctly starts in locales like zh_CN.UTF-8 where some of the - characters it uses internally can have ambiguous width. - -## [0.6.1] - 12-Feb-2019 - -### Fixed - -- The LineChart widget now correctly places custom labels. - -## [0.6.0] - 07-Feb-2019 - -### Added - -- The SegmentDisplay widget. -- A CHANGELOG. -- New line styles for borders. - -### Changed - -- Better recordings of the individual demos. - -### Fixed - -- The LineChart now has an option to change the behavior of the Y axis from - zero anchored to adaptive. -- Lint errors reported on the Go report card. -- Widgets now correctly handle a race when new user data are supplied between - calls to their Options() and Draw() methods. - -## [0.5.0] - 21-Jan-2019 - -### Added - -- Draw primitives for drawing circles. -- The Donut widget. - -### Fixed - -- Bugfixes in the braille canvas. -- Lint errors reported on the Go report card. -- Flaky behavior in termdash_test. - -## [0.4.0] - 15-Jan-2019 - -### Added - -- 256 color support. -- Variable size container splits. -- A more complete demo of the functionality. - -### Changed - -- Updated documentation and README. - -## [0.3.0] - 13-Jan-2019 - -### Added - -- Primitives for drawing lines. -- Implementation of a Braille canvas. -- The LineChart widget. - -## [0.2.0] - 02-Jul-2018 - -### Added - -- The SparkLine widget. -- The BarChart widget. -- Manually triggered redraw. -- Travis now checks for presence of licence headers. - -### Fixed - -- Fixing races in termdash_test. - -## 0.1.0 - 13-Jun-2018 - -### Added - -- Documentation of the project and its goals. -- Drawing infrastructure. -- Testing infrastructure. -- The Gauge widget. -- The Text widget. - -[unreleased]: https://github.com/mum4k/termdash/compare/v0.12.2...devel -[0.12.2]: https://github.com/mum4k/termdash/compare/v0.12.1...v0.12.2 -[0.12.1]: https://github.com/mum4k/termdash/compare/v0.12.0...v0.12.1 -[0.12.0]: https://github.com/mum4k/termdash/compare/v0.11.0...v0.12.0 -[0.11.0]: https://github.com/mum4k/termdash/compare/v0.10.0...v0.11.0 -[0.10.0]: https://github.com/mum4k/termdash/compare/v0.9.1...v0.10.0 -[0.9.1]: https://github.com/mum4k/termdash/compare/v0.9.0...v0.9.1 -[0.9.0]: https://github.com/mum4k/termdash/compare/v0.8.0...v0.9.0 -[0.8.0]: https://github.com/mum4k/termdash/compare/v0.7.2...v0.8.0 -[0.7.2]: https://github.com/mum4k/termdash/compare/v0.7.1...v0.7.2 -[0.7.1]: https://github.com/mum4k/termdash/compare/v0.7.0...v0.7.1 -[0.7.0]: https://github.com/mum4k/termdash/compare/v0.6.1...v0.7.0 -[0.6.1]: https://github.com/mum4k/termdash/compare/v0.6.0...v0.6.1 -[0.6.0]: https://github.com/mum4k/termdash/compare/v0.5.0...v0.6.0 -[0.5.0]: https://github.com/mum4k/termdash/compare/v0.4.0...v0.5.0 -[0.4.0]: https://github.com/mum4k/termdash/compare/v0.3.0...v0.4.0 -[0.3.0]: https://github.com/mum4k/termdash/compare/v0.2.0...v0.3.0 -[0.2.0]: https://github.com/mum4k/termdash/compare/v0.1.0...v0.2.0 diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/CONTRIBUTING.md b/examples/go-dashboard/src/github.com/mum4k/termdash/CONTRIBUTING.md deleted file mode 100644 index 9f2027288..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/CONTRIBUTING.md +++ /dev/null @@ -1,38 +0,0 @@ -# How to Contribute - -We'd love to accept your patches and contributions to this project. There are -just a few small guidelines you need to follow. - -## Fork and merge into the "devel" branch - -All development in termdash repository must happen in the [devel -branch](https://github.com/mum4k/termdash/tree/devel). The devel branch is -merged into the master branch during release of each new version. - -When you fork the termdash repository, be sure to checkout the devel branch. -When you are creating a pull request, be sure to pull back into the devel -branch. - -## Contributor License Agreement - -Contributions to this project must be accompanied by a Contributor License -Agreement. You (or your employer) retain the copyright to your contribution; -this simply gives us permission to use and redistribute your contributions as -part of the project. Head over to <https://cla.developers.google.com/> to see -your current agreements on file or to sign a new one. - -You generally only need to submit a CLA once, so if you've already submitted one -(even if it was for a different project), you probably don't need to do it -again. - -## Code reviews - -All submissions, including submissions by project members, require review. We -use GitHub pull requests for this purpose. Consult -[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more -information on using pull requests. - -## Community Guidelines - -This project follows [Google's Open Source Community -Guidelines](https://opensource.google.com/conduct/). diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/LICENSE b/examples/go-dashboard/src/github.com/mum4k/termdash/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/README.md b/examples/go-dashboard/src/github.com/mum4k/termdash/README.md deleted file mode 100644 index 8ecd1fd53..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/README.md +++ /dev/null @@ -1,215 +0,0 @@ -[](https://godoc.org/github.com/mum4k/termdash) -[](https://travis-ci.com/mum4k/termdash) -[](https://sourcegraph.com/github.com/mum4k/termdash?badge) -[](https://coveralls.io/github/mum4k/termdash?branch=master) -[](https://goreportcard.com/report/github.com/mum4k/termdash) -[](https://github.com/mum4k/termdash/blob/master/LICENSE) -[](https://github.com/avelino/awesome-go) - -# [<img src="./doc/images/termdash.png" alt="termdashlogo" type="image/png" width="30%">](http://github.com/mum4k/termdash/wiki) - -Termdash is a cross-platform customizable terminal based dashboard. - -[<img src="./doc/images/termdashdemo_0_9_0.gif" alt="termdashdemo" type="image/gif">](termdashdemo/termdashdemo.go) - -The feature set is inspired by the -[gizak/termui](http://github.com/gizak/termui) project, which in turn was -inspired by -[yaronn/blessed-contrib](http://github.com/yaronn/blessed-contrib). - -This rewrite focuses on code readability, maintainability and testability, see -the [design goals](doc/design_goals.md). It aims to achieve the following -[requirements](doc/requirements.md). See the [high-level design](doc/hld.md) -for more details. - -# Public API and status - -The public API surface is documented in the -[wiki](http://github.com/mum4k/termdash/wiki). - -Private packages can be identified by the presence of the **/private/** -directory in their import path. Stability of the private packages isn't -guaranteed and changes won't be backward compatible. - -There might still be breaking changes to the public API, at least until the -project reaches version 1.0.0. Any breaking changes will be published in the -[changelog](CHANGELOG.md). - -# Current feature set - -- Full support for terminal window resizing throughout the infrastructure. -- Customizable layout, widget placement, borders, margins, padding, colors, etc. -- Dynamic layout changes at runtime. -- Binary tree and Grid forms of setting up the layout. -- Focusable containers and widgets. -- Processing of keyboard and mouse events. -- Periodic and event driven screen redraw. -- A library of widgets, see below. -- UTF-8 for all text elements. -- Drawing primitives (Go functions) for widget development with character and - sub-character resolution. - -# Installation - -To install this library, run the following: - -```go -go get -u github.com/mum4k/termdash -``` - -# Usage - -The usage of most of these elements is demonstrated in -[termdashdemo.go](termdashdemo/termdashdemo.go). To execute the demo: - -```go -go run github.com/mum4k/termdash/termdashdemo/termdashdemo.go -``` - -# Documentation - -Please refer to the [Termdash wiki](http://github.com/mum4k/termdash/wiki) for -all documentation and resources. - -# Implemented Widgets - -## The Button - -Allows users to interact with the application, each button press runs a callback function. -Run the -[buttondemo](widgets/button/buttondemo/buttondemo.go). - -```go -go run github.com/mum4k/termdash/widgets/button/buttondemo/buttondemo.go -``` - -[<img src="./doc/images/buttondemo.gif" alt="buttondemo" type="image/gif" width="50%">](widgets/button/buttondemo/buttondemo.go) - -## The TextInput - -Allows users to interact with the application by entering, editing and -submitting text data. Run the -[textinputdemo](widgets/textinput/textinputdemo/textinputdemo.go). - -```go -go run github.com/mum4k/termdash/widgets/textinput/textinputdemo/textinputdemo.go -``` - -[<img src="./doc/images/textinputdemo.gif" alt="textinputdemo" type="image/gif" width="80%">](widgets/textinput/textinputdemo/textinputdemo.go) - -## The Gauge - -Displays the progress of an operation. Run the -[gaugedemo](widgets/gauge/gaugedemo/gaugedemo.go). - -```go -go run github.com/mum4k/termdash/widgets/gauge/gaugedemo/gaugedemo.go -``` - -[<img src="./doc/images/gaugedemo.gif" alt="gaugedemo" type="image/gif">](widgets/gauge/gaugedemo/gaugedemo.go) - -## The Donut - -Visualizes progress of an operation as a partial or a complete donut. Run the -[donutdemo](widgets/donut/donutdemo/donutdemo.go). - -```go -go run github.com/mum4k/termdash/widgets/donut/donutdemo/donutdemo.go -``` - -[<img src="./doc/images/donutdemo.gif" alt="donutdemo" type="image/gif">](widgets/donut/donutdemo/donutdemo.go) - -## The Text - -Displays text content, supports trimming and scrolling of content. Run the -[textdemo](widgets/text/textdemo/textdemo.go). - -```go -go run github.com/mum4k/termdash/widgets/text/textdemo/textdemo.go -``` - -[<img src="./doc/images/textdemo.gif" alt="textdemo" type="image/gif">](widgets/text/textdemo/textdemo.go) - -## The SparkLine - -Draws a graph showing a series of values as vertical bars. The bars can have -sub-cell height. Run the -[sparklinedemo](widgets/sparkline/sparklinedemo/sparklinedemo.go). - -```go -go run github.com/mum4k/termdash/widgets/sparkline/sparklinedemo/sparklinedemo.go -``` - -[<img src="./doc/images/sparklinedemo.gif" alt="sparklinedemo" type="image/gif" width="50%">](widgets/sparkline/sparklinedemo/sparklinedemo.go) - -## The BarChart - -Displays multiple bars showing relative ratios of values. Run the -[barchartdemo](widgets/barchart/barchartdemo/barchartdemo.go). - -```go -go run github.com/mum4k/termdash/widgets/barchart/barchartdemo/barchartdemo.go -``` - -[<img src="./doc/images/barchartdemo.gif" alt="barchartdemo" type="image/gif" width="50%">](widgets/barchart/barchartdemo/barchartdemo.go) - -## The LineChart - -Displays series of values on a line chart, supports zoom triggered by mouse -events. Run the -[linechartdemo](widgets/linechart/linechartdemo/linechartdemo.go). - -```go -go run github.com/mum4k/termdash/widgets/linechart/linechartdemo/linechartdemo.go -``` - -[<img src="./doc/images/linechartdemo.gif" alt="linechartdemo" type="image/gif" width="70%">](widgets/linechart/linechartdemo/linechartdemo.go) - -## The SegmentDisplay - -Displays text by simulating a 16-segment display. Run the -[segmentdisplaydemo](widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go). - -```go -go run github.com/mum4k/termdash/widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go -``` - -[<img src="./doc/images/segmentdisplaydemo.gif" alt="segmentdisplaydemo" type="image/gif">](widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go) - -# Contributing - -If you are willing to contribute, improve the infrastructure or develop a -widget, first of all Thank You! Your help is appreciated. - -Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines related -to the Google's CLA, and code review requirements. - -As stated above the primary goal of this project is to develop readable, well -designed code, the functionality and efficiency come second. This is achieved -through detailed code reviews, design discussions and following of the [design -guidelines](doc/design_guidelines.md). Please familiarize yourself with these -before contributing. - -If you're developing a new widget, please see the [widget -development](doc/widget_development.md) section. - -Termdash uses [this branching model](https://nvie.com/posts/a-successful-git-branching-model/). When you fork the repository, base your changes off the [devel](https://github.com/mum4k/termdash/tree/devel) branch and the pull request should merge it back to the devel branch. Commits to the master branch are limited to releases, major bug fixes and documentation updates. - -# Similar projects in Go - -- [clui](https://github.com/VladimirMarkelov/clui) -- [gocui](https://github.com/jroimartin/gocui) -- [gowid](https://github.com/gcla/gowid) -- [termui](https://github.com/gizak/termui) -- [tui-go](https://github.com/marcusolsson/tui-go) -- [tview](https://github.com/rivo/tview) - -# Projects using Termdash - -- [datadash](https://github.com/keithknott26/datadash): Visualize streaming or tabular data inside the terminal. -- [grafterm](https://github.com/slok/grafterm): Metrics dashboards visualization on the terminal. -- [perfstat](https://github.com/flaviostutz/perfstat): Analyze and show tips about possible bottlenecks in Linux systems. - -# Disclaimer - -This is not an official Google product. diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/align/align.go b/examples/go-dashboard/src/github.com/mum4k/termdash/align/align.go deleted file mode 100644 index da4087f57..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/align/align.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package align defines constants representing types of alignment. -package align - -// Horizontal indicates the type of horizontal alignment. -type Horizontal int - -// String implements fmt.Stringer() -func (h Horizontal) String() string { - if n, ok := horizontalNames[h]; ok { - return n - } - return "HorizontalUnknown" -} - -// horizontalNames maps Horizontal values to human readable names. -var horizontalNames = map[Horizontal]string{ - HorizontalLeft: "HorizontalLeft", - HorizontalCenter: "HorizontalCenter", - HorizontalRight: "HorizontalRight", -} - -const ( - // HorizontalLeft is left alignment along the horizontal axis. - HorizontalLeft Horizontal = iota - // HorizontalCenter is center alignment along the horizontal axis. - HorizontalCenter - // HorizontalRight is right alignment along the horizontal axis. - HorizontalRight -) - -// Vertical indicates the type of vertical alignment. -type Vertical int - -// String implements fmt.Stringer() -func (v Vertical) String() string { - if n, ok := verticalNames[v]; ok { - return n - } - return "VerticalUnknown" -} - -// verticalNames maps Vertical values to human readable names. -var verticalNames = map[Vertical]string{ - VerticalTop: "VerticalTop", - VerticalMiddle: "VerticalMiddle", - VerticalBottom: "VerticalBottom", -} - -const ( - // VerticalTop is top alignment along the vertical axis. - VerticalTop Vertical = iota - // VerticalMiddle is middle alignment along the vertical axis. - VerticalMiddle - // VerticalBottom is bottom alignment along the vertical axis. - VerticalBottom -) diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/cell/cell.go b/examples/go-dashboard/src/github.com/mum4k/termdash/cell/cell.go deleted file mode 100644 index c3eb6df24..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/cell/cell.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package cell implements cell options and attributes. -package cell - -// Option is used to provide options for cells on a 2-D terminal. -type Option interface { - // Set sets the provided option. - Set(*Options) -} - -// Options stores the provided options. -type Options struct { - FgColor Color - BgColor Color -} - -// Set allows existing options to be passed as an option. -func (o *Options) Set(other *Options) { - *other = *o -} - -// NewOptions returns a new Options instance after applying the provided options. -func NewOptions(opts ...Option) *Options { - o := &Options{} - for _, opt := range opts { - opt.Set(o) - } - return o -} - -// option implements Option. -type option func(*Options) - -// Set implements Option.set. -func (co option) Set(opts *Options) { - co(opts) -} - -// FgColor sets the foreground color of the cell. -func FgColor(color Color) Option { - return option(func(co *Options) { - co.FgColor = color - }) -} - -// BgColor sets the background color of the cell. -func BgColor(color Color) Option { - return option(func(co *Options) { - co.BgColor = color - }) -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/cell/color.go b/examples/go-dashboard/src/github.com/mum4k/termdash/cell/color.go deleted file mode 100644 index 94560b74c..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/cell/color.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cell - -import ( - "fmt" -) - -// color.go defines constants for cell colors. - -// Color is the color of a cell. -type Color int - -// String implements fmt.Stringer() -func (cc Color) String() string { - if n, ok := colorNames[cc]; ok { - return n - } - return fmt.Sprintf("Color:%d", cc) -} - -// colorNames maps Color values to human readable names. -var colorNames = map[Color]string{ - ColorDefault: "ColorDefault", - ColorBlack: "ColorBlack", - ColorRed: "ColorRed", - ColorGreen: "ColorGreen", - ColorYellow: "ColorYellow", - ColorBlue: "ColorBlue", - ColorMagenta: "ColorMagenta", - ColorCyan: "ColorCyan", - ColorWhite: "ColorWhite", -} - -// The supported terminal colors. -const ( - ColorDefault Color = iota - - // 8 "system" colors. - ColorBlack - ColorRed - ColorGreen - ColorYellow - ColorBlue - ColorMagenta - ColorCyan - ColorWhite -) - -// ColorNumber sets a color using its number. -// Make sure your terminal is set to a terminalapi.ColorMode that supports the -// target color. The provided value must be in the range 0-255. -// Larger or smaller values will be reset to the default color. -// -// For reference on these colors see the Xterm number in: -// https://jonasjacek.github.io/colors/ -func ColorNumber(n int) Color { - if n < 0 || n > 255 { - return ColorDefault - } - return Color(n + 1) // Colors are off-by-one due to ColorDefault being zero. -} - -// ColorRGB6 sets a color using the 6x6x6 terminal color. -// Make sure your terminal is set to the terminalapi.ColorMode256 mode. -// The provided values (r, g, b) must be in the range 0-5. -// Larger or smaller values will be reset to the default color. -// -// For reference on these colors see: -// https://superuser.com/questions/783656/whats-the-deal-with-terminal-colors -func ColorRGB6(r, g, b int) Color { - for _, c := range []int{r, g, b} { - if c < 0 || c > 5 { - return ColorDefault - } - } - return Color(0x10 + 36*r + 6*g + b + 1) // Colors are off-by-one due to ColorDefault being zero. -} - -// ColorRGB24 sets a color using the 24 bit web color scheme. -// Make sure your terminal is set to the terminalapi.ColorMode256 mode. -// The provided values (r, g, b) must be in the range 0-255. -// Larger or smaller values will be reset to the default color. -// -// For reference on these colors see the RGB column in: -// https://jonasjacek.github.io/colors/ -func ColorRGB24(r, g, b int) Color { - for _, c := range []int{r, g, b} { - if c < 0 || c > 255 { - return ColorDefault - } - } - return ColorRGB6(r/51, g/51, b/51) -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/container.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/container.go deleted file mode 100644 index 54cef7856..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/container.go +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package container defines a type that wraps other containers or widgets. - -The container supports splitting container into sub containers, defining -container styles and placing widgets. The container also creates and manages -canvases assigned to the placed widgets. -*/ -package container - -import ( - "errors" - "fmt" - "image" - "sync" - - "github.com/mum4k/termdash/linestyle" - "github.com/mum4k/termdash/private/alignfor" - "github.com/mum4k/termdash/private/area" - "github.com/mum4k/termdash/private/event" - "github.com/mum4k/termdash/terminal/terminalapi" - "github.com/mum4k/termdash/widgetapi" -) - -// Container wraps either sub containers or widgets and positions them on the -// terminal. -// This is thread-safe. -type Container struct { - // parent is the parent container, nil if this is the root container. - parent *Container - // The sub containers, if these aren't nil, the widget must be. - first *Container - second *Container - - // term is the terminal this container is placed on. - // All containers in the tree share the same terminal. - term terminalapi.Terminal - - // focusTracker tracks the active (focused) container. - // All containers in the tree share the same tracker. - focusTracker *focusTracker - - // area is the area of the terminal this container has access to. - // Initialized the first time Draw is called. - area image.Rectangle - - // opts are the options provided to the container. - opts *options - - // clearNeeded indicates if the terminal needs to be cleared next time we - // are clearNeeded the container. - // This is required if the container was updated and thus the layout might - // have changed. - clearNeeded bool - - // mu protects the container tree. - // All containers in the tree share the same lock. - mu *sync.Mutex -} - -// String represents the container metadata in a human readable format. -// Implements fmt.Stringer. -func (c *Container) String() string { - return fmt.Sprintf("Container@%p{parent:%p, first:%p, second:%p, area:%+v}", c, c.parent, c.first, c.second, c.area) -} - -// New returns a new root container that will use the provided terminal and -// applies the provided options. -func New(t terminalapi.Terminal, opts ...Option) (*Container, error) { - root := &Container{ - term: t, - opts: newOptions( /* parent = */ nil), - mu: &sync.Mutex{}, - } - - // Initially the root is focused. - root.focusTracker = newFocusTracker(root) - if err := applyOptions(root, opts...); err != nil { - return nil, err - } - if err := validateOptions(root); err != nil { - return nil, err - } - return root, nil -} - -// newChild creates a new child container of the given parent. -func newChild(parent *Container, opts []Option) (*Container, error) { - child := &Container{ - parent: parent, - term: parent.term, - focusTracker: parent.focusTracker, - opts: newOptions(parent.opts), - mu: parent.mu, - } - if err := applyOptions(child, opts...); err != nil { - return nil, err - } - return child, nil -} - -// hasBorder determines if this container has a border. -func (c *Container) hasBorder() bool { - return c.opts.border != linestyle.None -} - -// hasWidget determines if this container has a widget. -func (c *Container) hasWidget() bool { - return c.opts.widget != nil -} - -// usable returns the usable area in this container. -// This depends on whether the container has a border, etc. -func (c *Container) usable() image.Rectangle { - if c.hasBorder() { - return area.ExcludeBorder(c.area) - } - return c.area -} - -// widgetArea returns the area in the container that is available for the -// widget's canvas. Takes the container border, widget's requested maximum size -// and ratio and container's alignment into account. -// Returns a zero area if the container has no widget. -func (c *Container) widgetArea() (image.Rectangle, error) { - if !c.hasWidget() { - return image.ZR, nil - } - - padded, err := c.opts.padding.apply(c.usable()) - if err != nil { - return image.ZR, err - } - wOpts := c.opts.widget.Options() - - adjusted := padded - if maxX := wOpts.MaximumSize.X; maxX > 0 && adjusted.Dx() > maxX { - adjusted.Max.X -= adjusted.Dx() - maxX - } - if maxY := wOpts.MaximumSize.Y; maxY > 0 && adjusted.Dy() > maxY { - adjusted.Max.Y -= adjusted.Dy() - maxY - } - - if wOpts.Ratio.X > 0 && wOpts.Ratio.Y > 0 { - adjusted = area.WithRatio(adjusted, wOpts.Ratio) - } - aligned, err := alignfor.Rectangle(padded, adjusted, c.opts.hAlign, c.opts.vAlign) - if err != nil { - return image.ZR, err - } - return aligned, nil -} - -// split splits the container's usable area into child areas. -// Panics if the container isn't configured for a split. -func (c *Container) split() (image.Rectangle, image.Rectangle, error) { - ar, err := c.opts.padding.apply(c.usable()) - if err != nil { - return image.ZR, image.ZR, err - } - if c.opts.splitFixed > DefaultSplitFixed { - if c.opts.split == splitTypeVertical { - return area.VSplitCells(ar, c.opts.splitFixed) - } - return area.HSplitCells(ar, c.opts.splitFixed) - } - - if c.opts.split == splitTypeVertical { - return area.VSplit(ar, c.opts.splitPercent) - } - return area.HSplit(ar, c.opts.splitPercent) -} - -// createFirst creates and returns the first sub container of this container. -func (c *Container) createFirst(opts []Option) error { - first, err := newChild(c, opts) - if err != nil { - return err - } - c.first = first - return nil -} - -// createSecond creates and returns the second sub container of this container. -func (c *Container) createSecond(opts []Option) error { - second, err := newChild(c, opts) - if err != nil { - return err - } - c.second = second - return nil -} - -// Draw draws this container and all of its sub containers. -func (c *Container) Draw() error { - c.mu.Lock() - defer c.mu.Unlock() - - if c.clearNeeded { - if err := c.term.Clear(); err != nil { - return fmt.Errorf("term.Clear => error: %v", err) - } - c.clearNeeded = false - } - - // Update the area we are tracking for focus in case the terminal size - // changed. - ar, err := area.FromSize(c.term.Size()) - if err != nil { - return err - } - c.focusTracker.updateArea(ar) - return drawTree(c) -} - -// Update updates container with the specified id by setting the provided -// options. This can be used to perform dynamic layout changes, i.e. anything -// between replacing the widget in the container and completely changing the -// layout and splits. -// The argument id must match exactly one container with that was created with -// matching ID() option. The argument id must not be an empty string. -func (c *Container) Update(id string, opts ...Option) error { - c.mu.Lock() - defer c.mu.Unlock() - - target, err := findID(c, id) - if err != nil { - return err - } - c.clearNeeded = true - - if err := applyOptions(target, opts...); err != nil { - return err - } - if err := validateOptions(c); err != nil { - return err - } - - // The currently focused container might not be reachable anymore, because - // it was under the target. If that is so, move the focus up to the target. - if !c.focusTracker.reachableFrom(c) { - c.focusTracker.setActive(target) - } - return nil -} - -// updateFocus processes the mouse event and determines if it changes the -// focused container. -// Caller must hold c.mu. -func (c *Container) updateFocus(m *terminalapi.Mouse) { - target := pointCont(c, m.Position) - if target == nil { // Ignore mouse clicks where no containers are. - return - } - c.focusTracker.mouse(target, m) -} - -// processEvent processes events delivered to the container. -func (c *Container) processEvent(ev terminalapi.Event) error { - // This is done in two stages. - // 1) under lock we traverse the container and identify all targets - // (widgets) that should receive the event. - // 2) lock is released and events are delivered to the widgets. Widgets - // themselves are thread-safe. Lock must be releases when delivering, - // because some widgets might try to mutate the container when they - // receive the event, like dynamically change the layout. - c.mu.Lock() - sendFn, err := c.prepareEvTargets(ev) - c.mu.Unlock() - if err != nil { - return err - } - return sendFn() -} - -// prepareEvTargets returns a closure, that when called delivers the event to -// widgets that registered for it. -// Also processes the event on behalf of the container (tracks keyboard focus). -// Caller must hold c.mu. -func (c *Container) prepareEvTargets(ev terminalapi.Event) (func() error, error) { - switch e := ev.(type) { - case *terminalapi.Mouse: - c.updateFocus(ev.(*terminalapi.Mouse)) - - targets, err := c.mouseEvTargets(e) - if err != nil { - return nil, err - } - return func() error { - for _, mt := range targets { - if err := mt.widget.Mouse(mt.ev); err != nil { - return err - } - } - return nil - }, nil - - case *terminalapi.Keyboard: - targets := c.keyEvTargets() - return func() error { - for _, w := range targets { - if err := w.Keyboard(e); err != nil { - return err - } - } - return nil - }, nil - - default: - return nil, fmt.Errorf("container received an unsupported event type %T", ev) - } -} - -// keyEvTargets returns those widgets found in the container that should -// receive this keyboard event. -// Caller must hold c.mu. -func (c *Container) keyEvTargets() []widgetapi.Widget { - var ( - errStr string - widgets []widgetapi.Widget - ) - - // All the widgets that should receive this event. - // For now stable ordering (preOrder). - preOrder(c, &errStr, visitFunc(func(cur *Container) error { - if !cur.hasWidget() { - return nil - } - - wOpt := cur.opts.widget.Options() - switch wOpt.WantKeyboard { - case widgetapi.KeyScopeNone: - // Widget doesn't want any keyboard events. - return nil - - case widgetapi.KeyScopeFocused: - if cur.focusTracker.isActive(cur) { - widgets = append(widgets, cur.opts.widget) - } - - case widgetapi.KeyScopeGlobal: - widgets = append(widgets, cur.opts.widget) - } - return nil - })) - return widgets -} - -// mouseEvTarget contains a mouse event adjusted relative to the widget's area -// and the widget that should receive it. -type mouseEvTarget struct { - // widget is the widget that should receive the mouse event. - widget widgetapi.Widget - // ev is the adjusted mouse event. - ev *terminalapi.Mouse -} - -// newMouseEvTarget returns a new newMouseEvTarget. -func newMouseEvTarget(w widgetapi.Widget, wArea image.Rectangle, ev *terminalapi.Mouse) *mouseEvTarget { - return &mouseEvTarget{ - widget: w, - ev: adjustMouseEv(ev, wArea), - } -} - -// mouseEvTargets returns those widgets found in the container that should -// receive this mouse event. -// Caller must hold c.mu. -func (c *Container) mouseEvTargets(m *terminalapi.Mouse) ([]*mouseEvTarget, error) { - var ( - errStr string - widgets []*mouseEvTarget - ) - - // All the widgets that should receive this event. - // For now stable ordering (preOrder). - preOrder(c, &errStr, visitFunc(func(cur *Container) error { - if !cur.hasWidget() { - return nil - } - - wOpts := cur.opts.widget.Options() - wa, err := cur.widgetArea() - if err != nil { - return err - } - - switch wOpts.WantMouse { - case widgetapi.MouseScopeNone: - // Widget doesn't want any mouse events. - return nil - - case widgetapi.MouseScopeWidget: - // Only if the event falls inside of the widget's canvas. - if m.Position.In(wa) { - widgets = append(widgets, newMouseEvTarget(cur.opts.widget, wa, m)) - } - - case widgetapi.MouseScopeContainer: - // Only if the event falls inside the widget's parent container. - if m.Position.In(cur.area) { - widgets = append(widgets, newMouseEvTarget(cur.opts.widget, wa, m)) - } - - case widgetapi.MouseScopeGlobal: - // Widget wants all mouse events. - widgets = append(widgets, newMouseEvTarget(cur.opts.widget, wa, m)) - } - return nil - })) - - if errStr != "" { - return nil, errors.New(errStr) - } - return widgets, nil -} - -// Subscribe tells the container to subscribe itself and widgets to the -// provided event distribution system. -// This method is private to termdash, stability isn't guaranteed and changes -// won't be backward compatible. -func (c *Container) Subscribe(eds *event.DistributionSystem) { - c.mu.Lock() - defer c.mu.Unlock() - - // maxReps is the maximum number of repetitive events towards widgets - // before we throttle them. - const maxReps = 10 - - // Subscriber the container itself in order to track keyboard focus. - want := []terminalapi.Event{ - &terminalapi.Keyboard{}, - &terminalapi.Mouse{}, - } - eds.Subscribe(want, func(ev terminalapi.Event) { - if err := c.processEvent(ev); err != nil { - eds.Event(terminalapi.NewErrorf("failed to process event %v: %v", ev, err)) - } - }, event.MaxRepetitive(maxReps)) -} - -// adjustMouseEv adjusts the mouse event relative to the widget area. -func adjustMouseEv(m *terminalapi.Mouse, wArea image.Rectangle) *terminalapi.Mouse { - // The sent mouse coordinate is relative to the widget canvas, i.e. zero - // based, even though the widget might not be in the top left corner on the - // terminal. - offset := wArea.Min - if m.Position.In(wArea) { - return &terminalapi.Mouse{ - Position: m.Position.Sub(offset), - Button: m.Button, - } - } - return &terminalapi.Mouse{ - Position: image.Point{-1, -1}, - Button: m.Button, - } -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/draw.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/draw.go deleted file mode 100644 index d186b1272..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/draw.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package container - -// draw.go contains logic to draw containers and the contained widgets. - -import ( - "errors" - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/area" - "github.com/mum4k/termdash/private/canvas" - "github.com/mum4k/termdash/private/draw" - "github.com/mum4k/termdash/widgetapi" -) - -// drawTree draws this container and all of its sub containers. -func drawTree(c *Container) error { - var errStr string - - root := rootCont(c) - size := root.term.Size() - ar, err := root.opts.margin.apply(image.Rect(0, 0, size.X, size.Y)) - if err != nil { - return err - } - root.area = ar - - preOrder(root, &errStr, visitFunc(func(c *Container) error { - first, second, err := c.split() - if err != nil { - return err - } - if c.first != nil { - ar, err := c.first.opts.margin.apply(first) - if err != nil { - return err - } - c.first.area = ar - } - - if c.second != nil { - ar, err := c.second.opts.margin.apply(second) - if err != nil { - return err - } - c.second.area = ar - } - return drawCont(c) - })) - if errStr != "" { - return errors.New(errStr) - } - return nil -} - -// drawBorder draws the border around the container if requested. -func drawBorder(c *Container) error { - if !c.hasBorder() { - return nil - } - - cvs, err := canvas.New(c.area) - if err != nil { - return err - } - - ar, err := area.FromSize(cvs.Size()) - if err != nil { - return err - } - - var cOpts []cell.Option - if c.focusTracker.isActive(c) { - cOpts = append(cOpts, cell.FgColor(c.opts.inherited.focusedColor)) - } else { - cOpts = append(cOpts, cell.FgColor(c.opts.inherited.borderColor)) - } - - if err := draw.Border(cvs, ar, - draw.BorderLineStyle(c.opts.border), - draw.BorderTitle(c.opts.borderTitle, draw.OverrunModeThreeDot, cOpts...), - draw.BorderTitleAlign(c.opts.borderTitleHAlign), - draw.BorderCellOpts(cOpts...), - ); err != nil { - return err - } - return cvs.Apply(c.term) -} - -// drawWidget requests the widget to draw on the canvas. -func drawWidget(c *Container) error { - widgetArea, err := c.widgetArea() - if err != nil { - return err - } - if widgetArea == image.ZR { - return nil - } - - if !c.hasWidget() { - return nil - } - - needSize := image.Point{1, 1} - wOpts := c.opts.widget.Options() - if wOpts.MinimumSize.X > 0 && wOpts.MinimumSize.Y > 0 { - needSize = wOpts.MinimumSize - } - - if widgetArea.Dx() < needSize.X || widgetArea.Dy() < needSize.Y { - return drawResize(c, c.usable()) - } - - cvs, err := canvas.New(widgetArea) - if err != nil { - return err - } - - meta := &widgetapi.Meta{ - Focused: c.focusTracker.isActive(c), - } - - if err := c.opts.widget.Draw(cvs, meta); err != nil { - return err - } - return cvs.Apply(c.term) -} - -// drawResize draws an unicode character indicating that the size is too small to draw this container. -// Does nothing if the size is smaller than one cell, leaving no space for the character. -func drawResize(c *Container, area image.Rectangle) error { - if area.Dx() < 1 || area.Dy() < 1 { - return nil - } - - cvs, err := canvas.New(area) - if err != nil { - return err - } - if err := draw.ResizeNeeded(cvs); err != nil { - return err - } - return cvs.Apply(c.term) -} - -// drawCont draws the container and its widget. -func drawCont(c *Container) error { - if us := c.usable(); us.Dx() <= 0 || us.Dy() <= 0 { - return drawResize(c, c.area) - } - - if err := drawBorder(c); err != nil { - return fmt.Errorf("unable to draw container border: %v", err) - } - - if err := drawWidget(c); err != nil { - return fmt.Errorf("unable to draw widget %T: %v", c.opts.widget, err) - } - return nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/focus.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/focus.go deleted file mode 100644 index 4320eea73..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/focus.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package container - -// focus.go contains code that tracks the focused container. - -import ( - "image" - - "github.com/mum4k/termdash/mouse" - "github.com/mum4k/termdash/private/button" - "github.com/mum4k/termdash/terminal/terminalapi" -) - -// pointCont finds the top-most (on the screen) container whose area contains -// the given point. Returns nil if none of the containers in the tree contain -// this point. -func pointCont(c *Container, p image.Point) *Container { - var ( - errStr string - cont *Container - ) - postOrder(rootCont(c), &errStr, visitFunc(func(c *Container) error { - if p.In(c.area) && cont == nil { - cont = c - } - return nil - })) - return cont -} - -// focusTracker tracks the active (focused) container. -// This is not thread-safe, the implementation assumes that the owner of -// focusTracker performs locking. -type focusTracker struct { - // container is the currently focused container. - container *Container - - // candidate is the container that might become focused next. I.e. we got - // a mouse click and now waiting for a release or a timeout. - candidate *Container - - // buttonFSM is a state machine tracking mouse clicks in containers and - // moving focus from one container to the next. - buttonFSM *button.FSM -} - -// newFocusTracker returns a new focus tracker with focus set at the provided -// container. -func newFocusTracker(c *Container) *focusTracker { - return &focusTracker{ - container: c, - // Mouse FSM tracking clicks inside the entire area for the root - // container. - buttonFSM: button.NewFSM(mouse.ButtonLeft, c.area), - } -} - -// isActive determines if the provided container is the currently active container. -func (ft *focusTracker) isActive(c *Container) bool { - return ft.container == c -} - -// setActive sets the currently active container to the one provided. -func (ft *focusTracker) setActive(c *Container) { - ft.container = c -} - -// mouse identifies mouse events that change the focused container and track -// the focused container in the tree. -// The argument c is the container onto which the mouse event landed. -func (ft *focusTracker) mouse(target *Container, m *terminalapi.Mouse) { - clicked, bs := ft.buttonFSM.Event(m) - switch { - case bs == button.Down: - ft.candidate = target - case bs == button.Up && clicked: - if target == ft.candidate { - ft.container = target - } - } -} - -// updateArea updates the area that the focus tracker considers active for -// mouse clicks. -func (ft *focusTracker) updateArea(ar image.Rectangle) { - ft.buttonFSM.UpdateArea(ar) -} - -// reachableFrom asserts whether the currently focused container is reachable -// from the provided node in the tree. -func (ft *focusTracker) reachableFrom(node *Container) bool { - var ( - errStr string - reachable bool - ) - preOrder(node, &errStr, visitFunc(func(c *Container) error { - if c == ft.container { - reachable = true - } - return nil - })) - return reachable -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/options.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/options.go deleted file mode 100644 index 2d34af4db..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/options.go +++ /dev/null @@ -1,817 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package container - -// options.go defines container options. - -import ( - "errors" - "fmt" - "image" - - "github.com/mum4k/termdash/align" - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/linestyle" - "github.com/mum4k/termdash/private/area" - "github.com/mum4k/termdash/widgetapi" -) - -// applyOptions applies the options to the container and validates them. -func applyOptions(c *Container, opts ...Option) error { - for _, opt := range opts { - if err := opt.set(c); err != nil { - return err - } - } - return nil -} - -// ensure all the container identifiers are either empty or unique. -func validateIds(c *Container, seen map[string]bool) error { - if c.opts.id == "" { - return nil - } else if seen[c.opts.id] { - return fmt.Errorf("duplicate container ID %q", c.opts.id) - } - seen[c.opts.id] = true - - return nil -} - -// ensure all the container only have one split modifier. -func validateSplits(c *Container) error { - if c.opts.splitFixed > DefaultSplitFixed && c.opts.splitPercent != DefaultSplitPercent { - return fmt.Errorf( - "only one of splitFixed `%v` and splitPercent `%v` is allowed to be set per container", - c.opts.splitFixed, - c.opts.splitPercent, - ) - } - - return nil -} - -// validateOptions validates options set in the container tree. -func validateOptions(c *Container) error { - var errStr string - seenID := map[string]bool{} - preOrder(c, &errStr, func(c *Container) error { - if err := validateIds(c, seenID); err != nil { - return err - } - if err := validateSplits(c); err != nil { - return err - } - - return nil - }) - if errStr != "" { - return errors.New(errStr) - } - - return nil -} - -// Option is used to provide options to a container. -type Option interface { - // set sets the provided option. - set(*Container) error -} - -// options stores the options provided to the container. -type options struct { - // id is the identifier provided by the user. - id string - - // inherited are options that are inherited by child containers. - inherited inherited - - // split identifies how is this container split. - split splitType - splitPercent int - splitFixed int - - // widget is the widget in the container. - // A container can have either two sub containers (left and right) or a - // widget. But not both. - widget widgetapi.Widget - - // Alignment of the widget if present. - hAlign align.Horizontal - vAlign align.Vertical - - // border is the border around the container. - border linestyle.LineStyle - borderTitle string - borderTitleHAlign align.Horizontal - - // padding is a space reserved between the outer edge of the container and - // its content (the widget or other sub-containers). - padding padding - - // margin is a space reserved on the outside of the container. - margin margin -} - -// margin stores the configured margin for the container. -// For each margin direction, only one of the percentage or cells is set. -type margin struct { - topCells int - topPerc int - rightCells int - rightPerc int - bottomCells int - bottomPerc int - leftCells int - leftPerc int -} - -// apply applies the configured margin to the area. -func (p *margin) apply(ar image.Rectangle) (image.Rectangle, error) { - switch { - case p.topCells != 0 || p.rightCells != 0 || p.bottomCells != 0 || p.leftCells != 0: - return area.Shrink(ar, p.topCells, p.rightCells, p.bottomCells, p.leftCells) - case p.topPerc != 0 || p.rightPerc != 0 || p.bottomPerc != 0 || p.leftPerc != 0: - return area.ShrinkPercent(ar, p.topPerc, p.rightPerc, p.bottomPerc, p.leftPerc) - } - return ar, nil -} - -// padding stores the configured padding for the container. -// For each padding direction, only one of the percentage or cells is set. -type padding struct { - topCells int - topPerc int - rightCells int - rightPerc int - bottomCells int - bottomPerc int - leftCells int - leftPerc int -} - -// apply applies the configured padding to the area. -func (p *padding) apply(ar image.Rectangle) (image.Rectangle, error) { - switch { - case p.topCells != 0 || p.rightCells != 0 || p.bottomCells != 0 || p.leftCells != 0: - return area.Shrink(ar, p.topCells, p.rightCells, p.bottomCells, p.leftCells) - case p.topPerc != 0 || p.rightPerc != 0 || p.bottomPerc != 0 || p.leftPerc != 0: - return area.ShrinkPercent(ar, p.topPerc, p.rightPerc, p.bottomPerc, p.leftPerc) - } - return ar, nil -} - -// inherited contains options that are inherited by child containers. -type inherited struct { - // borderColor is the color used for the border. - borderColor cell.Color - // focusedColor is the color used for the border when focused. - focusedColor cell.Color -} - -// newOptions returns a new options instance with the default values. -// Parent are the inherited options from the parent container or nil if these -// options are for a container with no parent (the root). -func newOptions(parent *options) *options { - opts := &options{ - inherited: inherited{ - focusedColor: cell.ColorYellow, - }, - hAlign: align.HorizontalCenter, - vAlign: align.VerticalMiddle, - splitPercent: DefaultSplitPercent, - splitFixed: DefaultSplitFixed, - } - if parent != nil { - opts.inherited = parent.inherited - } - return opts -} - -// option implements Option. -type option func(*Container) error - -// set implements Option.set. -func (o option) set(c *Container) error { - return o(c) -} - -// SplitOption is used when splitting containers. -type SplitOption interface { - // setSplit sets the provided split option. - setSplit(*options) error -} - -// splitOption implements SplitOption. -type splitOption func(*options) error - -// setSplit implements SplitOption.setSplit. -func (so splitOption) setSplit(opts *options) error { - return so(opts) -} - -// DefaultSplitPercent is the default value for the SplitPercent option. -const DefaultSplitPercent = 50 - -// DefaultSplitFixed is the default value for the SplitFixed option. -const DefaultSplitFixed = -1 - -// SplitPercent sets the relative size of the split as percentage of the available space. -// When using SplitVertical, the provided size is applied to the new left -// container, the new right container gets the reminder of the size. -// When using SplitHorizontal, the provided size is applied to the new top -// container, the new bottom container gets the reminder of the size. -// The provided value must be a positive number in the range 0 < p < 100. -// If not provided, defaults to DefaultSplitPercent. -func SplitPercent(p int) SplitOption { - return splitOption(func(opts *options) error { - if min, max := 0, 100; p <= min || p >= max { - return fmt.Errorf("invalid split percentage %d, must be in range %d < p < %d", p, min, max) - } - opts.splitPercent = p - return nil - }) -} - -// SplitFixed sets the size of the first container to be a fixed value -// and makes the second container take up the remaining space. -// When using SplitVertical, the provided size is applied to the new left -// container, the new right container gets the reminder of the size. -// When using SplitHorizontal, the provided size is applied to the new top -// container, the new bottom container gets the reminder of the size. -// The provided value must be a positive number in the range 0 <= cells. -// If SplitFixed() is not specified, it defaults to SplitPercent() and its given value. -// Only one of SplitFixed() and SplitPercent() can be specified per container. -func SplitFixed(cells int) SplitOption { - return splitOption(func(opts *options) error { - if cells < 0 { - return fmt.Errorf("invalid fixed value %d, must be in range %d <= cells", cells, 0) - } - opts.splitFixed = cells - return nil - }) -} - -// SplitVertical splits the container along the vertical axis into two sub -// containers. The use of this option removes any widget placed at this -// container, containers with sub containers cannot contain widgets. -func SplitVertical(l LeftOption, r RightOption, opts ...SplitOption) Option { - return option(func(c *Container) error { - c.opts.split = splitTypeVertical - c.opts.widget = nil - for _, opt := range opts { - if err := opt.setSplit(c.opts); err != nil { - return err - } - } - - if err := c.createFirst(l.lOpts()); err != nil { - return err - } - return c.createSecond(r.rOpts()) - }) -} - -// SplitHorizontal splits the container along the horizontal axis into two sub -// containers. The use of this option removes any widget placed at this -// container, containers with sub containers cannot contain widgets. -func SplitHorizontal(t TopOption, b BottomOption, opts ...SplitOption) Option { - return option(func(c *Container) error { - c.opts.split = splitTypeHorizontal - c.opts.widget = nil - for _, opt := range opts { - if err := opt.setSplit(c.opts); err != nil { - return err - } - } - - if err := c.createFirst(t.tOpts()); err != nil { - return err - } - - return c.createSecond(b.bOpts()) - }) -} - -// ID sets an identifier for this container. -// This ID can be later used to perform dynamic layout changes by passing new -// options to this container. When provided, it must be a non-empty string that -// is unique among all the containers. -func ID(id string) Option { - return option(func(c *Container) error { - if id == "" { - return errors.New("the ID cannot be an empty string") - } - c.opts.id = id - return nil - }) -} - -// Clear clears this container. -// If the container contains a widget, the widget is removed. -// If the container had any sub containers or splits, they are removed. -func Clear() Option { - return option(func(c *Container) error { - c.opts.widget = nil - c.first = nil - c.second = nil - return nil - }) -} - -// PlaceWidget places the provided widget into the container. -// The use of this option removes any sub containers. Containers with sub -// containers cannot have widgets. -func PlaceWidget(w widgetapi.Widget) Option { - return option(func(c *Container) error { - c.opts.widget = w - c.first = nil - c.second = nil - return nil - }) -} - -// MarginTop sets reserved space outside of the container at its top. -// The provided number is the absolute margin in cells and must be zero or a -// positive integer. Only one of MarginTop or MarginTopPercent can be specified. -func MarginTop(cells int) Option { - return option(func(c *Container) error { - if min := 0; cells < min { - return fmt.Errorf("invalid MarginTop(%d), must be in range %d <= value", cells, min) - } - if c.opts.margin.topPerc > 0 { - return fmt.Errorf("cannot specify both MarginTop(%d) and MarginTopPercent(%d)", cells, c.opts.margin.topPerc) - } - c.opts.margin.topCells = cells - return nil - }) -} - -// MarginRight sets reserved space outside of the container at its right. -// The provided number is the absolute margin in cells and must be zero or a -// positive integer. Only one of MarginRight or MarginRightPercent can be specified. -func MarginRight(cells int) Option { - return option(func(c *Container) error { - if min := 0; cells < min { - return fmt.Errorf("invalid MarginRight(%d), must be in range %d <= value", cells, min) - } - if c.opts.margin.rightPerc > 0 { - return fmt.Errorf("cannot specify both MarginRight(%d) and MarginRightPercent(%d)", cells, c.opts.margin.rightPerc) - } - c.opts.margin.rightCells = cells - return nil - }) -} - -// MarginBottom sets reserved space outside of the container at its bottom. -// The provided number is the absolute margin in cells and must be zero or a -// positive integer. Only one of MarginBottom or MarginBottomPercent can be specified. -func MarginBottom(cells int) Option { - return option(func(c *Container) error { - if min := 0; cells < min { - return fmt.Errorf("invalid MarginBottom(%d), must be in range %d <= value", cells, min) - } - if c.opts.margin.bottomPerc > 0 { - return fmt.Errorf("cannot specify both MarginBottom(%d) and MarginBottomPercent(%d)", cells, c.opts.margin.bottomPerc) - } - c.opts.margin.bottomCells = cells - return nil - }) -} - -// MarginLeft sets reserved space outside of the container at its left. -// The provided number is the absolute margin in cells and must be zero or a -// positive integer. Only one of MarginLeft or MarginLeftPercent can be specified. -func MarginLeft(cells int) Option { - return option(func(c *Container) error { - if min := 0; cells < min { - return fmt.Errorf("invalid MarginLeft(%d), must be in range %d <= value", cells, min) - } - if c.opts.margin.leftPerc > 0 { - return fmt.Errorf("cannot specify both MarginLeft(%d) and MarginLeftPercent(%d)", cells, c.opts.margin.leftPerc) - } - c.opts.margin.leftCells = cells - return nil - }) -} - -// MarginTopPercent sets reserved space outside of the container at its top. -// The provided number is a relative margin defined as percentage of the container's height. -// Only one of MarginTop or MarginTopPercent can be specified. -// The value must be in range 0 <= value <= 100. -func MarginTopPercent(perc int) Option { - return option(func(c *Container) error { - if min, max := 0, 100; perc < min || perc > max { - return fmt.Errorf("invalid MarginTopPercent(%d), must be in range %d <= value <= %d", perc, min, max) - } - if c.opts.margin.topCells > 0 { - return fmt.Errorf("cannot specify both MarginTopPercent(%d) and MarginTop(%d)", perc, c.opts.margin.topCells) - } - c.opts.margin.topPerc = perc - return nil - }) -} - -// MarginRightPercent sets reserved space outside of the container at its right. -// The provided number is a relative margin defined as percentage of the container's height. -// Only one of MarginRight or MarginRightPercent can be specified. -// The value must be in range 0 <= value <= 100. -func MarginRightPercent(perc int) Option { - return option(func(c *Container) error { - if min, max := 0, 100; perc < min || perc > max { - return fmt.Errorf("invalid MarginRightPercent(%d), must be in range %d <= value <= %d", perc, min, max) - } - if c.opts.margin.rightCells > 0 { - return fmt.Errorf("cannot specify both MarginRightPercent(%d) and MarginRight(%d)", perc, c.opts.margin.rightCells) - } - c.opts.margin.rightPerc = perc - return nil - }) -} - -// MarginBottomPercent sets reserved space outside of the container at its bottom. -// The provided number is a relative margin defined as percentage of the container's height. -// Only one of MarginBottom or MarginBottomPercent can be specified. -// The value must be in range 0 <= value <= 100. -func MarginBottomPercent(perc int) Option { - return option(func(c *Container) error { - if min, max := 0, 100; perc < min || perc > max { - return fmt.Errorf("invalid MarginBottomPercent(%d), must be in range %d <= value <= %d", perc, min, max) - } - if c.opts.margin.bottomCells > 0 { - return fmt.Errorf("cannot specify both MarginBottomPercent(%d) and MarginBottom(%d)", perc, c.opts.margin.bottomCells) - } - c.opts.margin.bottomPerc = perc - return nil - }) -} - -// MarginLeftPercent sets reserved space outside of the container at its left. -// The provided number is a relative margin defined as percentage of the container's height. -// Only one of MarginLeft or MarginLeftPercent can be specified. -// The value must be in range 0 <= value <= 100. -func MarginLeftPercent(perc int) Option { - return option(func(c *Container) error { - if min, max := 0, 100; perc < min || perc > max { - return fmt.Errorf("invalid MarginLeftPercent(%d), must be in range %d <= value <= %d", perc, min, max) - } - if c.opts.margin.leftCells > 0 { - return fmt.Errorf("cannot specify both MarginLeftPercent(%d) and MarginLeft(%d)", perc, c.opts.margin.leftCells) - } - c.opts.margin.leftPerc = perc - return nil - }) -} - -// PaddingTop sets reserved space between container and the top side of its widget. -// The widget's area size is decreased to accommodate the padding. -// The provided number is the absolute padding in cells and must be zero or a -// positive integer. Only one of PaddingTop or PaddingTopPercent can be specified. -func PaddingTop(cells int) Option { - return option(func(c *Container) error { - if min := 0; cells < min { - return fmt.Errorf("invalid PaddingTop(%d), must be in range %d <= value", cells, min) - } - if c.opts.padding.topPerc > 0 { - return fmt.Errorf("cannot specify both PaddingTop(%d) and PaddingTopPercent(%d)", cells, c.opts.padding.topPerc) - } - c.opts.padding.topCells = cells - return nil - }) -} - -// PaddingRight sets reserved space between container and the right side of its widget. -// The widget's area size is decreased to accommodate the padding. -// The provided number is the absolute padding in cells and must be zero or a -// positive integer. Only one of PaddingRight or PaddingRightPercent can be specified. -func PaddingRight(cells int) Option { - return option(func(c *Container) error { - if min := 0; cells < min { - return fmt.Errorf("invalid PaddingRight(%d), must be in range %d <= value", cells, min) - } - if c.opts.padding.rightPerc > 0 { - return fmt.Errorf("cannot specify both PaddingRight(%d) and PaddingRightPercent(%d)", cells, c.opts.padding.rightPerc) - } - c.opts.padding.rightCells = cells - return nil - }) -} - -// PaddingBottom sets reserved space between container and the bottom side of its widget. -// The widget's area size is decreased to accommodate the padding. -// The provided number is the absolute padding in cells and must be zero or a -// positive integer. Only one of PaddingBottom or PaddingBottomPercent can be specified. -func PaddingBottom(cells int) Option { - return option(func(c *Container) error { - if min := 0; cells < min { - return fmt.Errorf("invalid PaddingBottom(%d), must be in range %d <= value", cells, min) - } - if c.opts.padding.bottomPerc > 0 { - return fmt.Errorf("cannot specify both PaddingBottom(%d) and PaddingBottomPercent(%d)", cells, c.opts.padding.bottomPerc) - } - c.opts.padding.bottomCells = cells - return nil - }) -} - -// PaddingLeft sets reserved space between container and the left side of its widget. -// The widget's area size is decreased to accommodate the padding. -// The provided number is the absolute padding in cells and must be zero or a -// positive integer. Only one of PaddingLeft or PaddingLeftPercent can be specified. -func PaddingLeft(cells int) Option { - return option(func(c *Container) error { - if min := 0; cells < min { - return fmt.Errorf("invalid PaddingLeft(%d), must be in range %d <= value", cells, min) - } - if c.opts.padding.leftPerc > 0 { - return fmt.Errorf("cannot specify both PaddingLeft(%d) and PaddingLeftPercent(%d)", cells, c.opts.padding.leftPerc) - } - c.opts.padding.leftCells = cells - return nil - }) -} - -// PaddingTopPercent sets reserved space between container and the top side of -// its widget. The widget's area size is decreased to accommodate the padding. -// The provided number is a relative padding defined as percentage of the -// container's height. The value must be in range 0 <= value <= 100. -// Only one of PaddingTop or PaddingTopPercent can be specified. -func PaddingTopPercent(perc int) Option { - return option(func(c *Container) error { - if min, max := 0, 100; perc < min || perc > max { - return fmt.Errorf("invalid PaddingTopPercent(%d), must be in range %d <= value <= %d", perc, min, max) - } - if c.opts.padding.topCells > 0 { - return fmt.Errorf("cannot specify both PaddingTopPercent(%d) and PaddingTop(%d)", perc, c.opts.padding.topCells) - } - c.opts.padding.topPerc = perc - return nil - }) -} - -// PaddingRightPercent sets reserved space between container and the right side of -// its widget. The widget's area size is decreased to accommodate the padding. -// The provided number is a relative padding defined as percentage of the -// container's width. The value must be in range 0 <= value <= 100. -// Only one of PaddingRight or PaddingRightPercent can be specified. -func PaddingRightPercent(perc int) Option { - return option(func(c *Container) error { - if min, max := 0, 100; perc < min || perc > max { - return fmt.Errorf("invalid PaddingRightPercent(%d), must be in range %d <= value <= %d", perc, min, max) - } - if c.opts.padding.rightCells > 0 { - return fmt.Errorf("cannot specify both PaddingRightPercent(%d) and PaddingRight(%d)", perc, c.opts.padding.rightCells) - } - c.opts.padding.rightPerc = perc - return nil - }) -} - -// PaddingBottomPercent sets reserved space between container and the bottom side of -// its widget. The widget's area size is decreased to accommodate the padding. -// The provided number is a relative padding defined as percentage of the -// container's height. The value must be in range 0 <= value <= 100. -// Only one of PaddingBottom or PaddingBottomPercent can be specified. -func PaddingBottomPercent(perc int) Option { - return option(func(c *Container) error { - if min, max := 0, 100; perc < min || perc > max { - return fmt.Errorf("invalid PaddingBottomPercent(%d), must be in range %d <= value <= %d", perc, min, max) - } - if c.opts.padding.bottomCells > 0 { - return fmt.Errorf("cannot specify both PaddingBottomPercent(%d) and PaddingBottom(%d)", perc, c.opts.padding.bottomCells) - } - c.opts.padding.bottomPerc = perc - return nil - }) -} - -// PaddingLeftPercent sets reserved space between container and the left side of -// its widget. The widget's area size is decreased to accommodate the padding. -// The provided number is a relative padding defined as percentage of the -// container's width. The value must be in range 0 <= value <= 100. -// Only one of PaddingLeft or PaddingLeftPercent can be specified. -func PaddingLeftPercent(perc int) Option { - return option(func(c *Container) error { - if min, max := 0, 100; perc < min || perc > max { - return fmt.Errorf("invalid PaddingLeftPercent(%d), must be in range %d <= value <= %d", perc, min, max) - } - if c.opts.padding.leftCells > 0 { - return fmt.Errorf("cannot specify both PaddingLeftPercent(%d) and PaddingLeft(%d)", perc, c.opts.padding.leftCells) - } - c.opts.padding.leftPerc = perc - return nil - }) -} - -// AlignHorizontal sets the horizontal alignment for the widget placed in the -// container. Has no effect if the container contains no widget. -// Defaults to alignment in the center. -func AlignHorizontal(h align.Horizontal) Option { - return option(func(c *Container) error { - c.opts.hAlign = h - return nil - }) -} - -// AlignVertical sets the vertical alignment for the widget placed in the container. -// Has no effect if the container contains no widget. -// Defaults to alignment in the middle. -func AlignVertical(v align.Vertical) Option { - return option(func(c *Container) error { - c.opts.vAlign = v - return nil - }) -} - -// Border configures the container to have a border of the specified style. -func Border(ls linestyle.LineStyle) Option { - return option(func(c *Container) error { - c.opts.border = ls - return nil - }) -} - -// BorderTitle sets a text title within the border. -func BorderTitle(title string) Option { - return option(func(c *Container) error { - c.opts.borderTitle = title - return nil - }) -} - -// BorderTitleAlignLeft aligns the border title on the left. -func BorderTitleAlignLeft() Option { - return option(func(c *Container) error { - c.opts.borderTitleHAlign = align.HorizontalLeft - return nil - }) -} - -// BorderTitleAlignCenter aligns the border title in the center. -func BorderTitleAlignCenter() Option { - return option(func(c *Container) error { - c.opts.borderTitleHAlign = align.HorizontalCenter - return nil - }) -} - -// BorderTitleAlignRight aligns the border title on the right. -func BorderTitleAlignRight() Option { - return option(func(c *Container) error { - c.opts.borderTitleHAlign = align.HorizontalRight - return nil - }) -} - -// BorderColor sets the color of the border around the container. -// This option is inherited to sub containers created by container splits. -func BorderColor(color cell.Color) Option { - return option(func(c *Container) error { - c.opts.inherited.borderColor = color - return nil - }) -} - -// FocusedColor sets the color of the border around the container when it has -// keyboard focus. -// This option is inherited to sub containers created by container splits. -func FocusedColor(color cell.Color) Option { - return option(func(c *Container) error { - c.opts.inherited.focusedColor = color - return nil - }) -} - -// splitType identifies how a container is split. -type splitType int - -// String implements fmt.Stringer() -func (st splitType) String() string { - if n, ok := splitTypeNames[st]; ok { - return n - } - return "splitTypeUnknown" -} - -// splitTypeNames maps splitType values to human readable names. -var splitTypeNames = map[splitType]string{ - splitTypeVertical: "splitTypeVertical", - splitTypeHorizontal: "splitTypeHorizontal", -} - -const ( - splitTypeVertical splitType = iota - splitTypeHorizontal -) - -// LeftOption is used to provide options to the left sub container after a -// vertical split of the parent. -type LeftOption interface { - // lOpts returns the options. - lOpts() []Option -} - -// leftOption implements LeftOption. -type leftOption func() []Option - -// lOpts implements LeftOption.lOpts. -func (lo leftOption) lOpts() []Option { - if lo == nil { - return nil - } - return lo() -} - -// Left applies options to the left sub container after a vertical split of the parent. -func Left(opts ...Option) LeftOption { - return leftOption(func() []Option { - return opts - }) -} - -// RightOption is used to provide options to the right sub container after a -// vertical split of the parent. -type RightOption interface { - // rOpts returns the options. - rOpts() []Option -} - -// rightOption implements RightOption. -type rightOption func() []Option - -// rOpts implements RightOption.rOpts. -func (lo rightOption) rOpts() []Option { - if lo == nil { - return nil - } - return lo() -} - -// Right applies options to the right sub container after a vertical split of the parent. -func Right(opts ...Option) RightOption { - return rightOption(func() []Option { - return opts - }) -} - -// TopOption is used to provide options to the top sub container after a -// horizontal split of the parent. -type TopOption interface { - // tOpts returns the options. - tOpts() []Option -} - -// topOption implements TopOption. -type topOption func() []Option - -// tOpts implements TopOption.tOpts. -func (lo topOption) tOpts() []Option { - if lo == nil { - return nil - } - return lo() -} - -// Top applies options to the top sub container after a horizontal split of the parent. -func Top(opts ...Option) TopOption { - return topOption(func() []Option { - return opts - }) -} - -// BottomOption is used to provide options to the bottom sub container after a -// horizontal split of the parent. -type BottomOption interface { - // bOpts returns the options. - bOpts() []Option -} - -// bottomOption implements BottomOption. -type bottomOption func() []Option - -// bOpts implements BottomOption.bOpts. -func (lo bottomOption) bOpts() []Option { - if lo == nil { - return nil - } - return lo() -} - -// Bottom applies options to the bottom sub container after a horizontal split of the parent. -func Bottom(opts ...Option) BottomOption { - return bottomOption(func() []Option { - return opts - }) -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/traversal.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/traversal.go deleted file mode 100644 index f728b50ba..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/traversal.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package container - -import ( - "errors" - "fmt" -) - -// traversal.go provides functions that navigate the container tree. - -// rootCont returns the root container. -func rootCont(c *Container) *Container { - for p := c.parent; p != nil; p = c.parent { - c = p - } - return c -} - -// visitFunc is executed during traversals when node is visited. -// If the visit function returns an error, the traversal terminates and the -// errStr is set to the text of the returned error. -type visitFunc func(*Container) error - -// preOrder performs pre-order DFS traversal on the container tree. -func preOrder(c *Container, errStr *string, visit visitFunc) { - if c == nil || *errStr != "" { - return - } - - if err := visit(c); err != nil { - *errStr = err.Error() - return - } - preOrder(c.first, errStr, visit) - preOrder(c.second, errStr, visit) -} - -// postOrder performs post-order DFS traversal on the container tree. -func postOrder(c *Container, errStr *string, visit visitFunc) { - if c == nil || *errStr != "" { - return - } - - postOrder(c.first, errStr, visit) - postOrder(c.second, errStr, visit) - if err := visit(c); err != nil { - *errStr = err.Error() - return - } -} - -// findID finds container with the provided ID. -// Returns an error of there is no container with the specified ID. -func findID(root *Container, id string) (*Container, error) { - if id == "" { - return nil, errors.New("the container ID must not be empty") - } - - var ( - errStr string - cont *Container - ) - preOrder(root, &errStr, visitFunc(func(c *Container) error { - if c.opts.id == id { - cont = c - } - return nil - })) - if cont == nil { - return nil, fmt.Errorf("cannot find container with ID %q", id) - } - return cont, nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/go.mod b/examples/go-dashboard/src/github.com/mum4k/termdash/go.mod deleted file mode 100644 index 3a81b3235..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module github.com/mum4k/termdash - -go 1.14 - -require ( - github.com/gdamore/tcell v1.3.0 - github.com/kylelemons/godebug v1.1.0 - github.com/mattn/go-runewidth v0.0.9 - github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be -) diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/keyboard/keyboard.go b/examples/go-dashboard/src/github.com/mum4k/termdash/keyboard/keyboard.go deleted file mode 100644 index 3a852b326..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/keyboard/keyboard.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package keyboard defines well known keyboard keys and shortcuts. -package keyboard - -// Key represents a single button on the keyboard. -// Printable characters are set to their ASCII/Unicode rune value. -// Non-printable (control) characters are equal to one of the constants defined -// below. -type Key rune - -// String implements fmt.Stringer() -func (b Key) String() string { - if n, ok := buttonNames[b]; ok { - return n - } else if b >= 0 { - return string(b) - } - return "KeyUnknown" -} - -// buttonNames maps Key values to human readable names. -var buttonNames = map[Key]string{ - KeyF1: "KeyF1", - KeyF2: "KeyF2", - KeyF3: "KeyF3", - KeyF4: "KeyF4", - KeyF5: "KeyF5", - KeyF6: "KeyF6", - KeyF7: "KeyF7", - KeyF8: "KeyF8", - KeyF9: "KeyF9", - KeyF10: "KeyF10", - KeyF11: "KeyF11", - KeyF12: "KeyF12", - KeyInsert: "KeyInsert", - KeyDelete: "KeyDelete", - KeyHome: "KeyHome", - KeyEnd: "KeyEnd", - KeyPgUp: "KeyPgUp", - KeyPgDn: "KeyPgDn", - KeyArrowUp: "KeyArrowUp", - KeyArrowDown: "KeyArrowDown", - KeyArrowLeft: "KeyArrowLeft", - KeyArrowRight: "KeyArrowRight", - KeyCtrlTilde: "KeyCtrlTilde", - KeyCtrlA: "KeyCtrlA", - KeyCtrlB: "KeyCtrlB", - KeyCtrlC: "KeyCtrlC", - KeyCtrlD: "KeyCtrlD", - KeyCtrlE: "KeyCtrlE", - KeyCtrlF: "KeyCtrlF", - KeyCtrlG: "KeyCtrlG", - KeyBackspace: "KeyBackspace", - KeyTab: "KeyTab", - KeyCtrlJ: "KeyCtrlJ", - KeyCtrlK: "KeyCtrlK", - KeyCtrlL: "KeyCtrlL", - KeyEnter: "KeyEnter", - KeyCtrlN: "KeyCtrlN", - KeyCtrlO: "KeyCtrlO", - KeyCtrlP: "KeyCtrlP", - KeyCtrlQ: "KeyCtrlQ", - KeyCtrlR: "KeyCtrlR", - KeyCtrlS: "KeyCtrlS", - KeyCtrlT: "KeyCtrlT", - KeyCtrlU: "KeyCtrlU", - KeyCtrlV: "KeyCtrlV", - KeyCtrlW: "KeyCtrlW", - KeyCtrlX: "KeyCtrlX", - KeyCtrlY: "KeyCtrlY", - KeyCtrlZ: "KeyCtrlZ", - KeyEsc: "KeyEsc", - KeyCtrl4: "KeyCtrl4", - KeyCtrl5: "KeyCtrl5", - KeyCtrl6: "KeyCtrl6", - KeyCtrl7: "KeyCtrl7", - KeySpace: "KeySpace", - KeyBackspace2: "KeyBackspace2", -} - -// Printable characters, but worth having constants for them. -const ( - KeySpace = ' ' -) - -// Negative values for non-printable characters. -const ( - KeyF1 Key = -(iota + 1) - KeyF2 - KeyF3 - KeyF4 - KeyF5 - KeyF6 - KeyF7 - KeyF8 - KeyF9 - KeyF10 - KeyF11 - KeyF12 - KeyInsert - KeyDelete - KeyHome - KeyEnd - KeyPgUp - KeyPgDn - KeyArrowUp - KeyArrowDown - KeyArrowLeft - KeyArrowRight - KeyCtrlTilde - KeyCtrlA - KeyCtrlB - KeyCtrlC - KeyCtrlD - KeyCtrlE - KeyCtrlF - KeyCtrlG - KeyBackspace - KeyTab - KeyCtrlJ - KeyCtrlK - KeyCtrlL - KeyEnter - KeyCtrlN - KeyCtrlO - KeyCtrlP - KeyCtrlQ - KeyCtrlR - KeyCtrlS - KeyCtrlT - KeyCtrlU - KeyCtrlV - KeyCtrlW - KeyCtrlX - KeyCtrlY - KeyCtrlZ - KeyEsc - KeyCtrl4 - KeyCtrl5 - KeyCtrl6 - KeyCtrl7 - KeyBackspace2 -) - -// Keys declared as duplicates by termbox. -const ( - KeyCtrl2 Key = KeyCtrlTilde - KeyCtrlSpace Key = KeyCtrlTilde - KeyCtrlH Key = KeyBackspace - KeyCtrlI Key = KeyTab - KeyCtrlM Key = KeyEnter - KeyCtrlLsqBracket Key = KeyEsc - KeyCtrl3 Key = KeyEsc - KeyCtrlBackslash Key = KeyCtrl4 - KeyCtrlRsqBracket Key = KeyCtrl5 - KeyCtrlSlash Key = KeyCtrl7 - KeyCtrlUnderscore Key = KeyCtrl7 - KeyCtrl8 Key = KeyBackspace2 -) diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/linestyle/linestyle.go b/examples/go-dashboard/src/github.com/mum4k/termdash/linestyle/linestyle.go deleted file mode 100644 index c34fc3959..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/linestyle/linestyle.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package linestyle defines various line styles. -package linestyle - -// LineStyle defines the supported line styles. -type LineStyle int - -// String implements fmt.Stringer() -func (ls LineStyle) String() string { - if n, ok := lineStyleNames[ls]; ok { - return n - } - return "LineStyleUnknown" -} - -// lineStyleNames maps LineStyle values to human readable names. -var lineStyleNames = map[LineStyle]string{ - None: "LineStyleNone", - Light: "LineStyleLight", - Double: "LineStyleDouble", - Round: "LineStyleRound", -} - -// Supported line styles. -// See https://en.wikipedia.org/wiki/Box-drawing_character. -const ( - // None indicates that no line should be present. - None LineStyle = iota - - // Light is line style using the '─' characters. - Light - - // Double is line style using the '═' characters. - Double - - // Round is line style using the rounded corners '╭' characters. - Round -) diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/mouse/mouse.go b/examples/go-dashboard/src/github.com/mum4k/termdash/mouse/mouse.go deleted file mode 100644 index d21e1d310..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/mouse/mouse.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package mouse defines known mouse buttons. -package mouse - -// Button represents a mouse button. -type Button int - -// String implements fmt.Stringer() -func (b Button) String() string { - if n, ok := buttonNames[b]; ok { - return n - } - return "ButtonUnknown" -} - -// buttonNames maps Button values to human readable names. -var buttonNames = map[Button]string{ - ButtonLeft: "ButtonLeft", - ButtonRight: "ButtonRight", - ButtonMiddle: "ButtonMiddle", - ButtonRelease: "ButtonRelease", - ButtonWheelUp: "ButtonWheelUp", - ButtonWheelDown: "ButtonWheelDown", -} - -// Buttons recognized on the mouse. -const ( - buttonUnknown Button = iota - ButtonLeft - ButtonRight - ButtonMiddle - ButtonRelease - ButtonWheelUp - ButtonWheelDown -) diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/alignfor/alignfor.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/alignfor/alignfor.go deleted file mode 100644 index 93cbac844..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/alignfor/alignfor.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package alignfor provides functions that align elements. -package alignfor - -import ( - "fmt" - "image" - "strings" - - "github.com/mum4k/termdash/align" - "github.com/mum4k/termdash/private/runewidth" - "github.com/mum4k/termdash/private/wrap" -) - -// hAlign aligns the given area in the rectangle horizontally. -func hAlign(rect image.Rectangle, ar image.Rectangle, h align.Horizontal) (image.Rectangle, error) { - gap := rect.Dx() - ar.Dx() - switch h { - case align.HorizontalRight: - // Use gap from above. - case align.HorizontalCenter: - gap /= 2 - case align.HorizontalLeft: - gap = 0 - default: - return image.ZR, fmt.Errorf("unsupported horizontal alignment %v", h) - } - - return image.Rect( - rect.Min.X+gap, - ar.Min.Y, - rect.Min.X+gap+ar.Dx(), - ar.Max.Y, - ), nil -} - -// vAlign aligns the given area in the rectangle vertically. -func vAlign(rect image.Rectangle, ar image.Rectangle, v align.Vertical) (image.Rectangle, error) { - gap := rect.Dy() - ar.Dy() - switch v { - case align.VerticalBottom: - // Use gap from above. - case align.VerticalMiddle: - gap /= 2 - case align.VerticalTop: - gap = 0 - default: - return image.ZR, fmt.Errorf("unsupported vertical alignment %v", v) - } - - return image.Rect( - ar.Min.X, - rect.Min.Y+gap, - ar.Max.X, - rect.Min.Y+gap+ar.Dy(), - ), nil -} - -// Rectangle aligns the area within the rectangle returning the -// aligned area. The area must fall within the rectangle. -func Rectangle(rect image.Rectangle, ar image.Rectangle, h align.Horizontal, v align.Vertical) (image.Rectangle, error) { - if !ar.In(rect) { - return image.ZR, fmt.Errorf("cannot align area %v inside rectangle %v, the area falls outside of the rectangle", ar, rect) - } - - aligned, err := hAlign(rect, ar, h) - if err != nil { - return image.ZR, err - } - aligned, err = vAlign(rect, aligned, v) - if err != nil { - return image.ZR, err - } - return aligned, nil -} - -// Text aligns the text within the given rectangle, returns the start point for the text. -// For the purposes of the alignment this assumes that text will be trimmed if -// it overruns the rectangle. -// This only supports a single line of text, the text must not contain non-printable characters, -// allows empty text. -func Text(rect image.Rectangle, text string, h align.Horizontal, v align.Vertical) (image.Point, error) { - if strings.ContainsRune(text, '\n') { - return image.ZP, fmt.Errorf("the provided text contains a newline character: %q", text) - } - - if text != "" { - if err := wrap.ValidText(text); err != nil { - return image.ZP, fmt.Errorf("the provided text contains non printable character(s): %s", err) - } - } - - cells := runewidth.StringWidth(text) - var textLen int - if cells < rect.Dx() { - textLen = cells - } else { - textLen = rect.Dx() - } - - textRect := image.Rect( - rect.Min.X, - rect.Min.Y, - // For the purposes of aligning the text, assume that it will be - // trimmed to the available space. - rect.Min.X+textLen, - rect.Min.Y+1, - ) - - aligned, err := Rectangle(rect, textRect, h, v) - if err != nil { - return image.ZP, err - } - return image.Point{aligned.Min.X, aligned.Min.Y}, nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/area/area.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/area/area.go deleted file mode 100644 index 34b21a1b5..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/area/area.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package area provides functions working with image areas. -package area - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/private/numbers" -) - -// Size returns the size of the provided area. -func Size(area image.Rectangle) image.Point { - return image.Point{ - area.Dx(), - area.Dy(), - } -} - -// FromSize returns the corresponding area for the provided size. -func FromSize(size image.Point) (image.Rectangle, error) { - if size.X < 0 || size.Y < 0 { - return image.Rectangle{}, fmt.Errorf("cannot convert zero or negative size to an area, got: %+v", size) - } - return image.Rect(0, 0, size.X, size.Y), nil -} - -// HSplit returns two new areas created by splitting the provided area at the -// specified percentage of its width. The percentage must be in the range -// 0 <= heightPerc <= 100. -// Can return zero size areas. -func HSplit(area image.Rectangle, heightPerc int) (top image.Rectangle, bottom image.Rectangle, err error) { - if min, max := 0, 100; heightPerc < min || heightPerc > max { - return image.ZR, image.ZR, fmt.Errorf("invalid heightPerc %d, must be in range %d <= heightPerc <= %d", heightPerc, min, max) - } - height := area.Dy() * heightPerc / 100 - top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+height) - if top.Dy() == 0 { - top = image.ZR - } - bottom = image.Rect(area.Min.X, area.Min.Y+height, area.Max.X, area.Max.Y) - if bottom.Dy() == 0 { - bottom = image.ZR - } - return top, bottom, nil -} - -// VSplit returns two new areas created by splitting the provided area at the -// specified percentage of its width. The percentage must be in the range -// 0 <= widthPerc <= 100. -// Can return zero size areas. -func VSplit(area image.Rectangle, widthPerc int) (left image.Rectangle, right image.Rectangle, err error) { - if min, max := 0, 100; widthPerc < min || widthPerc > max { - return image.ZR, image.ZR, fmt.Errorf("invalid widthPerc %d, must be in range %d <= widthPerc <= %d", widthPerc, min, max) - } - width := area.Dx() * widthPerc / 100 - left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+width, area.Max.Y) - if left.Dx() == 0 { - left = image.ZR - } - right = image.Rect(area.Min.X+width, area.Min.Y, area.Max.X, area.Max.Y) - if right.Dx() == 0 { - right = image.ZR - } - return left, right, nil -} - -// VSplitCells returns two new areas created by splitting the provided area -// after the specified amount of cells of its width. The number of cells must -// be a zero or a positive integer. Providing a zero returns left=image.ZR, -// right=area. Providing a number equal or larger to area's width returns -// left=area, right=image.ZR. -func VSplitCells(area image.Rectangle, cells int) (left image.Rectangle, right image.Rectangle, err error) { - if min := 0; cells < min { - return image.ZR, image.ZR, fmt.Errorf("invalid cells %d, must be a positive integer", cells) - } - if cells == 0 { - return image.ZR, area, nil - } - - width := area.Dx() - if cells >= width { - return area, image.ZR, nil - } - - left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+cells, area.Max.Y) - right = image.Rect(area.Min.X+cells, area.Min.Y, area.Max.X, area.Max.Y) - return left, right, nil -} - -// HSplitCells returns two new areas created by splitting the provided area -// after the specified amount of cells of its height. The number of cells must -// be a zero or a positive integer. Providing a zero returns top=image.ZR, -// bottom=area. Providing a number equal or larger to area's height returns -// top=area, bottom=image.ZR. -func HSplitCells(area image.Rectangle, cells int) (top image.Rectangle, bottom image.Rectangle, err error) { - if min := 0; cells < min { - return image.ZR, image.ZR, fmt.Errorf("invalid cells %d, must be a positive integer", cells) - } - if cells == 0 { - return image.ZR, area, nil - } - - height := area.Dy() - if cells >= height { - return area, image.ZR, nil - } - - top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+cells) - bottom = image.Rect(area.Min.X, area.Min.Y+cells, area.Max.X, area.Max.Y) - return top, bottom, nil -} - -// ExcludeBorder returns a new area created by subtracting a border around the -// provided area. Return the zero area if there isn't enough space to exclude -// the border. -func ExcludeBorder(area image.Rectangle) image.Rectangle { - // If the area dimensions are smaller than this, subtracting a point for the - // border on each of its sides results in a zero area. - const minDim = 2 - if area.Dx() < minDim || area.Dy() < minDim { - return image.ZR - } - return image.Rect( - numbers.Abs(area.Min.X+1), - numbers.Abs(area.Min.Y+1), - numbers.Abs(area.Max.X-1), - numbers.Abs(area.Max.Y-1), - ) -} - -// WithRatio returns the largest area that has the requested ratio but is -// either equal or smaller than the provided area. Returns zero area if the -// area or the ratio are zero, or if there is no such area. -func WithRatio(area image.Rectangle, ratio image.Point) image.Rectangle { - ratio = numbers.SimplifyRatio(ratio) - if area == image.ZR || ratio == image.ZP { - return image.ZR - } - - wFact := area.Dx() / ratio.X - hFact := area.Dy() / ratio.Y - - var fact int - if wFact < hFact { - fact = wFact - } else { - fact = hFact - } - return image.Rect( - area.Min.X, - area.Min.Y, - ratio.X*fact+area.Min.X, - ratio.Y*fact+area.Min.Y, - ) -} - -// Shrink returns a new area whose size is reduced by the specified amount of -// cells. Can return a zero area if there is no space left in the area. -// The values must be zero or positive integers. -func Shrink(area image.Rectangle, topCells, rightCells, bottomCells, leftCells int) (image.Rectangle, error) { - for _, v := range []struct { - name string - value int - }{ - {"topCells", topCells}, - {"rightCells", rightCells}, - {"bottomCells", bottomCells}, - {"leftCells", leftCells}, - } { - if min := 0; v.value < min { - return image.ZR, fmt.Errorf("invalid %s(%d), must be in range %d <= value", v.name, v.value, min) - } - } - - shrunk := area - shrunk.Min.X, _ = numbers.MinMaxInts([]int{shrunk.Min.X + leftCells, shrunk.Max.X}) - _, shrunk.Max.X = numbers.MinMaxInts([]int{shrunk.Max.X - rightCells, shrunk.Min.X}) - shrunk.Min.Y, _ = numbers.MinMaxInts([]int{shrunk.Min.Y + topCells, shrunk.Max.Y}) - _, shrunk.Max.Y = numbers.MinMaxInts([]int{shrunk.Max.Y - bottomCells, shrunk.Min.Y}) - - if shrunk.Dx() == 0 || shrunk.Dy() == 0 { - return image.ZR, nil - } - return shrunk, nil -} - -// ShrinkPercent returns a new area whose size is reduced by percentage of its -// width or height. Can return a zero area if there is no space left in the area. -// The topPerc and bottomPerc indicate the percentage of area's height. -// The rightPerc and leftPerc indicate the percentage of area's width. -// The percentages must be in range 0 <= v <= 100. -func ShrinkPercent(area image.Rectangle, topPerc, rightPerc, bottomPerc, leftPerc int) (image.Rectangle, error) { - for _, v := range []struct { - name string - value int - }{ - {"topPerc", topPerc}, - {"rightPerc", rightPerc}, - {"bottomPerc", bottomPerc}, - {"leftPerc", leftPerc}, - } { - if min, max := 0, 100; v.value < min || v.value > max { - return image.ZR, fmt.Errorf("invalid %s(%d), must be in range %d <= value <= %d", v.name, v.value, min, max) - } - } - - top := area.Dy() * topPerc / 100 - bottom := area.Dy() * bottomPerc / 100 - right := area.Dx() * rightPerc / 100 - left := area.Dx() * leftPerc / 100 - return Shrink(area, top, right, bottom, left) -} - -// MoveUp returns a new area that is moved up by the specified amount of cells. -// Returns an error if the move would result in negative Y coordinates. -// The values must be zero or positive integers. -func MoveUp(area image.Rectangle, cells int) (image.Rectangle, error) { - if min := 0; cells < min { - return image.ZR, fmt.Errorf("cannot move area %v up by %d cells, must be in range %d <= value", area, cells, min) - } - - if area.Min.Y < cells { - return image.ZR, fmt.Errorf("cannot move area %v up by %d cells, would result in negative Y coordinate", area, cells) - } - - moved := area - moved.Min.Y -= cells - moved.Max.Y -= cells - return moved, nil -} - -// MoveDown returns a new area that is moved down by the specified amount of -// cells. -// The values must be zero or positive integers. -func MoveDown(area image.Rectangle, cells int) (image.Rectangle, error) { - if min := 0; cells < min { - return image.ZR, fmt.Errorf("cannot move area %v down by %d cells, must be in range %d <= value", area, cells, min) - } - - moved := area - moved.Min.Y += cells - moved.Max.Y += cells - return moved, nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/button/button.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/button/button.go deleted file mode 100644 index d4e0601b8..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/button/button.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package button implements a state machine that tracks mouse button clicks. -package button - -import ( - "image" - - "github.com/mum4k/termdash/mouse" - "github.com/mum4k/termdash/terminal/terminalapi" -) - -// State represents the state of the mouse button. -type State int - -// String implements fmt.Stringer() -func (s State) String() string { - if n, ok := stateNames[s]; ok { - return n - } - return "StateUnknown" -} - -// stateNames maps State values to human readable names. -var stateNames = map[State]string{ - Up: "StateUp", - Down: "StateDown", -} - -const ( - // Up is the default idle state of the mouse button. - Up State = iota - - // Down is a state where the mouse button is pressed down and held. - Down -) - -// FSM implements a finite-state machine that tracks mouse clicks within an -// area. -// -// Simplifies tracking of mouse button clicks, i.e. when the caller wants to -// perform an action only if both the button press and release happen within -// the specified area. -// -// This object is not thread-safe. -type FSM struct { - // button is the mouse button whose state this FSM tracks. - button mouse.Button - - // area is the area provided to NewFSM. - area image.Rectangle - - // state is the current state of the FSM. - state stateFn -} - -// NewFSM creates a new FSM instance that tracks the state of the specified -// mouse button through button events that fall within the provided area. -func NewFSM(button mouse.Button, area image.Rectangle) *FSM { - return &FSM{ - button: button, - area: area, - state: wantPress, - } -} - -// Event is used to forward mouse events to the state machine. -// Only events related to the button specified on a call to NewFSM are -// processed. -// -// Returns a bool indicating if an action guarded by the button should be -// performed and the state of the button after the provided event. -// The bool is true if the button click should take an effect, i.e. if the -// FSM saw both the button click and its release. -func (fsm *FSM) Event(m *terminalapi.Mouse) (bool, State) { - clicked, bs, next := fsm.state(fsm, m) - fsm.state = next - return clicked, bs -} - -// UpdateArea informs FSM of an area change. -// This method is idempotent. -func (fsm *FSM) UpdateArea(area image.Rectangle) { - fsm.area = area -} - -// stateFn is a single state in the state machine. -// Returns bool indicating if a click happened, the state of the button and the -// next state of the FSM. -type stateFn func(fsm *FSM, m *terminalapi.Mouse) (bool, State, stateFn) - -// wantPress is the initial state, expecting a button press inside the area. -func wantPress(fsm *FSM, m *terminalapi.Mouse) (bool, State, stateFn) { - if m.Button != fsm.button || !m.Position.In(fsm.area) { - return false, Up, wantPress - } - return false, Down, wantRelease -} - -// wantRelease waits for a mouse button release in the same area as -// the press. -func wantRelease(fsm *FSM, m *terminalapi.Mouse) (bool, State, stateFn) { - switch m.Button { - case fsm.button: - if m.Position.In(fsm.area) { - // Remain in the same state, since termbox reports move of mouse with - // button held down as a series of clicks, one per position. - return false, Down, wantRelease - } - return false, Up, wantPress - - case mouse.ButtonRelease: - if m.Position.In(fsm.area) { - // Seen both press and release, report a click. - return true, Up, wantPress - } - // Release the button even if the release event happened outside of the area. - return false, Up, wantPress - - default: - return false, Up, wantPress - } -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/braille/braille.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/braille/braille.go deleted file mode 100644 index 7cd902f87..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/braille/braille.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package braille provides a canvas that uses braille characters. - -This is inspired by https://github.com/asciimoo/drawille. - -The braille patterns documentation: -http://www.alanwood.net/unicode/braille_patterns.html - -The use of braille characters gives additional points (higher resolution) on -the canvas, each character cell now has eight pixels that can be set -independently. Specifically each cell has the following pixels, the axes grow -right and down. - -Each cell: - - X→ 0 1 Y - ┌───┐ ↓ - │● ●│ 0 - │● ●│ 1 - │● ●│ 2 - │● ●│ 3 - └───┘ - -When using the braille canvas, the coordinates address the sub-cell points -rather then cells themselves. However all points in the cell still share the -same cell options. -*/ -package braille - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/canvas" - "github.com/mum4k/termdash/terminal/terminalapi" -) - -const ( - // ColMult is the resolution multiplier for the width, i.e. two pixels per cell. - ColMult = 2 - - // RowMult is the resolution multiplier for the height, i.e. four pixels per cell. - RowMult = 4 - - // brailleCharOffset is the offset of the braille pattern unicode characters. - // From: http://www.alanwood.net/unicode/braille_patterns.html - brailleCharOffset = 0x2800 - - // brailleLastChar is the last braille pattern rune. - brailleLastChar = 0x28FF -) - -// pixelRunes maps points addressing individual pixels in a cell into character -// offset. I.e. the correct character to set pixel(0,0) is -// brailleCharOffset|pixelRunes[image.Point{0,0}]. -var pixelRunes = map[image.Point]rune{ - {0, 0}: 0x01, {1, 0}: 0x08, - {0, 1}: 0x02, {1, 1}: 0x10, - {0, 2}: 0x04, {1, 2}: 0x20, - {0, 3}: 0x40, {1, 3}: 0x80, -} - -// Canvas is a canvas that uses the braille patterns. It is two times wider -// and four times taller than a regular canvas that uses just plain characters, -// since each cell now has 2x4 pixels that can be independently set. -// -// The braille canvas is an abstraction built on top of a regular character -// canvas. After setting and toggling pixels on the braille canvas, it should -// be copied to a regular character canvas or applied to a terminal which -// results in setting of braille pattern characters. -// See the examples for more details. -// -// The created braille canvas can be smaller and even misaligned relatively to -// the regular character canvas or terminal, allowing the callers to create a -// "view" of just a portion of the canvas or terminal. -type Canvas struct { - // regular is the regular character canvas the braille canvas is based on. - regular *canvas.Canvas -} - -// New returns a new braille canvas for the provided area. -func New(ar image.Rectangle) (*Canvas, error) { - rc, err := canvas.New(ar) - if err != nil { - return nil, err - } - return &Canvas{ - regular: rc, - }, nil -} - -// Size returns the size of the braille canvas in pixels. -func (c *Canvas) Size() image.Point { - s := c.regular.Size() - return image.Point{s.X * ColMult, s.Y * RowMult} -} - -// CellArea returns the area of the underlying cell canvas in cells. -func (c *Canvas) CellArea() image.Rectangle { - return c.regular.Area() -} - -// Area returns the area of the braille canvas in pixels. -// This will be zero-based area that is two times wider and four times taller -// than the area used to create the braille canvas. -func (c *Canvas) Area() image.Rectangle { - ar := c.regular.Area() - return image.Rect(0, 0, ar.Dx()*ColMult, ar.Dy()*RowMult) -} - -// Clear clears all the content on the canvas. -func (c *Canvas) Clear() error { - return c.regular.Clear() -} - -// SetPixel turns on pixel at the specified point. -// The provided cell options will be applied to the entire cell (all of its -// pixels). This method is idempotent. -func (c *Canvas) SetPixel(p image.Point, opts ...cell.Option) error { - cp, err := c.cellPoint(p) - if err != nil { - return err - } - cell, err := c.regular.Cell(cp) - if err != nil { - return err - } - - var r rune - if isBraille(cell.Rune) { - // If the cell already has a braille pattern rune, we will be adding - // the pixel. - r = cell.Rune - } else { - r = brailleCharOffset - } - - r |= pixelRunes[pixelPoint(p)] - if _, err := c.regular.SetCell(cp, r, opts...); err != nil { - return err - } - return nil -} - -// ClearPixel turns off pixel at the specified point. -// The provided cell options will be applied to the entire cell (all of its -// pixels). This method is idempotent. -func (c *Canvas) ClearPixel(p image.Point, opts ...cell.Option) error { - cp, err := c.cellPoint(p) - if err != nil { - return err - } - cell, err := c.regular.Cell(cp) - if err != nil { - return err - } - - // Clear is idempotent. - if !isBraille(cell.Rune) || !pixelSet(cell.Rune, p) { - return nil - } - - r := cell.Rune & ^pixelRunes[pixelPoint(p)] - if _, err := c.regular.SetCell(cp, r, opts...); err != nil { - return err - } - return nil -} - -// TogglePixel toggles the state of the pixel at the specified point, i.e. it -// either sets or clear it depending on its current state. -// The provided cell options will be applied to the entire cell (all of its -// pixels). -func (c *Canvas) TogglePixel(p image.Point, opts ...cell.Option) error { - cp, err := c.cellPoint(p) - if err != nil { - return err - } - curCell, err := c.regular.Cell(cp) - if err != nil { - return err - } - - if isBraille(curCell.Rune) && pixelSet(curCell.Rune, p) { - return c.ClearPixel(p, opts...) - } - return c.SetPixel(p, opts...) -} - -// SetCellOpts sets options on the specified cell of the braille canvas without -// modifying the content of the cell. -// Sets the default cell options if no options are provided. -// This method is idempotent. -func (c *Canvas) SetCellOpts(cellPoint image.Point, opts ...cell.Option) error { - curCell, err := c.regular.Cell(cellPoint) - if err != nil { - return err - } - - if len(opts) == 0 { - // Set the default options. - opts = []cell.Option{ - cell.FgColor(cell.ColorDefault), - cell.BgColor(cell.ColorDefault), - } - } - if _, err := c.regular.SetCell(cellPoint, curCell.Rune, opts...); err != nil { - return err - } - return nil -} - -// SetAreaCellOpts is like SetCellOpts, but sets the specified options on all -// the cells within the provided area. -func (c *Canvas) SetAreaCellOpts(cellArea image.Rectangle, opts ...cell.Option) error { - haveArea := c.regular.Area() - if !cellArea.In(haveArea) { - return fmt.Errorf("unable to set cell options in area %v, it must fit inside the available cell area is %v", cellArea, haveArea) - } - for col := cellArea.Min.X; col < cellArea.Max.X; col++ { - for row := cellArea.Min.Y; row < cellArea.Max.Y; row++ { - if err := c.SetCellOpts(image.Point{col, row}, opts...); err != nil { - return err - } - } - } - return nil -} - -// Apply applies the canvas to the corresponding area of the terminal. -// Guarantees to stay within limits of the area the canvas was created with. -func (c *Canvas) Apply(t terminalapi.Terminal) error { - return c.regular.Apply(t) -} - -// CopyTo copies the content of this canvas onto the destination canvas. -// This canvas can have an offset when compared to the destination canvas, i.e. -// the area of this canvas doesn't have to be zero-based. -func (c *Canvas) CopyTo(dst *canvas.Canvas) error { - return c.regular.CopyTo(dst) -} - -// cellPoint determines the point (coordinate) of the character cell given -// coordinates in pixels. -func (c *Canvas) cellPoint(p image.Point) (image.Point, error) { - if p.X < 0 || p.Y < 0 { - return image.ZP, fmt.Errorf("pixels cannot have negative coordinates: %v", p) - } - cp := image.Point{p.X / ColMult, p.Y / RowMult} - if ar := c.regular.Area(); !cp.In(ar) { - return image.ZP, fmt.Errorf("pixel at%v would be in a character cell at%v which falls outside of the canvas area %v", p, cp, ar) - } - return cp, nil -} - -// isBraille determines if the rune is a braille pattern rune. -func isBraille(r rune) bool { - return r >= brailleCharOffset && r <= brailleLastChar -} - -// pixelSet returns true if the provided rune has the specified pixel set. -func pixelSet(r rune, p image.Point) bool { - return r&pixelRunes[pixelPoint(p)] > 0 -} - -// pixelPoint translates point within canvas to point within the target cell. -func pixelPoint(p image.Point) image.Point { - return image.Point{p.X % ColMult, p.Y % RowMult} -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/buffer/buffer.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/buffer/buffer.go deleted file mode 100644 index 5c21dd0ba..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/buffer/buffer.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package buffer implements a 2-D buffer of cells. -package buffer - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/area" - "github.com/mum4k/termdash/private/runewidth" -) - -// NewCells breaks the provided text into cells and applies the options. -func NewCells(text string, opts ...cell.Option) []*Cell { - var res []*Cell - for _, r := range text { - res = append(res, NewCell(r, opts...)) - } - return res -} - -// Cell represents a single cell on the terminal. -type Cell struct { - // Rune is the rune stored in the cell. - Rune rune - - // Opts are the cell options. - Opts *cell.Options -} - -// String implements fmt.Stringer. -func (c *Cell) String() string { - return fmt.Sprintf("{%q}", c.Rune) -} - -// NewCell returns a new cell. -func NewCell(r rune, opts ...cell.Option) *Cell { - return &Cell{ - Rune: r, - Opts: cell.NewOptions(opts...), - } -} - -// Copy returns a copy the cell. -func (c *Cell) Copy() *Cell { - return &Cell{ - Rune: c.Rune, - Opts: cell.NewOptions(c.Opts), - } -} - -// Apply applies the provided options to the cell. -func (c *Cell) Apply(opts ...cell.Option) { - for _, opt := range opts { - opt.Set(c.Opts) - } -} - -// Buffer is a 2-D buffer of cells. -// The axes increase right and down. -// Uninitialized buffer is invalid, use New to create an instance. -// Don't set cells directly, use the SetCell method instead which safely -// handles limits and wide unicode characters. -type Buffer [][]*Cell - -// New returns a new Buffer of the provided size. -func New(size image.Point) (Buffer, error) { - if size.X <= 0 { - return nil, fmt.Errorf("invalid buffer width (size.X): %d, must be a positive number", size.X) - } - if size.Y <= 0 { - return nil, fmt.Errorf("invalid buffer height (size.Y): %d, must be a positive number", size.Y) - } - - b := make([][]*Cell, size.X) - for col := range b { - b[col] = make([]*Cell, size.Y) - for row := range b[col] { - b[col][row] = NewCell(0) - } - } - return b, nil -} - -// SetCell sets the rune of the specified cell in the buffer. Returns the -// number of cells the rune occupies, wide runes can occupy multiple cells when -// printed on the terminal. See http://www.unicode.org/reports/tr11/. -// Use the options to specify which attributes to modify, if an attribute -// option isn't specified, the attribute retains its previous value. -func (b Buffer) SetCell(p image.Point, r rune, opts ...cell.Option) (int, error) { - partial, err := b.IsPartial(p) - if err != nil { - return -1, err - } - if partial { - return -1, fmt.Errorf("cannot set rune %q at point %v, it is a partial cell occupied by a wide rune in the previous cell", r, p) - } - - remW, err := b.RemWidth(p) - if err != nil { - return -1, err - } - rw := runewidth.RuneWidth(r) - if rw == 0 { - // Even if the rune is invisible, like the zero-value rune, it still - // occupies at least the target cell. - rw = 1 - } - if rw > remW { - return -1, fmt.Errorf("cannot set rune %q of width %d at point %v, only have %d remaining cells at this line", r, rw, p, remW) - } - - c := b[p.X][p.Y] - c.Rune = r - c.Apply(opts...) - return rw, nil -} - -// IsPartial returns true if the cell at the specified point holds a part of a -// full width rune from a previous cell. See -// http://www.unicode.org/reports/tr11/. -func (b Buffer) IsPartial(p image.Point) (bool, error) { - size := b.Size() - ar, err := area.FromSize(size) - if err != nil { - return false, err - } - - if !p.In(ar) { - return false, fmt.Errorf("point %v falls outside of the area %v occupied by the buffer", p, ar) - } - - if p.X == 0 && p.Y == 0 { - return false, nil - } - - prevP := image.Point{p.X - 1, p.Y} - if prevP.X < 0 { - prevP = image.Point{size.X - 1, p.Y - 1} - } - - prevR := b[prevP.X][prevP.Y].Rune - switch rw := runewidth.RuneWidth(prevR); rw { - case 0, 1: - return false, nil - case 2: - return true, nil - default: - return false, fmt.Errorf("buffer cell %v contains rune %q which has an unsupported rune with %d", prevP, prevR, rw) - } -} - -// RemWidth returns the remaining width (horizontal row of cells) available -// from and inclusive of the specified point. -func (b Buffer) RemWidth(p image.Point) (int, error) { - size := b.Size() - ar, err := area.FromSize(size) - if err != nil { - return -1, err - } - - if !p.In(ar) { - return -1, fmt.Errorf("point %v falls outside of the area %v occupied by the buffer", p, ar) - } - return size.X - p.X, nil -} - -// Size returns the size of the buffer. -func (b Buffer) Size() image.Point { - return image.Point{ - len(b), - len(b[0]), - } -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/canvas.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/canvas.go deleted file mode 100644 index 65a1e6963..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/canvas.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package canvas defines the canvas that the widgets draw on. -package canvas - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/area" - "github.com/mum4k/termdash/private/canvas/buffer" - "github.com/mum4k/termdash/private/runewidth" - "github.com/mum4k/termdash/terminal/terminalapi" -) - -// Canvas is where a widget draws its output for display on the terminal. -type Canvas struct { - // area is the area the buffer was created for. - // Contains absolute coordinates on the target terminal, while the buffer - // contains relative zero-based coordinates for this canvas. - area image.Rectangle - - // buffer is where the drawing happens. - buffer buffer.Buffer -} - -// New returns a new Canvas with a buffer for the provided area. -func New(ar image.Rectangle) (*Canvas, error) { - if ar.Min.X < 0 || ar.Min.Y < 0 || ar.Max.X < 0 || ar.Max.Y < 0 { - return nil, fmt.Errorf("area cannot start or end on the negative axis, got: %+v", ar) - } - - b, err := buffer.New(area.Size(ar)) - if err != nil { - return nil, err - } - return &Canvas{ - area: ar, - buffer: b, - }, nil -} - -// Size returns the size of the 2-D canvas. -func (c *Canvas) Size() image.Point { - return c.buffer.Size() -} - -// Area returns the area of the 2-D canvas. -func (c *Canvas) Area() image.Rectangle { - s := c.buffer.Size() - return image.Rect(0, 0, s.X, s.Y) -} - -// Clear clears all the content on the canvas. -func (c *Canvas) Clear() error { - b, err := buffer.New(c.Size()) - if err != nil { - return err - } - c.buffer = b - return nil -} - -// SetCell sets the rune of the specified cell on the canvas. Returns the -// number of cells the rune occupies, wide runes can occupy multiple cells when -// printed on the terminal. See http://www.unicode.org/reports/tr11/. -// Use the options to specify which attributes to modify, if an attribute -// option isn't specified, the attribute retains its previous value. -func (c *Canvas) SetCell(p image.Point, r rune, opts ...cell.Option) (int, error) { - return c.buffer.SetCell(p, r, opts...) -} - -// Cell returns a copy of the specified cell. -func (c *Canvas) Cell(p image.Point) (*buffer.Cell, error) { - ar, err := area.FromSize(c.Size()) - if err != nil { - return nil, err - } - if !p.In(ar) { - return nil, fmt.Errorf("point %v falls outside of the area %v occupied by the canvas", p, ar) - } - - return c.buffer[p.X][p.Y].Copy(), nil -} - -// SetCellOpts sets options on the specified cell of the canvas without -// modifying the content of the cell. -// Sets the default cell options if no options are provided. -// This method is idempotent. -func (c *Canvas) SetCellOpts(p image.Point, opts ...cell.Option) error { - curCell, err := c.Cell(p) - if err != nil { - return err - } - - if len(opts) == 0 { - // Set the default options. - opts = []cell.Option{ - cell.FgColor(cell.ColorDefault), - cell.BgColor(cell.ColorDefault), - } - } - if _, err := c.SetCell(p, curCell.Rune, opts...); err != nil { - return err - } - return nil -} - -// SetAreaCells is like SetCell, but sets the specified rune and options on all -// the cells within the provided area. -// This method is idempotent. -func (c *Canvas) SetAreaCells(cellArea image.Rectangle, r rune, opts ...cell.Option) error { - haveArea := c.Area() - if !cellArea.In(haveArea) { - return fmt.Errorf("unable to set cell runes in area %v, it must fit inside the available cell area is %v", cellArea, haveArea) - } - - rw := runewidth.RuneWidth(r) - for row := cellArea.Min.Y; row < cellArea.Max.Y; row++ { - for col := cellArea.Min.X; col < cellArea.Max.X; { - p := image.Point{col, row} - if col+rw > cellArea.Max.X { - break - } - cells, err := c.SetCell(p, r, opts...) - if err != nil { - return err - } - col += cells - } - } - return nil -} - -// SetAreaCellOpts is like SetCellOpts, but sets the specified options on all -// the cells within the provided area. -func (c *Canvas) SetAreaCellOpts(cellArea image.Rectangle, opts ...cell.Option) error { - haveArea := c.Area() - if !cellArea.In(haveArea) { - return fmt.Errorf("unable to set cell options in area %v, it must fit inside the available cell area is %v", cellArea, haveArea) - } - for col := cellArea.Min.X; col < cellArea.Max.X; col++ { - for row := cellArea.Min.Y; row < cellArea.Max.Y; row++ { - if err := c.SetCellOpts(image.Point{col, row}, opts...); err != nil { - return err - } - } - } - return nil -} - -// setCellFunc is a function that sets cell content on a terminal or a canvas. -type setCellFunc func(image.Point, rune, ...cell.Option) error - -// copyTo is the internal implementation of code that copies the content of a -// canvas. If a non zero offset is provided, all the copied points are offset by -// this amount. -// The dstSetCell function is called for every point in this canvas when -// copying it to the destination. -func (c *Canvas) copyTo(offset image.Point, dstSetCell setCellFunc) error { - for col := range c.buffer { - for row := range c.buffer[col] { - partial, err := c.buffer.IsPartial(image.Point{col, row}) - if err != nil { - return err - } - if partial { - // Skip over partial cells, i.e. cells that follow a cell - // containing a full-width rune. A full-width rune takes only - // one cell in the buffer, but two on the terminal. - // See http://www.unicode.org/reports/tr11/. - continue - } - cell := c.buffer[col][row] - p := image.Point{col, row}.Add(offset) - if err := dstSetCell(p, cell.Rune, cell.Opts); err != nil { - return fmt.Errorf("setCellFunc%v => error: %v", p, err) - } - } - } - return nil -} - -// Apply applies the canvas to the corresponding area of the terminal. -// Guarantees to stay within limits of the area the canvas was created with. -func (c *Canvas) Apply(t terminalapi.Terminal) error { - termArea, err := area.FromSize(t.Size()) - if err != nil { - return err - } - - bufArea, err := area.FromSize(c.buffer.Size()) - if err != nil { - return err - } - - if !bufArea.In(termArea) { - return fmt.Errorf("the canvas area %+v doesn't fit onto the terminal %+v", bufArea, termArea) - } - - // The image.Point{0, 0} of this canvas isn't always exactly at - // image.Point{0, 0} on the terminal. - // Depends on area assigned by the container. - offset := c.area.Min - return c.copyTo(offset, t.SetCell) -} - -// CopyTo copies the content of this canvas onto the destination canvas. -// This canvas can have an offset when compared to the destination canvas, i.e. -// the area of this canvas doesn't have to be zero-based. -func (c *Canvas) CopyTo(dst *Canvas) error { - if !c.area.In(dst.Area()) { - return fmt.Errorf("the canvas area %v doesn't fit or lie inside the destination canvas area %v", c.area, dst.Area()) - } - - fn := setCellFunc(func(p image.Point, r rune, opts ...cell.Option) error { - if _, err := dst.SetCell(p, r, opts...); err != nil { - return fmt.Errorf("dst.SetCell => %v", err) - } - return nil - }) - - // Neither of the two canvases (source and destination) have to be zero - // based. Canvas is not zero based if it is positioned elsewhere, i.e. - // providing a smaller view of another canvas. - // E.g. a widget can assign a smaller portion of its canvas to a component - // in order to restrict drawing of this component to a smaller area. To do - // this it can create a sub-canvas. This sub-canvas can have a specific - // starting position other than image.Point{0, 0} relative to the parent - // canvas. Copying this sub-canvas back onto the parent accounts for this - // offset. - offset := c.area.Min - return c.copyTo(offset, fn) -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/border.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/border.go deleted file mode 100644 index a19ec096c..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/border.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -// border.go contains code that draws borders. - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/align" - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/linestyle" - "github.com/mum4k/termdash/private/alignfor" - "github.com/mum4k/termdash/private/canvas" -) - -// BorderOption is used to provide options to Border(). -type BorderOption interface { - // set sets the provided option. - set(*borderOptions) -} - -// borderOptions stores the provided options. -type borderOptions struct { - cellOpts []cell.Option - lineStyle linestyle.LineStyle - title string - titleOM OverrunMode - titleCellOpts []cell.Option - titleHAlign align.Horizontal -} - -// borderOption implements BorderOption. -type borderOption func(bOpts *borderOptions) - -// set implements BorderOption.set. -func (bo borderOption) set(bOpts *borderOptions) { - bo(bOpts) -} - -// DefaultBorderLineStyle is the default value for the BorderLineStyle option. -const DefaultBorderLineStyle = linestyle.Light - -// BorderLineStyle sets the style of the line used to draw the border. -func BorderLineStyle(ls linestyle.LineStyle) BorderOption { - return borderOption(func(bOpts *borderOptions) { - bOpts.lineStyle = ls - }) -} - -// BorderCellOpts sets options on the cells that create the border. -func BorderCellOpts(opts ...cell.Option) BorderOption { - return borderOption(func(bOpts *borderOptions) { - bOpts.cellOpts = opts - }) -} - -// BorderTitle sets a title for the border. -func BorderTitle(title string, overrun OverrunMode, opts ...cell.Option) BorderOption { - return borderOption(func(bOpts *borderOptions) { - bOpts.title = title - bOpts.titleOM = overrun - bOpts.titleCellOpts = opts - }) -} - -// BorderTitleAlign configures the horizontal alignment for the title. -func BorderTitleAlign(h align.Horizontal) BorderOption { - return borderOption(func(bOpts *borderOptions) { - bOpts.titleHAlign = h - }) -} - -// borderChar returns the correct border character from the parts for the use -// at the specified point of the border. Returns -1 if no character should be at -// this point. -func borderChar(p image.Point, border image.Rectangle, parts map[linePart]rune) rune { - switch { - case p.X == border.Min.X && p.Y == border.Min.Y: - return parts[topLeftCorner] - case p.X == border.Max.X-1 && p.Y == border.Min.Y: - return parts[topRightCorner] - case p.X == border.Min.X && p.Y == border.Max.Y-1: - return parts[bottomLeftCorner] - case p.X == border.Max.X-1 && p.Y == border.Max.Y-1: - return parts[bottomRightCorner] - case p.X == border.Min.X || p.X == border.Max.X-1: - return parts[vLine] - case p.Y == border.Min.Y || p.Y == border.Max.Y-1: - return parts[hLine] - } - return -1 -} - -// drawTitle draws a text title at the top of the border. -func drawTitle(c *canvas.Canvas, border image.Rectangle, opt *borderOptions) error { - // Don't attempt to draw the title if there isn't space for at least one rune. - // The title must not overwrite any of the corner runes on the border so we - // need the following minimum width. - const minForTitle = 3 - if border.Dx() < minForTitle { - return nil - } - - available := image.Rect( - border.Min.X+1, // One space for the top left corner char. - border.Min.Y, - border.Max.X-1, // One space for the top right corner char. - border.Min.Y+1, - ) - start, err := alignfor.Text(available, opt.title, opt.titleHAlign, align.VerticalTop) - if err != nil { - return err - } - - return Text( - c, opt.title, start, - TextCellOpts(opt.titleCellOpts...), - TextOverrunMode(opt.titleOM), - TextMaxX(available.Max.X), - ) -} - -// Border draws a border on the canvas. -func Border(c *canvas.Canvas, border image.Rectangle, opts ...BorderOption) error { - if ar := c.Area(); !border.In(ar) { - return fmt.Errorf("the requested border %+v falls outside of the provided canvas %+v", border, ar) - } - - const minSize = 2 - if border.Dx() < minSize || border.Dy() < minSize { - return fmt.Errorf("the smallest supported border is %dx%d, got: %dx%d", minSize, minSize, border.Dx(), border.Dy()) - } - - opt := &borderOptions{ - lineStyle: DefaultBorderLineStyle, - } - for _, o := range opts { - o.set(opt) - } - - parts, err := lineParts(opt.lineStyle) - if err != nil { - return err - } - - for col := border.Min.X; col < border.Max.X; col++ { - for row := border.Min.Y; row < border.Max.Y; row++ { - p := image.Point{col, row} - r := borderChar(p, border, parts) - if r == -1 { - continue - } - - cells, err := c.SetCell(p, r, opt.cellOpts...) - if err != nil { - return err - } - if cells != 1 { - panic(fmt.Sprintf("invalid border rune %q, this rune occupies %d cells, border implementation only supports half-width runes that occupy exactly one cell", r, cells)) - } - } - } - - if opt.title != "" { - return drawTitle(c, border, opt) - } - return nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_circle.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_circle.go deleted file mode 100644 index d2b3b86bc..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_circle.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -// braille_circle.go contains code that draws circles on a braille canvas. - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/canvas/braille" - "github.com/mum4k/termdash/private/numbers/trig" -) - -// BrailleCircleOption is used to provide options to BrailleCircle. -type BrailleCircleOption interface { - // set sets the provided option. - set(*brailleCircleOptions) -} - -// brailleCircleOptions stores the provided options. -type brailleCircleOptions struct { - cellOpts []cell.Option - filled bool - pixelChange braillePixelChange - - arcOnly bool - startDegree int - endDegree int -} - -// newBrailleCircleOptions returns a new brailleCircleOptions instance. -func newBrailleCircleOptions() *brailleCircleOptions { - return &brailleCircleOptions{ - pixelChange: braillePixelChangeSet, - } -} - -// validate validates the provided options. -func (opts *brailleCircleOptions) validate() error { - if !opts.arcOnly { - return nil - } - - if opts.startDegree == opts.endDegree { - return fmt.Errorf("invalid degree range, start %d and end %d cannot be equal", opts.startDegree, opts.endDegree) - } - return nil -} - -// brailleCircleOption implements BrailleCircleOption. -type brailleCircleOption func(*brailleCircleOptions) - -// set implements BrailleCircleOption.set. -func (o brailleCircleOption) set(opts *brailleCircleOptions) { - o(opts) -} - -// BrailleCircleCellOpts sets options on the cells that contain the circle. -// Cell options on a braille canvas can only be set on the entire cell, not per -// pixel. -func BrailleCircleCellOpts(cOpts ...cell.Option) BrailleCircleOption { - return brailleCircleOption(func(opts *brailleCircleOptions) { - opts.cellOpts = cOpts - }) -} - -// BrailleCircleFilled indicates that the drawn circle should be filled. -func BrailleCircleFilled() BrailleCircleOption { - return brailleCircleOption(func(opts *brailleCircleOptions) { - opts.filled = true - }) -} - -// BrailleCircleArcOnly indicates that only a portion of the circle should be drawn. -// The arc will be between the two provided angles in degrees. -// Each angle must be in range 0 <= angle <= 360. Start and end must not be equal. -// The zero angle is on the X axis, angles grow counter-clockwise. -func BrailleCircleArcOnly(startDegree, endDegree int) BrailleCircleOption { - return brailleCircleOption(func(opts *brailleCircleOptions) { - opts.arcOnly = true - opts.startDegree = startDegree - opts.endDegree = endDegree - - }) -} - -// BrailleCircleClearPixels changes the behavior of BrailleCircle, so that it -// clears the pixels belonging to the circle instead of setting them. -// Useful in order to "erase" a circle from the canvas as opposed to drawing one. -func BrailleCircleClearPixels() BrailleCircleOption { - return brailleCircleOption(func(opts *brailleCircleOptions) { - opts.pixelChange = braillePixelChangeClear - }) -} - -// BrailleCircle draws an approximated circle with the specified mid point and radius. -// The mid point must be a valid pixel within the canvas. -// All the points that form the circle must fit into the canvas. -// The smallest valid radius is two. -func BrailleCircle(bc *braille.Canvas, mid image.Point, radius int, opts ...BrailleCircleOption) error { - if ar := bc.Area(); !mid.In(ar) { - return fmt.Errorf("unable to draw circle with mid point %v which is outside of the braille canvas area %v", mid, ar) - } - if min := 2; radius < min { - return fmt.Errorf("unable to draw circle with radius %d, must be in range %d <= radius", radius, min) - } - - opt := newBrailleCircleOptions() - for _, o := range opts { - o.set(opt) - } - - if err := opt.validate(); err != nil { - return err - } - - points := circlePoints(mid, radius) - if opt.arcOnly { - f, err := trig.FilterByAngle(points, mid, opt.startDegree, opt.endDegree) - if err != nil { - return err - } - points = f - if opt.filled && (opt.startDegree != 0 || opt.endDegree != 360) { - points = append(points, openingPoints(mid, radius, opt)...) - } - } - if err := drawPoints(bc, points, opt); err != nil { - return fmt.Errorf("failed to draw circle with mid:%v, radius:%d, start:%d degrees, end:%d degrees: %v", mid, radius, opt.startDegree, opt.endDegree, err) - } - if opt.filled { - return fillCircle(bc, points, mid, radius, opt) - } - return nil -} - -// drawPoints draws the points onto the canvas. -func drawPoints(bc *braille.Canvas, points []image.Point, opt *brailleCircleOptions) error { - for _, p := range points { - switch opt.pixelChange { - case braillePixelChangeSet: - if err := bc.SetPixel(p, opt.cellOpts...); err != nil { - return fmt.Errorf("SetPixel => %v", err) - } - case braillePixelChangeClear: - if err := bc.ClearPixel(p, opt.cellOpts...); err != nil { - return fmt.Errorf("ClearPixel => %v", err) - } - - } - } - return nil -} - -// fillCircle fills a circle that consists of the provided point and has the -// mid point and radius. -func fillCircle(bc *braille.Canvas, points []image.Point, mid image.Point, radius int, opt *brailleCircleOptions) error { - lineOpts := []BrailleLineOption{ - BrailleLineCellOpts(opt.cellOpts...), - } - fillOpts := []BrailleFillOption{ - BrailleFillCellOpts(opt.cellOpts...), - } - if opt.pixelChange == braillePixelChangeClear { - lineOpts = append(lineOpts, BrailleLineClearPixels()) - fillOpts = append(fillOpts, BrailleFillClearPixels()) - } - - // Determine a fill point that should be inside of the circle sector. - midA, err := trig.RangeMid(opt.startDegree, opt.endDegree) - if err != nil { - return err - } - fp := trig.CirclePointAtAngle(midA, mid, radius-1) - - // Ensure the fill point falls inside the circle. - // If drawing a partial circle, it must also fall within points belonging - // to the opening. - // This might not be true if drawing a partial circle and the arc is very - // small. - shape := points - if opt.arcOnly { - startP := trig.CirclePointAtAngle(opt.startDegree, mid, radius-1) - endP := trig.CirclePointAtAngle(opt.endDegree, mid, radius-1) - shape = append(shape, startP, endP) - } - if trig.PointIsIn(fp, shape) { - if err := BrailleFill(bc, fp, points, fillOpts...); err != nil { - return err - } - if err := BrailleLine(bc, mid, fp, lineOpts...); err != nil { - return err - } - } - return nil -} - -// openingPoints returns points on the lines from the mid point to the circle -// opening when drawing an incomplete circle. -func openingPoints(mid image.Point, radius int, opt *brailleCircleOptions) []image.Point { - var points []image.Point - startP := trig.CirclePointAtAngle(opt.startDegree, mid, radius) - endP := trig.CirclePointAtAngle(opt.endDegree, mid, radius) - points = append(points, brailleLinePoints(mid, startP)...) - points = append(points, brailleLinePoints(mid, endP)...) - return points -} - -// circlePoints returns a list of points that represent a circle with -// the specified mid point and radius. -func circlePoints(mid image.Point, radius int) []image.Point { - var points []image.Point - - // Bresenham algorithm. - // https://en.wikipedia.org/wiki/Midpoint_circle_algorithm - x := radius - y := 0 - dx := 1 - dy := 1 - diff := dx - (radius << 1) // Cheap multiplication by two. - - for x >= y { - points = append( - points, - image.Point{mid.X + x, mid.Y + y}, - image.Point{mid.X + y, mid.Y + x}, - image.Point{mid.X - y, mid.Y + x}, - image.Point{mid.X - x, mid.Y + y}, - image.Point{mid.X - x, mid.Y - y}, - image.Point{mid.X - y, mid.Y - x}, - image.Point{mid.X + y, mid.Y - x}, - image.Point{mid.X + x, mid.Y - y}, - ) - - if diff <= 0 { - y++ - diff += dy - dy += 2 - } - - if diff > 0 { - x-- - dx += 2 - diff += dx - (radius << 1) - } - - } - return points -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_fill.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_fill.go deleted file mode 100644 index 8bb311f1c..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_fill.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -// braille_fill.go implements the flood-fill algorithm for filling shapes on the braille canvas. - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/canvas/braille" -) - -// BrailleFillOption is used to provide options to BrailleFill. -type BrailleFillOption interface { - // set sets the provided option. - set(*brailleFillOptions) -} - -// brailleFillOptions stores the provided options. -type brailleFillOptions struct { - cellOpts []cell.Option - pixelChange braillePixelChange -} - -// newBrailleFillOptions returns a new brailleFillOptions instance. -func newBrailleFillOptions() *brailleFillOptions { - return &brailleFillOptions{ - pixelChange: braillePixelChangeSet, - } -} - -// brailleFillOption implements BrailleFillOption. -type brailleFillOption func(*brailleFillOptions) - -// set implements BrailleFillOption.set. -func (o brailleFillOption) set(opts *brailleFillOptions) { - o(opts) -} - -// BrailleFillCellOpts sets options on the cells that are set as part of -// filling shapes. -// Cell options on a braille canvas can only be set on the entire cell, not per -// pixel. -func BrailleFillCellOpts(cOpts ...cell.Option) BrailleFillOption { - return brailleFillOption(func(opts *brailleFillOptions) { - opts.cellOpts = cOpts - }) -} - -// BrailleFillClearPixels changes the behavior of BrailleFill, so that it -// clears the pixels instead of setting them. -// Useful in order to "erase" the filled area as opposed to drawing one. -func BrailleFillClearPixels() BrailleFillOption { - return brailleFillOption(func(opts *brailleFillOptions) { - opts.pixelChange = braillePixelChangeClear - }) -} - -// BrailleFill fills the braille canvas starting at the specified point. -// The function will not fill or cross over any points in the defined border. -// The start point must be in the canvas. -func BrailleFill(bc *braille.Canvas, start image.Point, border []image.Point, opts ...BrailleFillOption) error { - if ar := bc.Area(); !start.In(ar) { - return fmt.Errorf("unable to start filling canvas at point %v which is outside of the braille canvas area %v", start, ar) - } - - opt := newBrailleFillOptions() - for _, o := range opts { - o.set(opt) - } - - b := map[image.Point]struct{}{} - for _, p := range border { - b[p] = struct{}{} - } - - v := newVisitable(bc.Area(), b) - visitor := func(p image.Point) error { - switch opt.pixelChange { - case braillePixelChangeSet: - return bc.SetPixel(p, opt.cellOpts...) - case braillePixelChangeClear: - return bc.ClearPixel(p, opt.cellOpts...) - } - return nil - } - return brailleDFS(v, start, visitor) -} - -// visitable represents an area that can be visited. -// It tracks nodes that are already visited. -type visitable struct { - area image.Rectangle - visited map[image.Point]struct{} -} - -// newVisitable returns a new visitable object initialized for the provided -// area and already visited nodes. -func newVisitable(ar image.Rectangle, visited map[image.Point]struct{}) *visitable { - if visited == nil { - visited = map[image.Point]struct{}{} - } - return &visitable{ - area: ar, - visited: visited, - } -} - -// neighborsAt returns all valid neighbors for the specified point. -func (v *visitable) neighborsAt(p image.Point) []image.Point { - var res []image.Point - for _, neigh := range []image.Point{ - {p.X - 1, p.Y}, // left - {p.X + 1, p.Y}, // right - {p.X, p.Y - 1}, // up - {p.X, p.Y + 1}, // down - } { - if !neigh.In(v.area) { - continue - } - if _, ok := v.visited[neigh]; ok { - continue - } - v.visited[neigh] = struct{}{} - res = append(res, neigh) - } - return res -} - -// brailleDFS visits every point in the area and runs the visitor function. -func brailleDFS(v *visitable, p image.Point, visitFn func(image.Point) error) error { - neigh := v.neighborsAt(p) - if len(neigh) == 0 { - return nil - } - - for _, n := range neigh { - if err := visitFn(n); err != nil { - return err - } - if err := brailleDFS(v, n, visitFn); err != nil { - return err - } - } - return nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_line.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_line.go deleted file mode 100644 index c9f412321..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_line.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -// braille_line.go contains code that draws lines on a braille canvas. - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/canvas/braille" - "github.com/mum4k/termdash/private/numbers" -) - -// braillePixelChange represents an action on a pixel on the braille canvas. -type braillePixelChange int - -// String implements fmt.Stringer() -func (bpc braillePixelChange) String() string { - if n, ok := braillePixelChangeNames[bpc]; ok { - return n - } - return "braillePixelChangeUnknown" -} - -// braillePixelChangeNames maps braillePixelChange values to human readable names. -var braillePixelChangeNames = map[braillePixelChange]string{ - braillePixelChangeSet: "braillePixelChangeSet", - braillePixelChangeClear: "braillePixelChangeClear", -} - -const ( - braillePixelChangeUnknown braillePixelChange = iota - - braillePixelChangeSet - braillePixelChangeClear -) - -// BrailleLineOption is used to provide options to BrailleLine(). -type BrailleLineOption interface { - // set sets the provided option. - set(*brailleLineOptions) -} - -// brailleLineOptions stores the provided options. -type brailleLineOptions struct { - cellOpts []cell.Option - pixelChange braillePixelChange -} - -// newBrailleLineOptions returns a new brailleLineOptions instance. -func newBrailleLineOptions() *brailleLineOptions { - return &brailleLineOptions{ - pixelChange: braillePixelChangeSet, - } -} - -// brailleLineOption implements BrailleLineOption. -type brailleLineOption func(*brailleLineOptions) - -// set implements BrailleLineOption.set. -func (o brailleLineOption) set(opts *brailleLineOptions) { - o(opts) -} - -// BrailleLineCellOpts sets options on the cells that contain the line. -// Cell options on a braille canvas can only be set on the entire cell, not per -// pixel. -func BrailleLineCellOpts(cOpts ...cell.Option) BrailleLineOption { - return brailleLineOption(func(opts *brailleLineOptions) { - opts.cellOpts = cOpts - }) -} - -// BrailleLineClearPixels changes the behavior of BrailleLine, so that it -// clears the pixels belonging to the line instead of setting them. -// Useful in order to "erase" a line from the canvas as opposed to drawing one. -func BrailleLineClearPixels() BrailleLineOption { - return brailleLineOption(func(opts *brailleLineOptions) { - opts.pixelChange = braillePixelChangeClear - }) -} - -// BrailleLine draws an approximated line segment on the braille canvas between -// the two provided points. -// Both start and end must be valid points within the canvas. Start and end can -// be the same point in which case only one pixel will be set on the braille -// canvas. -// The start or end coordinates must not be negative. -func BrailleLine(bc *braille.Canvas, start, end image.Point, opts ...BrailleLineOption) error { - if start.X < 0 || start.Y < 0 { - return fmt.Errorf("the start coordinates cannot be negative, got: %v", start) - } - if end.X < 0 || end.Y < 0 { - return fmt.Errorf("the end coordinates cannot be negative, got: %v", end) - } - - opt := newBrailleLineOptions() - for _, o := range opts { - o.set(opt) - } - - points := brailleLinePoints(start, end) - for _, p := range points { - switch opt.pixelChange { - case braillePixelChangeSet: - if err := bc.SetPixel(p, opt.cellOpts...); err != nil { - return fmt.Errorf("bc.SetPixel(%v) => %v", p, err) - } - case braillePixelChangeClear: - if err := bc.ClearPixel(p, opt.cellOpts...); err != nil { - return fmt.Errorf("bc.ClearPixel(%v) => %v", p, err) - } - } - } - return nil -} - -// brailleLinePoints returns the points to set when drawing the line. -func brailleLinePoints(start, end image.Point) []image.Point { - // Implements Bresenham's line algorithm. - // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm - - vertProj := numbers.Abs(end.Y - start.Y) - horizProj := numbers.Abs(end.X - start.X) - if vertProj < horizProj { - if start.X > end.X { - return lineLow(end.X, end.Y, start.X, start.Y) - } - return lineLow(start.X, start.Y, end.X, end.Y) - } - if start.Y > end.Y { - return lineHigh(end.X, end.Y, start.X, start.Y) - } - return lineHigh(start.X, start.Y, end.X, end.Y) -} - -// lineLow returns points that create a line whose horizontal projection -// (end.X - start.X) is longer than its vertical projection -// (end.Y - start.Y). -func lineLow(x0, y0, x1, y1 int) []image.Point { - deltaX := x1 - x0 - deltaY := y1 - y0 - - stepY := 1 - if deltaY < 0 { - stepY = -1 - deltaY = -deltaY - } - - var res []image.Point - diff := 2*deltaY - deltaX - y := y0 - for x := x0; x <= x1; x++ { - res = append(res, image.Point{x, y}) - if diff > 0 { - y += stepY - diff -= 2 * deltaX - } - diff += 2 * deltaY - } - return res -} - -// lineHigh returns points that createa line whose vertical projection -// (end.Y - start.Y) is longer than its horizontal projection -// (end.X - start.X). -func lineHigh(x0, y0, x1, y1 int) []image.Point { - deltaX := x1 - x0 - deltaY := y1 - y0 - - stepX := 1 - if deltaX < 0 { - stepX = -1 - deltaX = -deltaX - } - - var res []image.Point - diff := 2*deltaX - deltaY - x := x0 - for y := y0; y <= y1; y++ { - res = append(res, image.Point{x, y}) - - if diff > 0 { - x += stepX - diff -= 2 * deltaY - } - diff += 2 * deltaX - } - return res -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/draw.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/draw.go deleted file mode 100644 index 37c01bf7e..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/draw.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package draw provides functions that draw lines, shapes, etc on 2-D terminal -// like canvases. -package draw diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line.go deleted file mode 100644 index 35318f42d..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -// hv_line.go contains code that draws horizontal and vertical lines. - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/linestyle" - "github.com/mum4k/termdash/private/canvas" -) - -// HVLineOption is used to provide options to HVLine(). -type HVLineOption interface { - // set sets the provided option. - set(*hVLineOptions) -} - -// hVLineOptions stores the provided options. -type hVLineOptions struct { - cellOpts []cell.Option - lineStyle linestyle.LineStyle -} - -// newHVLineOptions returns a new hVLineOptions instance. -func newHVLineOptions() *hVLineOptions { - return &hVLineOptions{ - lineStyle: DefaultLineStyle, - } -} - -// hVLineOption implements HVLineOption. -type hVLineOption func(*hVLineOptions) - -// set implements HVLineOption.set. -func (o hVLineOption) set(opts *hVLineOptions) { - o(opts) -} - -// DefaultLineStyle is the default value for the HVLineStyle option. -const DefaultLineStyle = linestyle.Light - -// HVLineStyle sets the style of the line. -// Defaults to DefaultLineStyle. -func HVLineStyle(ls linestyle.LineStyle) HVLineOption { - return hVLineOption(func(opts *hVLineOptions) { - opts.lineStyle = ls - }) -} - -// HVLineCellOpts sets options on the cells that contain the line. -func HVLineCellOpts(cOpts ...cell.Option) HVLineOption { - return hVLineOption(func(opts *hVLineOptions) { - opts.cellOpts = cOpts - }) -} - -// HVLine represents one horizontal or vertical line. -type HVLine struct { - // Start is the cell where the line starts. - Start image.Point - // End is the cell where the line ends. - End image.Point -} - -// HVLines draws horizontal or vertical lines. Handles drawing of the correct -// characters for locations where any two lines cross (e.g. a corner, a T shape -// or a cross). Each line must be at least two cells long. Both start and end -// must be on the same horizontal (same X coordinate) or same vertical (same Y -// coordinate) line. -func HVLines(c *canvas.Canvas, lines []HVLine, opts ...HVLineOption) error { - opt := newHVLineOptions() - for _, o := range opts { - o.set(opt) - } - - g := newHVLineGraph() - for _, l := range lines { - line, err := newHVLine(c, l.Start, l.End, opt) - if err != nil { - return err - } - g.addLine(line) - - switch { - case line.horizontal(): - for curX := line.start.X; ; curX++ { - cur := image.Point{curX, line.start.Y} - if _, err := c.SetCell(cur, line.mainPart, opt.cellOpts...); err != nil { - return err - } - - if curX == line.end.X { - break - } - } - - case line.vertical(): - for curY := line.start.Y; ; curY++ { - cur := image.Point{line.start.X, curY} - if _, err := c.SetCell(cur, line.mainPart, opt.cellOpts...); err != nil { - return err - } - - if curY == line.end.Y { - break - } - } - } - } - - for _, n := range g.multiEdgeNodes() { - r, err := n.rune(opt.lineStyle) - if err != nil { - return err - } - if _, err := c.SetCell(n.p, r, opt.cellOpts...); err != nil { - return err - } - } - - return nil -} - -// hVLine represents a line that will be drawn on the canvas. -type hVLine struct { - // start is the starting point of the line. - start image.Point - - // end is the ending point of the line. - end image.Point - - // mainPart is either parts[vLine] or parts[hLine] depending on whether - // this is horizontal or vertical line. - mainPart rune - - // opts are the options provided in a call to HVLine(). - opts *hVLineOptions -} - -// newHVLine creates a new hVLine instance. -// Swaps start and end if necessary, so that horizontal drawing is always left -// to right and vertical is always top down. -func newHVLine(c *canvas.Canvas, start, end image.Point, opts *hVLineOptions) (*hVLine, error) { - if ar := c.Area(); !start.In(ar) || !end.In(ar) { - return nil, fmt.Errorf("both the start%v and the end%v must be in the canvas area: %v", start, end, ar) - } - - parts, err := lineParts(opts.lineStyle) - if err != nil { - return nil, err - } - - var mainPart rune - switch { - case start.X != end.X && start.Y != end.Y: - return nil, fmt.Errorf("can only draw horizontal (same X coordinates) or vertical (same Y coordinates), got start:%v end:%v", start, end) - - case start.X == end.X && start.Y == end.Y: - return nil, fmt.Errorf("the line must at least one cell long, got start%v, end%v", start, end) - - case start.X == end.X: - mainPart = parts[vLine] - if start.Y > end.Y { - start, end = end, start - } - - case start.Y == end.Y: - mainPart = parts[hLine] - if start.X > end.X { - start, end = end, start - } - - } - - return &hVLine{ - start: start, - end: end, - mainPart: mainPart, - opts: opts, - }, nil -} - -// horizontal determines if this is a horizontal line. -func (hvl *hVLine) horizontal() bool { - return hvl.mainPart == lineStyleChars[hvl.opts.lineStyle][hLine] -} - -// vertical determines if this is a vertical line. -func (hvl *hVLine) vertical() bool { - return hvl.mainPart == lineStyleChars[hvl.opts.lineStyle][vLine] -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line_graph.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line_graph.go deleted file mode 100644 index ccbc72a57..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line_graph.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -// hv_line_graph.go helps to keep track of locations where lines cross. - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/linestyle" -) - -// hVLineEdge is an edge between two points on the graph. -type hVLineEdge struct { - // from is the starting node of this edge. - // From is guaranteed to be less than to. - from image.Point - - // to is the ending point of this edge. - to image.Point -} - -// newHVLineEdge returns a new edge between the two points. -func newHVLineEdge(from, to image.Point) hVLineEdge { - return hVLineEdge{ - from: from, - to: to, - } -} - -// hVLineNode represents one node in the graph. -// I.e. one cell. -type hVLineNode struct { - // p is the point where this node is. - p image.Point - - // edges are the edges between this node and the surrounding nodes. - // The code only supports horizontal and vertical lines so there can only - // ever be edges to nodes on these planes. - edges map[hVLineEdge]bool -} - -// newHVLineNode creates a new newHVLineNode. -func newHVLineNode(p image.Point) *hVLineNode { - return &hVLineNode{ - p: p, - edges: map[hVLineEdge]bool{}, - } -} - -// hasDown determines if this node has an edge to the one below it. -func (n *hVLineNode) hasDown() bool { - target := newHVLineEdge(n.p, image.Point{n.p.X, n.p.Y + 1}) - _, ok := n.edges[target] - return ok -} - -// hasUp determines if this node has an edge to the one above it. -func (n *hVLineNode) hasUp() bool { - target := newHVLineEdge(image.Point{n.p.X, n.p.Y - 1}, n.p) - _, ok := n.edges[target] - return ok -} - -// hasLeft determines if this node has an edge to the next node on the left. -func (n *hVLineNode) hasLeft() bool { - target := newHVLineEdge(image.Point{n.p.X - 1, n.p.Y}, n.p) - _, ok := n.edges[target] - return ok -} - -// hasRight determines if this node has an edge to the next node on the right. -func (n *hVLineNode) hasRight() bool { - target := newHVLineEdge(n.p, image.Point{n.p.X + 1, n.p.Y}) - _, ok := n.edges[target] - return ok -} - -// rune, given the selected line style returns the correct line character to -// represent this node. -// Only handles nodes with two or more edges, as returned by multiEdgeNodes(). -func (n *hVLineNode) rune(ls linestyle.LineStyle) (rune, error) { - parts, err := lineParts(ls) - if err != nil { - return -1, err - } - - switch len(n.edges) { - case 2: - switch { - case n.hasLeft() && n.hasRight(): - return parts[hLine], nil - case n.hasUp() && n.hasDown(): - return parts[vLine], nil - case n.hasDown() && n.hasRight(): - return parts[topLeftCorner], nil - case n.hasDown() && n.hasLeft(): - return parts[topRightCorner], nil - case n.hasUp() && n.hasRight(): - return parts[bottomLeftCorner], nil - case n.hasUp() && n.hasLeft(): - return parts[bottomRightCorner], nil - default: - return -1, fmt.Errorf("unexpected two edges in node representing point %v: %v", n.p, n.edges) - } - - case 3: - switch { - case n.hasUp() && n.hasLeft() && n.hasRight(): - return parts[hAndUp], nil - case n.hasDown() && n.hasLeft() && n.hasRight(): - return parts[hAndDown], nil - case n.hasUp() && n.hasDown() && n.hasRight(): - return parts[vAndRight], nil - case n.hasUp() && n.hasDown() && n.hasLeft(): - return parts[vAndLeft], nil - - default: - return -1, fmt.Errorf("unexpected three edges in node representing point %v: %v", n.p, n.edges) - } - - case 4: - return parts[vAndH], nil - default: - return -1, fmt.Errorf("unexpected number of edges(%d) in node representing point %v", len(n.edges), n.p) - } -} - -// hVLineGraph represents lines on the canvas as a bidirectional graph of -// nodes. Helps to determine the characters that should be used where multiple -// lines cross. -type hVLineGraph struct { - nodes map[image.Point]*hVLineNode -} - -// newHVLineGraph creates a new hVLineGraph. -func newHVLineGraph() *hVLineGraph { - return &hVLineGraph{ - nodes: make(map[image.Point]*hVLineNode), - } -} - -// getOrCreateNode gets an existing or creates a new node for the point. -func (g *hVLineGraph) getOrCreateNode(p image.Point) *hVLineNode { - if n, ok := g.nodes[p]; ok { - return n - } - n := newHVLineNode(p) - g.nodes[p] = n - return n -} - -// addLine adds a line to the graph. -// This adds edges between all the points on the line. -func (g *hVLineGraph) addLine(line *hVLine) { - switch { - case line.horizontal(): - for curX := line.start.X; curX < line.end.X; curX++ { - from := image.Point{curX, line.start.Y} - to := image.Point{curX + 1, line.start.Y} - n1 := g.getOrCreateNode(from) - n2 := g.getOrCreateNode(to) - edge := newHVLineEdge(from, to) - n1.edges[edge] = true - n2.edges[edge] = true - } - - case line.vertical(): - for curY := line.start.Y; curY < line.end.Y; curY++ { - from := image.Point{line.start.X, curY} - to := image.Point{line.start.X, curY + 1} - n1 := g.getOrCreateNode(from) - n2 := g.getOrCreateNode(to) - edge := newHVLineEdge(from, to) - n1.edges[edge] = true - n2.edges[edge] = true - } - } -} - -// multiEdgeNodes returns all nodes that have more than one edge. These are -// the nodes where we might need to use different line characters to represent -// the crossing of multiple lines. -func (g *hVLineGraph) multiEdgeNodes() []*hVLineNode { - var nodes []*hVLineNode - for _, n := range g.nodes { - if len(n.edges) <= 1 { - continue - } - nodes = append(nodes, n) - } - return nodes -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/line_style.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/line_style.go deleted file mode 100644 index 41f1df4ee..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/line_style.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -import ( - "fmt" - - "github.com/mum4k/termdash/linestyle" - "github.com/mum4k/termdash/private/runewidth" -) - -// line_style.go contains the Unicode characters used for drawing lines of -// different styles. - -// lineStyleChars maps the line styles to the corresponding component characters. -// Source: http://en.wikipedia.org/wiki/Box-drawing_character. -var lineStyleChars = map[linestyle.LineStyle]map[linePart]rune{ - linestyle.Light: { - hLine: '─', - vLine: '│', - topLeftCorner: '┌', - topRightCorner: '┐', - bottomLeftCorner: '└', - bottomRightCorner: '┘', - hAndUp: '┴', - hAndDown: '┬', - vAndLeft: '┤', - vAndRight: '├', - vAndH: '┼', - }, - linestyle.Double: { - hLine: '═', - vLine: '║', - topLeftCorner: '╔', - topRightCorner: '╗', - bottomLeftCorner: '╚', - bottomRightCorner: '╝', - hAndUp: '╩', - hAndDown: '╦', - vAndLeft: '╣', - vAndRight: '╠', - vAndH: '╬', - }, - linestyle.Round: { - hLine: '─', - vLine: '│', - topLeftCorner: '╭', - topRightCorner: '╮', - bottomLeftCorner: '╰', - bottomRightCorner: '╯', - hAndUp: '┴', - hAndDown: '┬', - vAndLeft: '┤', - vAndRight: '├', - vAndH: '┼', - }, -} - -// init verifies that all line parts are half-width runes (occupy only one -// cell). -func init() { - for ls, parts := range lineStyleChars { - for part, r := range parts { - if got := runewidth.RuneWidth(r); got > 1 { - panic(fmt.Errorf("line style %v line part %v is a rune %c with width %v, all parts must be half-width runes (width of one)", ls, part, r, got)) - } - } - } -} - -// lineParts returns the line component characters for the provided line style. -func lineParts(ls linestyle.LineStyle) (map[linePart]rune, error) { - parts, ok := lineStyleChars[ls] - if !ok { - return nil, fmt.Errorf("unsupported line style %d", ls) - } - return parts, nil -} - -// linePart identifies individual line parts. -type linePart int - -// String implements fmt.Stringer() -func (lp linePart) String() string { - if n, ok := linePartNames[lp]; ok { - return n - } - return "linePartUnknown" -} - -// linePartNames maps linePart values to human readable names. -var linePartNames = map[linePart]string{ - vLine: "linePartVLine", - topLeftCorner: "linePartTopLeftCorner", - topRightCorner: "linePartTopRightCorner", - bottomLeftCorner: "linePartBottomLeftCorner", - bottomRightCorner: "linePartBottomRightCorner", - hAndUp: "linePartHAndUp", - hAndDown: "linePartHAndDown", - vAndLeft: "linePartVAndLeft", - vAndRight: "linePartVAndRight", - vAndH: "linePartVAndH", -} - -const ( - hLine linePart = iota - vLine - topLeftCorner - topRightCorner - bottomLeftCorner - bottomRightCorner - hAndUp - hAndDown - vAndLeft - vAndRight - vAndH -) diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/rectangle.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/rectangle.go deleted file mode 100644 index cd96ff715..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/rectangle.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -// rectangle.go draws a rectangle. - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/canvas" -) - -// RectangleOption is used to provide options to the Rectangle function. -type RectangleOption interface { - // set sets the provided option. - set(*rectOptions) -} - -// rectOptions stores the provided options. -type rectOptions struct { - cellOpts []cell.Option - char rune -} - -// rectOption implements RectangleOption. -type rectOption func(rOpts *rectOptions) - -// set implements RectangleOption.set. -func (ro rectOption) set(rOpts *rectOptions) { - ro(rOpts) -} - -// RectCellOpts sets options on the cells that create the rectangle. -func RectCellOpts(opts ...cell.Option) RectangleOption { - return rectOption(func(rOpts *rectOptions) { - rOpts.cellOpts = append(rOpts.cellOpts, opts...) - }) -} - -// DefaultRectChar is the default value for the RectChar option. -const DefaultRectChar = ' ' - -// RectChar sets the character used in each of the cells of the rectangle. -func RectChar(c rune) RectangleOption { - return rectOption(func(rOpts *rectOptions) { - rOpts.char = c - }) -} - -// Rectangle draws a filled rectangle on the canvas. -func Rectangle(c *canvas.Canvas, r image.Rectangle, opts ...RectangleOption) error { - opt := &rectOptions{ - char: DefaultRectChar, - } - for _, o := range opts { - o.set(opt) - } - - if ar := c.Area(); !r.In(ar) { - return fmt.Errorf("the requested rectangle %v doesn't fit the canvas area %v", r, ar) - } - - if r.Dx() < 1 || r.Dy() < 1 { - return fmt.Errorf("the rectangle must be at least 1x1 cell, got %v", r) - } - - for col := r.Min.X; col < r.Max.X; col++ { - for row := r.Min.Y; row < r.Max.Y; row++ { - cells, err := c.SetCell(image.Point{col, row}, opt.char, opt.cellOpts...) - if err != nil { - return err - } - if cells != 1 { - return fmt.Errorf("invalid rectangle character %q, this character occupies %d cells, the implementation only supports half-width runes that occupy exactly one cell", opt.char, cells) - } - } - } - return nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/text.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/text.go deleted file mode 100644 index 17c4954a0..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/text.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -// text.go contains code that prints UTF-8 encoded strings on the canvas. - -import ( - "fmt" - "image" - "strings" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/canvas" - "github.com/mum4k/termdash/private/runewidth" -) - -// OverrunMode represents -type OverrunMode int - -// String implements fmt.Stringer() -func (om OverrunMode) String() string { - if n, ok := overrunModeNames[om]; ok { - return n - } - return "OverrunModeUnknown" -} - -// overrunModeNames maps OverrunMode values to human readable names. -var overrunModeNames = map[OverrunMode]string{ - OverrunModeStrict: "OverrunModeStrict", - OverrunModeTrim: "OverrunModeTrim", - OverrunModeThreeDot: "OverrunModeThreeDot", -} - -const ( - // OverrunModeStrict verifies that the drawn value fits the canvas and - // returns an error if it doesn't. - OverrunModeStrict OverrunMode = iota - - // OverrunModeTrim trims the part of the text that doesn't fit. - OverrunModeTrim - - // OverrunModeThreeDot trims the text and places the horizontal ellipsis - // '…' character at the end. - OverrunModeThreeDot -) - -// TextOption is used to provide options to Text(). -type TextOption interface { - // set sets the provided option. - set(*textOptions) -} - -// textOptions stores the provided options. -type textOptions struct { - cellOpts []cell.Option - maxX int - overrunMode OverrunMode -} - -// textOption implements TextOption. -type textOption func(*textOptions) - -// set implements TextOption.set. -func (to textOption) set(tOpts *textOptions) { - to(tOpts) -} - -// TextCellOpts sets options on the cells that contain the text. -func TextCellOpts(opts ...cell.Option) TextOption { - return textOption(func(tOpts *textOptions) { - tOpts.cellOpts = opts - }) -} - -// TextMaxX sets a limit on the X coordinate (column) of the drawn text. -// The X coordinate of all cells used by the text must be within -// start.X <= X < TextMaxX. -// If not provided, the width of the canvas is used as TextMaxX. -func TextMaxX(x int) TextOption { - return textOption(func(tOpts *textOptions) { - tOpts.maxX = x - }) -} - -// TextOverrunMode indicates what to do with text that overruns the TextMaxX() -// or the width of the canvas if TextMaxX() isn't specified. -// Defaults to OverrunModeStrict. -func TextOverrunMode(om OverrunMode) TextOption { - return textOption(func(tOpts *textOptions) { - tOpts.overrunMode = om - }) -} - -// TrimText trims the provided text so that it fits the specified amount of cells. -func TrimText(text string, maxCells int, om OverrunMode) (string, error) { - if maxCells < 1 { - return "", fmt.Errorf("maxCells(%d) cannot be less than one", maxCells) - } - - textCells := runewidth.StringWidth(text) - if textCells <= maxCells { - // Nothing to do if the text fits. - return text, nil - } - - switch om { - case OverrunModeStrict: - return "", fmt.Errorf("the requested text %q takes %d cells to draw, space is available for only %d cells and overrun mode is %v", text, textCells, maxCells, om) - case OverrunModeTrim, OverrunModeThreeDot: - default: - return "", fmt.Errorf("unsupported overrun mode %d", om) - } - - var b strings.Builder - cur := 0 - for _, r := range text { - rw := runewidth.RuneWidth(r) - if cur+rw >= maxCells { - switch { - case om == OverrunModeTrim: - // Only write the rune if it still fits, i.e. don't cut - // full-width runes in half. - if cur+rw == maxCells { - b.WriteRune(r) - } - case om == OverrunModeThreeDot: - b.WriteRune('…') - } - break - } - - b.WriteRune(r) - cur += rw - } - return b.String(), nil -} - -// Text prints the provided text on the canvas starting at the provided point. -func Text(c *canvas.Canvas, text string, start image.Point, opts ...TextOption) error { - ar := c.Area() - if !start.In(ar) { - return fmt.Errorf("the requested start point %v falls outside of the provided canvas %v", start, ar) - } - - opt := &textOptions{} - for _, o := range opts { - o.set(opt) - } - - if opt.maxX < 0 || opt.maxX > ar.Max.X { - return fmt.Errorf("invalid TextMaxX(%v), must be a positive number that is <= canvas.width %v", opt.maxX, ar.Dx()) - } - - var wantMaxX int - if opt.maxX == 0 { - wantMaxX = ar.Max.X - } else { - wantMaxX = opt.maxX - } - - maxCells := wantMaxX - start.X - trimmed, err := TrimText(text, maxCells, opt.overrunMode) - if err != nil { - return err - } - - cur := start - for _, r := range trimmed { - cells, err := c.SetCell(cur, r, opt.cellOpts...) - if err != nil { - return err - } - cur = image.Point{cur.X + cells, cur.Y} - } - return nil -} - -// ResizeNeeded draws an unicode character indicating that the canvas size is -// too small to draw meaningful content. -func ResizeNeeded(cvs *canvas.Canvas) error { - return Text(cvs, "⇄", image.Point{0, 0}) -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/vertical_text.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/vertical_text.go deleted file mode 100644 index 44aadc9e5..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/vertical_text.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package draw - -// vertical_text.go contains code that prints UTF-8 encoded strings on the -// canvas in vertical columns instead of lines. - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/canvas" -) - -// VerticalTextOption is used to provide options to Text(). -type VerticalTextOption interface { - // set sets the provided option. - set(*verticalTextOptions) -} - -// verticalTextOptions stores the provided options. -type verticalTextOptions struct { - cellOpts []cell.Option - maxY int - overrunMode OverrunMode -} - -// verticalTextOption implements VerticalTextOption. -type verticalTextOption func(*verticalTextOptions) - -// set implements VerticalTextOption.set. -func (vto verticalTextOption) set(vtOpts *verticalTextOptions) { - vto(vtOpts) -} - -// VerticalTextCellOpts sets options on the cells that contain the text. -func VerticalTextCellOpts(opts ...cell.Option) VerticalTextOption { - return verticalTextOption(func(vtOpts *verticalTextOptions) { - vtOpts.cellOpts = opts - }) -} - -// VerticalTextMaxY sets a limit on the Y coordinate (row) of the drawn text. -// The Y coordinate of all cells used by the vertical text must be within -// start.Y <= Y < VerticalTextMaxY. -// If not provided, the height of the canvas is used as VerticalTextMaxY. -func VerticalTextMaxY(y int) VerticalTextOption { - return verticalTextOption(func(vtOpts *verticalTextOptions) { - vtOpts.maxY = y - }) -} - -// VerticalTextOverrunMode indicates what to do with text that overruns the -// VerticalTextMaxY() or the width of the canvas if VerticalTextMaxY() isn't -// specified. -// Defaults to OverrunModeStrict. -func VerticalTextOverrunMode(om OverrunMode) VerticalTextOption { - return verticalTextOption(func(vtOpts *verticalTextOptions) { - vtOpts.overrunMode = om - }) -} - -// VerticalText prints the provided text on the canvas starting at the provided point. -// The text is printed in a vertical orientation, i.e: -// H -// e -// l -// l -// o -func VerticalText(c *canvas.Canvas, text string, start image.Point, opts ...VerticalTextOption) error { - ar := c.Area() - if !start.In(ar) { - return fmt.Errorf("the requested start point %v falls outside of the provided canvas %v", start, ar) - } - - opt := &verticalTextOptions{} - for _, o := range opts { - o.set(opt) - } - - if opt.maxY < 0 || opt.maxY > ar.Max.Y { - return fmt.Errorf("invalid VerticalTextMaxY(%v), must be a positive number that is <= canvas.width %v", opt.maxY, ar.Dy()) - } - - var wantMaxY int - if opt.maxY == 0 { - wantMaxY = ar.Max.Y - } else { - wantMaxY = opt.maxY - } - - maxCells := wantMaxY - start.Y - trimmed, err := TrimText(text, maxCells, opt.overrunMode) - if err != nil { - return err - } - - cur := start - for _, r := range trimmed { - cells, err := c.SetCell(cur, r, opt.cellOpts...) - if err != nil { - return err - } - cur = image.Point{cur.X, cur.Y + cells} - } - return nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/event.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/event.go deleted file mode 100644 index e9ef18dbb..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/event.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package event provides a non-blocking event distribution and subscription -// system. -package event - -import ( - "context" - "reflect" - "sync" - - "github.com/mum4k/termdash/private/event/eventqueue" - "github.com/mum4k/termdash/terminal/terminalapi" -) - -// Callback is a function provided by an event subscriber. -// It gets called with each event that passed the subscription filter. -// Implementations must be thread-safe, events come from a separate goroutine. -// Implementation should be light-weight, otherwise a slow-processing -// subscriber can build a long tail of events. -type Callback func(terminalapi.Event) - -// queue is a queue of terminal events. -type queue interface { - Push(e terminalapi.Event) - Pull(ctx context.Context) terminalapi.Event - Close() -} - -// subscriber represents a single subscriber. -type subscriber struct { - // cb is the callback the subscriber receives events on. - cb Callback - - // filter filters events towards the subscriber. - // An empty filter receives all events. - filter map[reflect.Type]bool - - // queue is a queue of events towards the subscriber. - queue queue - - // cancel when called terminates the goroutine that forwards events towards - // this subscriber. - cancel context.CancelFunc - - // processes is the number of events that were fully processed, i.e. - // delivered to the callback. - processed int - - // mu protects busy. - mu sync.Mutex -} - -// newSubscriber creates a new event subscriber. -func newSubscriber(filter []terminalapi.Event, cb Callback, opts *subscribeOptions) *subscriber { - f := map[reflect.Type]bool{} - for _, ev := range filter { - f[reflect.TypeOf(ev)] = true - } - - ctx, cancel := context.WithCancel(context.Background()) - var q queue - if opts.throttle { - q = eventqueue.NewThrottled(opts.maxRep) - } else { - q = eventqueue.New() - } - - s := &subscriber{ - cb: cb, - filter: f, - queue: q, - cancel: cancel, - } - - // Terminates when stop() is called. - go s.run(ctx) - return s -} - -// callback sends the event to the callback. -func (s *subscriber) callback(ev terminalapi.Event) { - s.cb(ev) - - func() { - s.mu.Lock() - defer s.mu.Unlock() - s.processed++ - }() -} - -// run periodically forwards events towards the subscriber. -// Terminates when the context expires. -func (s *subscriber) run(ctx context.Context) { - for { - ev := s.queue.Pull(ctx) - if ev != nil { - s.callback(ev) - } - - select { - case <-ctx.Done(): - return - default: - } - } -} - -// event forwards an event to the subscriber. -func (s *subscriber) event(ev terminalapi.Event) { - if len(s.filter) == 0 { - s.queue.Push(ev) - } - - t := reflect.TypeOf(ev) - if s.filter[t] { - s.queue.Push(ev) - } -} - -// processedEvents returns the number of events processed by this subscriber. -func (s *subscriber) processedEvents() int { - s.mu.Lock() - defer s.mu.Unlock() - return s.processed -} - -// stop stops the event subscriber. -func (s *subscriber) stop() { - s.cancel() - s.queue.Close() -} - -// DistributionSystem distributes events to subscribers. -// -// Subscribers can request filtering of events they get based on event type or -// subscribe to all events. -// -// The distribution system maintains a queue towards each subscriber, making -// sure that a single slow subscriber only slows itself down, rather than the -// entire application. -// -// This object is thread-safe. -type DistributionSystem struct { - // subscribers subscribe to events. - // maps subscriber id to subscriber. - subscribers map[int]*subscriber - - // nextID is id for the next subscriber. - nextID int - - // mu protects the distribution system. - mu sync.Mutex -} - -// NewDistributionSystem creates a new event distribution system. -func NewDistributionSystem() *DistributionSystem { - return &DistributionSystem{ - subscribers: map[int]*subscriber{}, - } -} - -// Event should be called with events coming from the terminal. -// The distribution system will distribute these to all the subscribers. -func (eds *DistributionSystem) Event(ev terminalapi.Event) { - eds.mu.Lock() - defer eds.mu.Unlock() - - for _, sub := range eds.subscribers { - sub.event(ev) - } -} - -// StopFunc when called unsubscribes the subscriber from all events and -// releases resources tied to the subscriber. -type StopFunc func() - -// SubscribeOption is used to provide options to Subscribe. -type SubscribeOption interface { - // set sets the provided option. - set(*subscribeOptions) -} - -// subscribeOptions stores the provided options. -type subscribeOptions struct { - throttle bool - maxRep int -} - -// subscribeOption implements Option. -type subscribeOption func(*subscribeOptions) - -// set implements SubscribeOption.set. -func (o subscribeOption) set(sOpts *subscribeOptions) { - o(sOpts) -} - -// MaxRepetitive when provided, instructs the system to drop repetitive -// events instead of delivering them. -// The argument maxRep indicates the maximum number of repetitive events to -// enqueue towards the subscriber. -func MaxRepetitive(maxRep int) SubscribeOption { - return subscribeOption(func(sOpts *subscribeOptions) { - sOpts.throttle = true - sOpts.maxRep = maxRep - }) -} - -// Subscribe subscribes to events according to the filter. -// An empty filter indicates that the subscriber wishes to receive events of -// all kinds. If the filter is non-empty, only events of the provided type will -// be sent to the subscriber. -// Returns a function that allows the subscriber to unsubscribe. -func (eds *DistributionSystem) Subscribe(filter []terminalapi.Event, cb Callback, opts ...SubscribeOption) StopFunc { - eds.mu.Lock() - defer eds.mu.Unlock() - - opt := &subscribeOptions{} - for _, o := range opts { - o.set(opt) - } - - id := eds.nextID - eds.nextID++ - sub := newSubscriber(filter, cb, opt) - eds.subscribers[id] = sub - - return func() { - eds.mu.Lock() - defer eds.mu.Unlock() - - sub.stop() - delete(eds.subscribers, id) - } -} - -// Processed returns the number of events that were fully processed, i.e. -// delivered to all the subscribers and their callbacks returned. -func (eds *DistributionSystem) Processed() int { - eds.mu.Lock() - defer eds.mu.Unlock() - - var res int - for _, sub := range eds.subscribers { - res += sub.processedEvents() - } - return res -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/eventqueue/eventqueue.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/eventqueue/eventqueue.go deleted file mode 100644 index eb22d4f2c..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/eventqueue/eventqueue.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package eventqueue provides an unboud FIFO queue of events. -package eventqueue - -import ( - "context" - "reflect" - "sync" - "time" - - "github.com/mum4k/termdash/terminal/terminalapi" -) - -// node is a single data item on the queue. -type node struct { - prev *node - next *node - event terminalapi.Event -} - -// Unbound is an unbound FIFO queue of terminal events. -// Unbound must not be copied, pass it by reference only. -// This implementation is thread-safe. -type Unbound struct { - first *node - last *node - // mu protects first and last. - mu sync.Mutex - - // cond is used to notify any callers waiting on a call to Pull(). - cond *sync.Cond - - // condMU protects cond. - condMU sync.RWMutex - - // done is closed when the queue isn't needed anymore. - done chan struct{} -} - -// New returns a new Unbound queue of terminal events. -// Call Close() when done with the queue. -func New() *Unbound { - u := &Unbound{ - done: make(chan (struct{})), - } - u.cond = sync.NewCond(&u.condMU) - go u.wake() // Stops when Close() is called. - return u -} - -// wake periodically wakes up all goroutines waiting at Pull() so they can -// check if the context expired. -func (u *Unbound) wake() { - const spinTime = 250 * time.Millisecond - t := time.NewTicker(spinTime) - defer t.Stop() - for { - select { - case <-t.C: - u.cond.Broadcast() - case <-u.done: - return - } - } -} - -// Empty determines if the queue is empty. -func (u *Unbound) Empty() bool { - u.mu.Lock() - defer u.mu.Unlock() - return u.empty() -} - -// empty determines if the queue is empty. -func (u *Unbound) empty() bool { - return u.first == nil -} - -// Push pushes an event onto the queue. -func (u *Unbound) Push(e terminalapi.Event) { - u.mu.Lock() - defer u.mu.Unlock() - u.push(e) -} - -// push is the implementation of Push. -// Caller must hold u.mu. -func (u *Unbound) push(e terminalapi.Event) { - n := &node{ - event: e, - } - if u.empty() { - u.first = n - u.last = n - } else { - prev := u.last - u.last.next = n - u.last = n - u.last.prev = prev - } - u.cond.Signal() -} - -// Pop pops an event from the queue. Returns nil if the queue is empty. -func (u *Unbound) Pop() terminalapi.Event { - u.mu.Lock() - defer u.mu.Unlock() - - if u.empty() { - return nil - } - - n := u.first - u.first = u.first.next - - if u.empty() { - u.last = nil - } - return n.event -} - -// Pull is like Pop(), but blocks until an item is available or the context -// expires. Returns a nil event if the context expired. -func (u *Unbound) Pull(ctx context.Context) terminalapi.Event { - if e := u.Pop(); e != nil { - return e - } - - u.cond.L.Lock() - defer u.cond.L.Unlock() - for { - select { - case <-ctx.Done(): - return nil - default: - } - - if e := u.Pop(); e != nil { - return e - } - u.cond.Wait() - } -} - -// Close should be called when the queue isn't needed anymore. -func (u *Unbound) Close() { - close(u.done) -} - -// Throttled is an unbound and throttled FIFO queue of terminal events. -// Throttled must not be copied, pass it by reference only. -// This implementation is thread-safe. -type Throttled struct { - queue *Unbound - max int -} - -// NewThrottled returns a new Throttled queue of terminal events. -// -// This queue scans the queue content on each Push call and won't Push the -// event if there already is a continuous chain of exactly the same events -// en queued. The argument maxRep specifies the maximum number of repetitive -// events. -// -// Call Close() when done with the queue. -func NewThrottled(maxRep int) *Throttled { - t := &Throttled{ - queue: New(), - max: maxRep, - } - return t -} - -// Empty determines if the queue is empty. -func (t *Throttled) Empty() bool { - return t.queue.empty() -} - -// Push pushes an event onto the queue. -func (t *Throttled) Push(e terminalapi.Event) { - t.queue.mu.Lock() - defer t.queue.mu.Unlock() - - if t.queue.empty() { - t.queue.push(e) - return - } - - var same int - for n := t.queue.last; n != nil; n = n.prev { - if reflect.DeepEqual(e, n.event) { - same++ - } else { - break - } - - if same > t.max { - return // Drop the repetitive event. - } - } - t.queue.push(e) -} - -// Pop pops an event from the queue. Returns nil if the queue is empty. -func (t *Throttled) Pop() terminalapi.Event { - return t.queue.Pop() -} - -// Pull is like Pop(), but blocks until an item is available or the context -// expires. Returns a nil event if the context expired. -func (t *Throttled) Pull(ctx context.Context) terminalapi.Event { - return t.queue.Pull(ctx) -} - -// Close should be called when the queue isn't needed anymore. -func (t *Throttled) Close() { - close(t.queue.done) -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/numbers.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/numbers.go deleted file mode 100644 index e91620f77..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/numbers.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package numbers implements various numerical functions. -package numbers - -import ( - "image" - "math" -) - -// RoundToNonZeroPlaces rounds the float up, so that it has at least the provided -// number of non-zero decimal places. -// Returns the rounded float and the number of leading decimal places that -// are zero. Returns the original float when places is zero. Negative places -// are treated as positive, so that -2 == 2. -func RoundToNonZeroPlaces(f float64, places int) (float64, int) { - if f == 0 { - return 0, 0 - } - - decOnly := zeroBeforeDecimal(f) - if decOnly == 0 { - return f, 0 - } - nzMult := multToNonZero(decOnly) - if places == 0 { - return f, multToPlaces(nzMult) - } - plMult := placesToMult(places) - - m := float64(nzMult * plMult) - return math.Ceil(f*m) / m, multToPlaces(nzMult) -} - -// multToNonZero returns multiplier for the float, so that the first decimal -// place is non-zero. The float must not be zero. -func multToNonZero(f float64) int { - v := f - if v < 0 { - v *= -1 - } - - mult := 1 - for v < 0.1 { - v *= 10 - mult *= 10 - } - return mult -} - -// placesToMult translates the number of decimal places to a multiple of 10. -func placesToMult(places int) int { - if places < 0 { - places *= -1 - } - - mult := 1 - for i := 0; i < places; i++ { - mult *= 10 - } - return mult -} - -// multToPlaces translates the multiple of 10 to a number of decimal places. -func multToPlaces(mult int) int { - places := 0 - for mult > 1 { - mult /= 10 - places++ - } - return places -} - -// zeroBeforeDecimal modifies the float so that it only has zero value before -// the decimal point. -func zeroBeforeDecimal(f float64) float64 { - var sign float64 = 1 - if f < 0 { - f *= -1 - sign = -1 - } - - floor := math.Floor(f) - return (f - floor) * sign -} - -// MinMax returns the smallest and the largest value among the provided values. -// Returns (0, 0) if there are no values. -// Ignores NaN values. Allowing NaN values could lead to a corner case where all -// values can be NaN, in this case the function will return NaN as min and max. -func MinMax(values []float64) (min, max float64) { - if len(values) == 0 { - return 0, 0 - } - min = math.MaxFloat64 - max = -1 * math.MaxFloat64 - allNaN := true - for _, v := range values { - if math.IsNaN(v) { - continue - } - allNaN = false - - if v < min { - min = v - } - if v > max { - max = v - } - } - - if allNaN { - return math.NaN(), math.NaN() - } - - return min, max -} - -// MinMaxInts returns the smallest and the largest int value among the provided -// values. Returns (0, 0) if there are no values. -func MinMaxInts(values []int) (min, max int) { - if len(values) == 0 { - return 0, 0 - } - min = math.MaxInt32 - max = -1 * math.MaxInt32 - - for _, v := range values { - if v < min { - min = v - } - if v > max { - max = v - } - } - return min, max -} - -// DegreesToRadians converts degrees to the equivalent in radians. -func DegreesToRadians(degrees int) float64 { - if degrees > 360 { - degrees %= 360 - } - return (float64(degrees) / 180) * math.Pi -} - -// RadiansToDegrees converts radians to the equivalent in degrees. -func RadiansToDegrees(radians float64) int { - d := int(math.Round(radians * 180 / math.Pi)) - if d < 0 { - d += 360 - } - return d -} - -// Abs returns the absolute value of x. -func Abs(x int) int { - if x < 0 { - return -x - } - return x -} - -// findGCF finds the greatest common factor of two integers. -func findGCF(a, b int) int { - if a == 0 || b == 0 { - return 0 - } - a = Abs(a) - b = Abs(b) - - // https://en.wikipedia.org/wiki/Euclidean_algorithm - for { - rem := a % b - a = b - b = rem - - if b == 0 { - break - } - } - return a -} - -// SimplifyRatio simplifies the given ratio. -func SimplifyRatio(ratio image.Point) image.Point { - gcf := findGCF(ratio.X, ratio.Y) - if gcf == 0 { - return image.ZP - } - return image.Point{ - X: ratio.X / gcf, - Y: ratio.Y / gcf, - } -} - -// SplitByRatio splits the provided number by the specified ratio. -func SplitByRatio(n int, ratio image.Point) image.Point { - sr := SimplifyRatio(ratio) - if sr.Eq(image.ZP) { - return image.ZP - } - fn := float64(n) - sum := float64(sr.X + sr.Y) - fact := fn / sum - return image.Point{ - int(math.Round(fact * float64(sr.X))), - int(math.Round(fact * float64(sr.Y))), - } -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/trig/trig.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/trig/trig.go deleted file mode 100644 index 16d179d02..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/trig/trig.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package trig implements various trigonometrical calculations. -package trig - -import ( - "fmt" - "image" - "math" - "sort" - - "github.com/mum4k/termdash/private/numbers" -) - -// CirclePointAtAngle given an angle in degrees and a circle midpoint and -// radius, calculates coordinates of a point on the circle at that angle. -// Angles are zero at the X axis and grow counter-clockwise. -func CirclePointAtAngle(degrees int, mid image.Point, radius int) image.Point { - angle := numbers.DegreesToRadians(degrees) - r := float64(radius) - x := mid.X + int(math.Round(r*math.Cos(angle))) - // Y coordinates grow down on the canvas. - y := mid.Y - int(math.Round(r*math.Sin(angle))) - return image.Point{x, y} -} - -// CircleAngleAtPoint given a point on a circle and its midpoint, -// calculates the angle in degrees. -// Angles are zero at the X axis and grow counter-clockwise. -func CircleAngleAtPoint(point, mid image.Point) int { - adj := float64(point.X - mid.X) - opp := float64(mid.Y - point.Y) - if opp != 0 { - angle := math.Atan2(opp, adj) - return numbers.RadiansToDegrees(angle) - } else if adj >= 0 { - return 0 - } else { - return 180 - } -} - -// PointIsIn asserts whether the provided point is inside of a shape outlined -// with the provided points. -// Does not verify that the shape is closed or complete, it merely counts the -// number of intersections with the shape on one row. -func PointIsIn(p image.Point, points []image.Point) bool { - maxX := p.X - set := map[image.Point]struct{}{} - for _, sp := range points { - set[sp] = struct{}{} - if sp.X > maxX { - maxX = sp.X - } - } - - if _, ok := set[p]; ok { - // Not inside if it is on the shape. - return false - } - - byY := map[int][]int{} // maps y->x - for p := range set { - byY[p.Y] = append(byY[p.Y], p.X) - } - for y := range byY { - sort.Ints(byY[y]) - } - - set = map[image.Point]struct{}{} - for y, xses := range byY { - set[image.Point{xses[0], y}] = struct{}{} - if len(xses) == 1 { - continue - } - - for i := 1; i < len(xses); i++ { - if xses[i] != xses[i-1]+1 { - set[image.Point{xses[i], y}] = struct{}{} - } - } - } - - crosses := 0 - for x := p.X; x <= maxX; x++ { - if _, ok := set[image.Point{x, p.Y}]; ok { - crosses++ - } - } - return crosses%2 != 0 -} - -const ( - // MinAngle is the smallest valid angle in degrees. - MinAngle = 0 - // MaxAngle is the largest valid angle in degrees. - MaxAngle = 360 -) - -// angleRange represents a range of angles in degrees. -// The range includes all angles such that start <= angle <= end. -type angleRange struct { - // start is the start if the range. - // This is always less or equal to the end. - start int - - // end is the end of the range. - end int -} - -// contains asserts whether the specified angle is in the range. -func (ar *angleRange) contains(angle int) bool { - return angle >= ar.start && angle <= ar.end -} - -// normalizeRange normalizes the start and end angles in degrees into ranges of -// angles. Useful for cases where the 0/360 point falls within the range. -// E.g: -// 0,25 => angleRange{0, 26} -// 0,360 => angleRange{0, 361} -// 359,20 => angleRange{359, 361}, angleRange{0, 21} -func normalizeRange(start, end int) ([]*angleRange, error) { - if start < MinAngle || start > MaxAngle { - return nil, fmt.Errorf("invalid start angle:%d, must be in range %d <= start <= %d", start, MinAngle, MaxAngle) - } - if end < MinAngle || end > MaxAngle { - return nil, fmt.Errorf("invalid end angle:%d, must be in range %d <= end <= %d", end, MinAngle, MaxAngle) - } - - if start == MaxAngle && end == 0 { - start, end = end, start - } - - if start <= end { - return []*angleRange{ - {start, end}, - }, nil - } - - // The range is crossing the 0/360 degree point. - // Break it into multiple ranges. - return []*angleRange{ - {start, MaxAngle}, - {0, end}, - }, nil -} - -// RangeSize returns the size of the degree range. -// E.g: -// 0,25 => 25 -// 359,1 => 2 -func RangeSize(start, end int) (int, error) { - ranges, err := normalizeRange(start, end) - if err != nil { - return 0, err - } - if len(ranges) == 1 { - return end - start, nil - } - return MaxAngle - start + end, nil -} - -// RangeMid returns an angle that lies in the middle between start and end. -// E.g: -// 0,10 => 5 -// 350,10 => 0 -func RangeMid(start, end int) (int, error) { - ranges, err := normalizeRange(start, end) - if err != nil { - return 0, err - } - if len(ranges) == 1 { - return start + ((end - start) / 2), nil - } - - length := MaxAngle - start + end - want := length / 2 - res := start + want - return res % MaxAngle, nil -} - -// FilterByAngle filters the provided points, returning only those that fall -// within the starting and the ending angle on a circle with the provided mid -// point. -func FilterByAngle(points []image.Point, mid image.Point, start, end int) ([]image.Point, error) { - var res []image.Point - ranges, err := normalizeRange(start, end) - if err != nil { - return nil, err - } - if mid.X < 0 || mid.Y < 0 { - return nil, fmt.Errorf("the mid point %v cannot have negative coordinates", mid) - } - - for _, p := range points { - angle := CircleAngleAtPoint(p, mid) - - // Edge case, this might mean 0 or 360. - // Decide based on where we are starting. - if angle == 0 && start > 0 { - angle = MaxAngle - } - - for _, r := range ranges { - if r.contains(angle) { - res = append(res, p) - break - } - } - } - return res, nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/runewidth/runewidth.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/runewidth/runewidth.go deleted file mode 100644 index 4f2f63a8f..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/runewidth/runewidth.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package runewidth is a wrapper over github.com/mattn/go-runewidth which -// gives different treatment to certain runes with ambiguous width. -package runewidth - -import runewidth "github.com/mattn/go-runewidth" - -// RuneWidth returns the number of cells needed to draw r. -// Background in http://www.unicode.org/reports/tr11/. -// -// Treats runes used internally by termdash as single-cell (half-width) runes -// regardless of the locale. I.e. runes that are used to draw lines, boxes, -// indicate resize or text trimming was needed and runes used by the braille -// canvas. -// -// This should be safe, since even in locales where these runes have ambiguous -// width, we still place all the character content around them so they should -// have be half-width. -func RuneWidth(r rune) int { - if inTable(r, exceptions) { - return 1 - } - return runewidth.RuneWidth(r) -} - -// StringWidth is like RuneWidth, but returns the number of cells occupied by -// all the runes in the string. -func StringWidth(s string) int { - var width int - for _, r := range []rune(s) { - width += RuneWidth(r) - } - return width -} - -// inTable determines if the rune falls within the table. -// Copied from github.com/mattn/go-runewidth/blob/master/runewidth.go. -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 -} - -type interval struct { - first rune - last rune -} - -type table []interval - -// exceptions runes defined here are always considered to be half-width even if -// they might be ambiguous in some contexts. -var exceptions = table{ - // Characters used by termdash to indicate text trim or scroll. - {0x2026, 0x2026}, - {0x21c4, 0x21c4}, - {0x21e7, 0x21e7}, - {0x21e9, 0x21e9}, - - // Box drawing, used as line-styles. - // https://en.wikipedia.org/wiki/Box-drawing_character - {0x2500, 0x257F}, - - // Block elements used as sparks. - // https://en.wikipedia.org/wiki/Box-drawing_character - {0x2580, 0x258F}, -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/wrap/wrap.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/wrap/wrap.go deleted file mode 100644 index 5ee78a7a5..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/wrap/wrap.go +++ /dev/null @@ -1,409 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package wrap implements line wrapping at character or word boundaries. -package wrap - -import ( - "errors" - "fmt" - "strings" - "unicode" - - "github.com/mum4k/termdash/private/canvas/buffer" - "github.com/mum4k/termdash/private/runewidth" -) - -// Mode sets the wrapping mode. -type Mode int - -// String implements fmt.Stringer() -func (m Mode) String() string { - if n, ok := modeNames[m]; ok { - return n - } - return "ModeUnknown" -} - -// modeNames maps Mode values to human readable names. -var modeNames = map[Mode]string{ - Never: "WrapModeNever", - AtRunes: "WrapModeAtRunes", - AtWords: "WrapModeAtWords", -} - -const ( - // Never is the default wrapping mode, which disables line wrapping. - Never Mode = iota - - // AtRunes is a wrapping mode where if the width of the text crosses the - // width of the canvas, wrapping is performed at rune boundaries. - AtRunes - - // AtWords is a wrapping mode where if the width of the text crosses the - // width of the canvas, wrapping is performed at word boundaries. The - // wrapping still switches back to the AtRunes mode for any words that are - // longer than the width. - AtWords -) - -// ValidText validates the provided text for wrapping. -// The text must not be empty, contain any control or -// space characters other than '\n' and ' '. -func ValidText(text string) error { - if text == "" { - return errors.New("the text cannot be empty") - } - - for _, c := range text { - if c == ' ' || c == '\n' { // Allowed space and control runes. - continue - } - if unicode.IsControl(c) { - return fmt.Errorf("the provided text %q cannot contain control characters, found: %q", text, c) - } - if unicode.IsSpace(c) { - return fmt.Errorf("the provided text %q cannot contain space character %q", text, c) - } - } - return nil -} - -// ValidCells validates the provided cells for wrapping. -// The text in the cells must follow the same rules as described for ValidText. -func ValidCells(cells []*buffer.Cell) error { - var b strings.Builder - for _, c := range cells { - b.WriteRune(c.Rune) - } - return ValidText(b.String()) -} - -// Cells returns the cells wrapped into individual lines according to the -// specified width and wrapping mode. -// -// This function consumes any cells that contain newline characters and uses -// them to start new lines. -// -// If the mode is AtWords, this function also drops cells with leading space -// character before a word at which the wrap occurs. -func Cells(cells []*buffer.Cell, width int, m Mode) ([][]*buffer.Cell, error) { - if err := ValidCells(cells); err != nil { - return nil, err - } - switch m { - case Never: - case AtRunes: - case AtWords: - default: - return nil, fmt.Errorf("unsupported wrapping mode %v(%d)", m, m) - } - if width <= 0 { - return nil, nil - } - - cs := newCellScanner(cells, width, m) - for state := scanCellRunes; state != nil; state = state(cs) { - } - return cs.lines, nil -} - -// cellScannerState is a state in the FSM that scans the input text and identifies -// newlines. -type cellScannerState func(*cellScanner) cellScannerState - -// cellScanner tracks the progress of scanning the input cells when finding -// lines. -type cellScanner struct { - // cells are the cells being scanned. - cells []*buffer.Cell - - // nextIdx is the index of the cell that will be returned by next. - nextIdx int - - // wordStartIdx stores the starting index of the current word. - // A starting position of a word includes any leading space characters. - // E.g.: hello world - // ^ - // lastWordIdx - wordStartIdx int - // wordEndIdx stores the ending index of the current word. - // The word consists of all indexes that are - // wordStartIdx <= idx < wordEndIdx. - // A word also includes any punctuation after it. - wordEndIdx int - - // width is the width of the canvas the text will be drawn on. - width int - - // posX tracks the horizontal position of the current cell on the canvas. - posX int - - // mode is the wrapping mode. - mode Mode - - // atRunesInWord overrides the mode back to AtRunes. - atRunesInWord bool - - // lines are the identified lines. - lines [][]*buffer.Cell - - // line is the current line. - line []*buffer.Cell -} - -// newCellScanner returns a scanner of the provided cells. -func newCellScanner(cells []*buffer.Cell, width int, m Mode) *cellScanner { - return &cellScanner{ - cells: cells, - width: width, - mode: m, - } -} - -// next returns the next cell and advances the scanner. -// Returns nil when there are no more cells to scan. -func (cs *cellScanner) next() *buffer.Cell { - c := cs.peek() - if c != nil { - cs.nextIdx++ - } - return c -} - -// peek returns the next cell without advancing the scanner's position. -// Returns nil when there are no more cells to peek at. -func (cs *cellScanner) peek() *buffer.Cell { - if cs.nextIdx >= len(cs.cells) { - return nil - } - return cs.cells[cs.nextIdx] -} - -// peekPrev returns the previous cell without changing the scanner's position. -// Returns nil if the scanner is at the first cell. -func (cs *cellScanner) peekPrev() *buffer.Cell { - if cs.nextIdx == 0 { - return nil - } - return cs.cells[cs.nextIdx-1] -} - -// wordCells returns all the cells that belong to the current word. -func (cs *cellScanner) wordCells() []*buffer.Cell { - return cs.cells[cs.wordStartIdx:cs.wordEndIdx] -} - -// wordWidth returns the width of the current word in cells when printed on the -// terminal. -func (cs *cellScanner) wordWidth() int { - var b strings.Builder - for _, wc := range cs.wordCells() { - b.WriteRune(wc.Rune) - } - return runewidth.StringWidth(b.String()) -} - -// isWordStart determines if the scanner is at the beginning of a word. -func (cs *cellScanner) isWordStart() bool { - if cs.mode != AtWords { - return false - } - - current := cs.peekPrev() - next := cs.peek() - if current == nil || next == nil { - return false - } - - switch nr := next.Rune; { - case nr == '\n': - case nr == ' ': - default: - return true - } - return false -} - -// scanCellRunes scans the cells a rune at a time. -func scanCellRunes(cs *cellScanner) cellScannerState { - for { - cell := cs.next() - if cell == nil { - return scanEOF - } - - r := cell.Rune - if r == '\n' { - return newLineForLineBreak - } - - if cs.mode == Never { - return runeToCurrentLine - } - - if cs.atRunesInWord && !isWordCell(cell) { - cs.atRunesInWord = false - } - - if !cs.atRunesInWord && cs.isWordStart() { - return markWordStart - } - - if runeWrapNeeded(r, cs.posX, cs.width) { - return newLineForAtRunes - } - - return runeToCurrentLine - } -} - -// runeToCurrentLine scans a single cell rune onto the current line. -func runeToCurrentLine(cs *cellScanner) cellScannerState { - cell := cs.peekPrev() - // Move horizontally within the line for each scanned cell. - cs.posX += runewidth.RuneWidth(cell.Rune) - - // Copy the cell into the current line. - cs.line = append(cs.line, cell) - return scanCellRunes -} - -// newLineForLineBreak processes a newline character cell. -func newLineForLineBreak(cs *cellScanner) cellScannerState { - cs.lines = append(cs.lines, cs.line) - cs.posX = 0 - cs.line = nil - return scanCellRunes -} - -// newLineForAtRunes processes a line wrap at rune boundaries due to canvas width. -func newLineForAtRunes(cs *cellScanner) cellScannerState { - // The character on which we wrapped will be printed and is the start of - // new line. - cs.lines = append(cs.lines, cs.line) - cs.posX = runewidth.RuneWidth(cs.peekPrev().Rune) - cs.line = []*buffer.Cell{cs.peekPrev()} - return scanCellRunes -} - -// scanEOF terminates the scanning. -func scanEOF(cs *cellScanner) cellScannerState { - // Need to add the current line if it isn't empty, or if the previous rune - // was a newline. - // Newlines aren't copied onto the lines so just checking for emptiness - // isn't enough. We still want to include trailing empty newlines if - // they are in the input text. - if len(cs.line) > 0 || cs.peekPrev().Rune == '\n' { - cs.lines = append(cs.lines, cs.line) - } - return nil -} - -// markWordStart stores the starting position of the current word. -func markWordStart(cs *cellScanner) cellScannerState { - cs.wordStartIdx = cs.nextIdx - 1 - cs.wordEndIdx = cs.nextIdx - return scanWord -} - -// scanWord scans the entire word until it finds its end. -func scanWord(cs *cellScanner) cellScannerState { - for { - if isWordCell(cs.peek()) { - cs.next() - cs.wordEndIdx++ - continue - } - return wordToCurrentLine - } -} - -// wordToCurrentLine decides how to place the word into the output. -func wordToCurrentLine(cs *cellScanner) cellScannerState { - wordCells := cs.wordCells() - wordWidth := cs.wordWidth() - - if cs.posX+wordWidth <= cs.width { - // Place the word onto the current line. - cs.posX += wordWidth - cs.line = append(cs.line, wordCells...) - return scanCellRunes - } - return wrapWord -} - -// wrapWord wraps the word onto the next line or lines. -func wrapWord(cs *cellScanner) cellScannerState { - // Edge-case - the word starts the line and immediately doesn't fit. - if cs.posX > 0 { - cs.lines = append(cs.lines, cs.line) - cs.posX = 0 - cs.line = nil - } - - for i, wc := range cs.wordCells() { - if i == 0 && wc.Rune == ' ' { - // Skip the leading space when word wrapping. - continue - } - - if !runeWrapNeeded(wc.Rune, cs.posX, cs.width) { - cs.posX += runewidth.RuneWidth(wc.Rune) - cs.line = append(cs.line, wc) - continue - } - - // Replace the last placed rune with a dash indicating we wrapped the - // word. Only do this for half-width runes. - lastIdx := len(cs.line) - 1 - last := cs.line[lastIdx] - lastRW := runewidth.RuneWidth(last.Rune) - if cs.width > 1 && lastRW == 1 { - cs.line[lastIdx] = buffer.NewCell('-', last.Opts) - // Reset the scanner's position back to start scanning at the first - // rune of this word that wasn't placed. - cs.nextIdx = cs.wordStartIdx + i - 1 - } else { - // Edge-case width is one, no space to put the dash rune. - cs.nextIdx = cs.wordStartIdx + i - } - cs.atRunesInWord = true - return scanCellRunes - } - - cs.nextIdx = cs.wordEndIdx - return scanCellRunes -} - -// isWordCell determines if the cell contains a rune that belongs to a word. -func isWordCell(c *buffer.Cell) bool { - if c == nil { - return false - } - switch r := c.Rune; { - case r == '\n': - case r == ' ': - default: - return true - } - return false -} - -// runeWrapNeeded returns true if wrapping is needed for the rune at the horizontal -// position on the canvas that has the specified width. -func runeWrapNeeded(r rune, posX, width int) bool { - rw := runewidth.RuneWidth(r) - return posX > width-rw -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/termdash.go b/examples/go-dashboard/src/github.com/mum4k/termdash/termdash.go deleted file mode 100644 index 8103ee717..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/termdash.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package termdash implements a terminal based dashboard. - -While running, the terminal dashboard performs the following: - - Periodic redrawing of the canvas and all the widgets. - - Event based redrawing of the widgets (i.e. on Keyboard or Mouse events). - - Forwards input events to widgets and optional subscribers. - - Handles terminal resize events. -*/ -package termdash - -import ( - "context" - "errors" - "fmt" - "sync" - "time" - - "github.com/mum4k/termdash/container" - "github.com/mum4k/termdash/private/event" - "github.com/mum4k/termdash/terminal/terminalapi" -) - -// DefaultRedrawInterval is the default for the RedrawInterval option. -const DefaultRedrawInterval = 250 * time.Millisecond - -// Option is used to provide options. -type Option interface { - // set sets the provided option. - set(td *termdash) -} - -// option implements Option. -type option func(td *termdash) - -// set implements Option.set. -func (o option) set(td *termdash) { - o(td) -} - -// RedrawInterval sets how often termdash redraws the container and all the widgets. -// Defaults to DefaultRedrawInterval. Use the controller to disable the -// periodic redraw. -func RedrawInterval(t time.Duration) Option { - return option(func(td *termdash) { - td.redrawInterval = t - }) -} - -// ErrorHandler is used to provide a function that will be called with all -// errors that occur while the dashboard is running. If not provided, any -// errors panic the application. -// The provided function must be thread-safe. -func ErrorHandler(f func(error)) Option { - return option(func(td *termdash) { - td.errorHandler = f - }) -} - -// KeyboardSubscriber registers a subscriber for Keyboard events. Each -// keyboard event is forwarded to the container and the registered subscriber. -// The provided function must be thread-safe. -func KeyboardSubscriber(f func(*terminalapi.Keyboard)) Option { - return option(func(td *termdash) { - td.keyboardSubscriber = f - }) -} - -// MouseSubscriber registers a subscriber for Mouse events. Each mouse event -// is forwarded to the container and the registered subscriber. -// The provided function must be thread-safe. -func MouseSubscriber(f func(*terminalapi.Mouse)) Option { - return option(func(td *termdash) { - td.mouseSubscriber = f - }) -} - -// withEDS indicates that termdash should run with the provided event -// distribution system instead of creating one. -// Useful for tests. -func withEDS(eds *event.DistributionSystem) Option { - return option(func(td *termdash) { - td.eds = eds - }) -} - -// Run runs the terminal dashboard with the provided container on the terminal. -// Redraws the terminal periodically. If you prefer a manual redraw, use the -// Controller instead. -// Blocks until the context expires. -func Run(ctx context.Context, t terminalapi.Terminal, c *container.Container, opts ...Option) error { - td := newTermdash(t, c, opts...) - - err := td.start(ctx) - // Only return the status (error or nil) after the termdash event - // processing goroutine actually exits. - td.stop() - return err -} - -// Controller controls a termdash instance. -// The controller instance is only valid until Close() is called. -// The controller is not thread-safe. -type Controller struct { - td *termdash - cancel context.CancelFunc -} - -// NewController initializes termdash and returns an instance of the controller. -// Periodic redrawing is disabled when using the controller, the RedrawInterval -// option is ignored. -// Close the controller when it isn't needed anymore. -func NewController(t terminalapi.Terminal, c *container.Container, opts ...Option) (*Controller, error) { - ctx, cancel := context.WithCancel(context.Background()) - ctrl := &Controller{ - td: newTermdash(t, c, opts...), - cancel: cancel, - } - - // stops when Close() is called. - go ctrl.td.processEvents(ctx) - if err := ctrl.td.periodicRedraw(); err != nil { - return nil, err - } - return ctrl, nil -} - -// Redraw triggers redraw of the terminal. -func (c *Controller) Redraw() error { - if c.td == nil { - return errors.New("the termdash instance is no longer running, this controller is now invalid") - } - - c.td.mu.Lock() - defer c.td.mu.Unlock() - return c.td.redraw() -} - -// Close closes the Controller and its termdash instance. -func (c *Controller) Close() { - c.cancel() - c.td.stop() - c.td = nil -} - -// termdash is a terminal based dashboard. -// This object is thread-safe. -type termdash struct { - // term is the terminal the dashboard runs on. - term terminalapi.Terminal - - // container maintains terminal splits and places widgets. - container *container.Container - - // eds distributes input events to subscribers. - eds *event.DistributionSystem - - // closeCh gets closed when Stop() is called, which tells the event - // collecting goroutine to exit. - closeCh chan struct{} - // exitCh gets closed when the event collecting goroutine actually exits. - exitCh chan struct{} - - // clearNeeded indicates if the terminal needs to be cleared next time - // we're drawing it. Terminal needs to be cleared if its sized changed. - clearNeeded bool - - // mu protects termdash. - mu sync.Mutex - - // Options. - redrawInterval time.Duration - errorHandler func(error) - mouseSubscriber func(*terminalapi.Mouse) - keyboardSubscriber func(*terminalapi.Keyboard) -} - -// newTermdash creates a new termdash. -func newTermdash(t terminalapi.Terminal, c *container.Container, opts ...Option) *termdash { - td := &termdash{ - term: t, - container: c, - eds: event.NewDistributionSystem(), - closeCh: make(chan struct{}), - exitCh: make(chan struct{}), - redrawInterval: DefaultRedrawInterval, - } - - for _, opt := range opts { - opt.set(td) - } - td.subscribers() - c.Subscribe(td.eds) - return td -} - -// subscribers subscribes event receivers that live in this package to EDS. -func (td *termdash) subscribers() { - // Handler for all errors that occur during input event processing. - td.eds.Subscribe([]terminalapi.Event{terminalapi.NewError("")}, func(ev terminalapi.Event) { - td.handleError(ev.(*terminalapi.Error).Error()) - }) - - // Handles terminal resize events. - td.eds.Subscribe([]terminalapi.Event{&terminalapi.Resize{}}, func(terminalapi.Event) { - td.setClearNeeded() - }) - - // Redraws the screen on Keyboard and Mouse events. - // These events very likely change the content of the widgets (e.g. zooming - // a LineChart) so a redraw is needed to make that visible. - td.eds.Subscribe([]terminalapi.Event{ - &terminalapi.Keyboard{}, - &terminalapi.Mouse{}, - }, func(terminalapi.Event) { - td.evRedraw() - }, event.MaxRepetitive(0)) // No repetitive events that cause terminal redraw. - - // Keyboard and Mouse subscribers specified via options. - if td.keyboardSubscriber != nil { - td.eds.Subscribe([]terminalapi.Event{&terminalapi.Keyboard{}}, func(ev terminalapi.Event) { - td.keyboardSubscriber(ev.(*terminalapi.Keyboard)) - }) - } - if td.mouseSubscriber != nil { - td.eds.Subscribe([]terminalapi.Event{&terminalapi.Mouse{}}, func(ev terminalapi.Event) { - td.mouseSubscriber(ev.(*terminalapi.Mouse)) - }) - } -} - -// handleError forwards the error to the error handler if one was -// provided or panics. -func (td *termdash) handleError(err error) { - if td.errorHandler != nil { - td.errorHandler(err) - } else { - panic(err) - } -} - -// setClearNeeded flags that the terminal needs to be cleared next time we're -// drawing it. -func (td *termdash) setClearNeeded() { - td.mu.Lock() - defer td.mu.Unlock() - td.clearNeeded = true -} - -// redraw redraws the container and its widgets. -// The caller must hold td.mu. -func (td *termdash) redraw() error { - if td.clearNeeded { - if err := td.term.Clear(); err != nil { - return fmt.Errorf("term.Clear => error: %v", err) - } - td.clearNeeded = false - } - - if err := td.container.Draw(); err != nil { - return fmt.Errorf("container.Draw => error: %v", err) - } - - if err := td.term.Flush(); err != nil { - return fmt.Errorf("term.Flush => error: %v", err) - } - return nil -} - -// evRedraw redraws the container and its widgets. -func (td *termdash) evRedraw() error { - td.mu.Lock() - defer td.mu.Unlock() - - // Don't redraw immediately, give widgets that are performing enough time - // to update. - // We don't want to actually synchronize until all widgets update, we are - // purposefully leaving slow widgets behind. - time.Sleep(25 * time.Millisecond) - return td.redraw() -} - -// periodicRedraw is called once each RedrawInterval. -func (td *termdash) periodicRedraw() error { - td.mu.Lock() - defer td.mu.Unlock() - return td.redraw() -} - -// processEvents processes terminal input events. -// This is the body of the event collecting goroutine. -func (td *termdash) processEvents(ctx context.Context) { - defer close(td.exitCh) - - for { - ev := td.term.Event(ctx) - if ev != nil { - td.eds.Event(ev) - } - - select { - case <-ctx.Done(): - return - default: - } - } -} - -// start starts the terminal dashboard. Blocks until the context expires or -// until stop() is called. -func (td *termdash) start(ctx context.Context) error { - // Redraw once to initialize the container sizes. - if err := td.periodicRedraw(); err != nil { - close(td.exitCh) - return err - } - - redrawTimer := time.NewTicker(td.redrawInterval) - defer redrawTimer.Stop() - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - // stops when stop() is called or the context expires. - go td.processEvents(ctx) - - for { - select { - case <-redrawTimer.C: - if err := td.periodicRedraw(); err != nil { - return err - } - - case <-ctx.Done(): - return nil - - case <-td.closeCh: - return nil - } - } -} - -// stop tells the event collecting goroutine to stop. -// Blocks until it exits. -func (td *termdash) stop() { - close(td.closeCh) - <-td.exitCh -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/cell_options.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/cell_options.go deleted file mode 100644 index 41ee76013..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/cell_options.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package termbox - -// cell_options.go converts termdash cell options to the termbox format. - -import ( - "github.com/mum4k/termdash/cell" - tbx "github.com/nsf/termbox-go" -) - -// cellColor converts termdash cell color to the termbox format. -func cellColor(c cell.Color) tbx.Attribute { - return tbx.Attribute(c) -} - -// cellOptsToFg converts the cell options to the termbox foreground attribute. -func cellOptsToFg(opts *cell.Options) tbx.Attribute { - return cellColor(opts.FgColor) -} - -// cellOptsToBg converts the cell options to the termbox background attribute. -func cellOptsToBg(opts *cell.Options) tbx.Attribute { - return cellColor(opts.BgColor) -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/color_mode.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/color_mode.go deleted file mode 100644 index 793f2a966..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/color_mode.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package termbox - -import ( - "fmt" - - "github.com/mum4k/termdash/terminal/terminalapi" - tbx "github.com/nsf/termbox-go" -) - -// colorMode converts termdash color modes to the termbox format. -func colorMode(cm terminalapi.ColorMode) (tbx.OutputMode, error) { - switch cm { - case terminalapi.ColorModeNormal: - return tbx.OutputNormal, nil - case terminalapi.ColorMode256: - return tbx.Output256, nil - case terminalapi.ColorMode216: - return tbx.Output216, nil - case terminalapi.ColorModeGrayscale: - return tbx.OutputGrayscale, nil - default: - return -1, fmt.Errorf("don't know how to convert color mode %v to the termbox format", cm) - } -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/event.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/event.go deleted file mode 100644 index c26d88c18..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/event.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package termbox - -// event.go converts termbox events to the termdash format. - -import ( - "image" - - "github.com/mum4k/termdash/keyboard" - "github.com/mum4k/termdash/mouse" - "github.com/mum4k/termdash/terminal/terminalapi" - tbx "github.com/nsf/termbox-go" -) - -// tbxToTd maps termbox key values to the termdash format. -var tbxToTd = map[tbx.Key]keyboard.Key{ - tbx.KeySpace: keyboard.KeySpace, - tbx.KeyF1: keyboard.KeyF1, - tbx.KeyF2: keyboard.KeyF2, - tbx.KeyF3: keyboard.KeyF3, - tbx.KeyF4: keyboard.KeyF4, - tbx.KeyF5: keyboard.KeyF5, - tbx.KeyF6: keyboard.KeyF6, - tbx.KeyF7: keyboard.KeyF7, - tbx.KeyF8: keyboard.KeyF8, - tbx.KeyF9: keyboard.KeyF9, - tbx.KeyF10: keyboard.KeyF10, - tbx.KeyF11: keyboard.KeyF11, - tbx.KeyF12: keyboard.KeyF12, - tbx.KeyInsert: keyboard.KeyInsert, - tbx.KeyDelete: keyboard.KeyDelete, - tbx.KeyHome: keyboard.KeyHome, - tbx.KeyEnd: keyboard.KeyEnd, - tbx.KeyPgup: keyboard.KeyPgUp, - tbx.KeyPgdn: keyboard.KeyPgDn, - tbx.KeyArrowUp: keyboard.KeyArrowUp, - tbx.KeyArrowDown: keyboard.KeyArrowDown, - tbx.KeyArrowLeft: keyboard.KeyArrowLeft, - tbx.KeyArrowRight: keyboard.KeyArrowRight, - tbx.KeyCtrlTilde: keyboard.KeyCtrlTilde, - tbx.KeyCtrlA: keyboard.KeyCtrlA, - tbx.KeyCtrlB: keyboard.KeyCtrlB, - tbx.KeyCtrlC: keyboard.KeyCtrlC, - tbx.KeyCtrlD: keyboard.KeyCtrlD, - tbx.KeyCtrlE: keyboard.KeyCtrlE, - tbx.KeyCtrlF: keyboard.KeyCtrlF, - tbx.KeyCtrlG: keyboard.KeyCtrlG, - tbx.KeyBackspace: keyboard.KeyBackspace, - tbx.KeyTab: keyboard.KeyTab, - tbx.KeyCtrlJ: keyboard.KeyCtrlJ, - tbx.KeyCtrlK: keyboard.KeyCtrlK, - tbx.KeyCtrlL: keyboard.KeyCtrlL, - tbx.KeyEnter: keyboard.KeyEnter, - tbx.KeyCtrlN: keyboard.KeyCtrlN, - tbx.KeyCtrlO: keyboard.KeyCtrlO, - tbx.KeyCtrlP: keyboard.KeyCtrlP, - tbx.KeyCtrlQ: keyboard.KeyCtrlQ, - tbx.KeyCtrlR: keyboard.KeyCtrlR, - tbx.KeyCtrlS: keyboard.KeyCtrlS, - tbx.KeyCtrlT: keyboard.KeyCtrlT, - tbx.KeyCtrlU: keyboard.KeyCtrlU, - tbx.KeyCtrlV: keyboard.KeyCtrlV, - tbx.KeyCtrlW: keyboard.KeyCtrlW, - tbx.KeyCtrlX: keyboard.KeyCtrlX, - tbx.KeyCtrlY: keyboard.KeyCtrlY, - tbx.KeyCtrlZ: keyboard.KeyCtrlZ, - tbx.KeyEsc: keyboard.KeyEsc, - tbx.KeyCtrl4: keyboard.KeyCtrl4, - tbx.KeyCtrl5: keyboard.KeyCtrl5, - tbx.KeyCtrl6: keyboard.KeyCtrl6, - tbx.KeyCtrl7: keyboard.KeyCtrl7, - tbx.KeyBackspace2: keyboard.KeyBackspace2, -} - -// convKey converts a termbox keyboard event to the termdash format. -func convKey(tbxEv tbx.Event) terminalapi.Event { - if tbxEv.Key != 0 && tbxEv.Ch != 0 { - return terminalapi.NewErrorf("the key event contain both a key(%v) and a character(%v)", tbxEv.Key, tbxEv.Ch) - } - - if tbxEv.Ch != 0 { - return &terminalapi.Keyboard{ - Key: keyboard.Key(tbxEv.Ch), - } - } - - k, ok := tbxToTd[tbxEv.Key] - if !ok { - return terminalapi.NewErrorf("unknown keyboard key '%v' in a keyboard event", k) - } - return &terminalapi.Keyboard{ - Key: k, - } -} - -// convMouse converts a termbox mouse event to the termdash format. -func convMouse(tbxEv tbx.Event) terminalapi.Event { - var button mouse.Button - - switch k := tbxEv.Key; k { - case tbx.MouseLeft: - button = mouse.ButtonLeft - case tbx.MouseMiddle: - button = mouse.ButtonMiddle - case tbx.MouseRight: - button = mouse.ButtonRight - case tbx.MouseRelease: - button = mouse.ButtonRelease - case tbx.MouseWheelUp: - button = mouse.ButtonWheelUp - case tbx.MouseWheelDown: - button = mouse.ButtonWheelDown - default: - return terminalapi.NewErrorf("unknown mouse key %v in a mouse event", k) - } - - return &terminalapi.Mouse{ - Position: image.Point{tbxEv.MouseX, tbxEv.MouseY}, - Button: button, - } -} - -// convResize converts a termbox resize event to the termdash format. -func convResize(tbxEv tbx.Event) terminalapi.Event { - size := image.Point{tbxEv.Width, tbxEv.Height} - if size.X < 0 || size.Y < 0 { - return terminalapi.NewErrorf("terminal resized to negative size: %v", size) - } - return &terminalapi.Resize{ - Size: size, - } -} - -// toTermdashEvents converts a termbox event to the termdash event format. -func toTermdashEvents(tbxEv tbx.Event) []terminalapi.Event { - switch t := tbxEv.Type; t { - case tbx.EventInterrupt: - return []terminalapi.Event{ - terminalapi.NewError("event type EventInterrupt isn't supported"), - } - case tbx.EventRaw: - return []terminalapi.Event{ - terminalapi.NewError("event type EventRaw isn't supported"), - } - case tbx.EventNone: - return []terminalapi.Event{ - terminalapi.NewError("event type EventNone isn't supported"), - } - case tbx.EventError: - return []terminalapi.Event{ - terminalapi.NewErrorf("input error occurred: %v", tbxEv.Err), - } - case tbx.EventResize: - return []terminalapi.Event{convResize(tbxEv)} - case tbx.EventMouse: - return []terminalapi.Event{convMouse(tbxEv)} - case tbx.EventKey: - return []terminalapi.Event{ - convKey(tbxEv), - } - default: - return []terminalapi.Event{ - terminalapi.NewErrorf("unknown termbox event type: %v", t), - } - } -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/termbox.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/termbox.go deleted file mode 100644 index 4329e4686..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/termbox.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package termbox implements terminal using the nsf/termbox-go library. -package termbox - -import ( - "context" - "image" - - "github.com/mum4k/termdash/cell" - "github.com/mum4k/termdash/private/event/eventqueue" - "github.com/mum4k/termdash/terminal/terminalapi" - tbx "github.com/nsf/termbox-go" -) - -// Option is used to provide options. -type Option interface { - // set sets the provided option. - set(*Terminal) -} - -// option implements Option. -type option func(*Terminal) - -// set implements Option.set. -func (o option) set(t *Terminal) { - o(t) -} - -// DefaultColorMode is the default value for the ColorMode option. -const DefaultColorMode = terminalapi.ColorMode256 - -// ColorMode sets the terminal color mode. -// Defaults to DefaultColorMode. -func ColorMode(cm terminalapi.ColorMode) Option { - return option(func(t *Terminal) { - t.colorMode = cm - }) -} - -// Terminal provides input and output to a real terminal. Wraps the -// nsf/termbox-go terminal implementation. This object is not thread-safe. -// Implements terminalapi.Terminal. -type Terminal struct { - // events is a queue of input events. - events *eventqueue.Unbound - - // done gets closed when Close() is called. - done chan struct{} - - // Options. - colorMode terminalapi.ColorMode -} - -// newTerminal creates the terminal and applies the options. -func newTerminal(opts ...Option) *Terminal { - t := &Terminal{ - events: eventqueue.New(), - done: make(chan struct{}), - colorMode: DefaultColorMode, - } - for _, opt := range opts { - opt.set(t) - } - return t -} - -// New returns a new termbox based Terminal. -// Call Close() when the terminal isn't required anymore. -func New(opts ...Option) (*Terminal, error) { - if err := tbx.Init(); err != nil { - return nil, err - } - tbx.SetInputMode(tbx.InputEsc | tbx.InputMouse) - - t := newTerminal(opts...) - om, err := colorMode(t.colorMode) - if err != nil { - return nil, err - } - tbx.SetOutputMode(om) - - go t.pollEvents() // Stops when Close() is called. - return t, nil -} - -// Size implements terminalapi.Terminal.Size. -func (t *Terminal) Size() image.Point { - w, h := tbx.Size() - return image.Point{w, h} -} - -// Clear implements terminalapi.Terminal.Clear. -func (t *Terminal) Clear(opts ...cell.Option) error { - o := cell.NewOptions(opts...) - return tbx.Clear(cellOptsToFg(o), cellOptsToBg(o)) -} - -// Flush implements terminalapi.Terminal.Flush. -func (t *Terminal) Flush() error { - return tbx.Flush() -} - -// SetCursor implements terminalapi.Terminal.SetCursor. -func (t *Terminal) SetCursor(p image.Point) { - tbx.SetCursor(p.X, p.Y) -} - -// HideCursor implements terminalapi.Terminal.HideCursor. -func (t *Terminal) HideCursor() { - tbx.HideCursor() -} - -// SetCell implements terminalapi.Terminal.SetCell. -func (t *Terminal) SetCell(p image.Point, r rune, opts ...cell.Option) error { - o := cell.NewOptions(opts...) - tbx.SetCell(p.X, p.Y, r, cellOptsToFg(o), cellOptsToBg(o)) - return nil -} - -// pollEvents polls and enqueues the input events. -func (t *Terminal) pollEvents() { - for { - select { - case <-t.done: - return - default: - } - - events := toTermdashEvents(tbx.PollEvent()) - for _, ev := range events { - t.events.Push(ev) - } - } -} - -// Event implements terminalapi.Terminal.Event. -func (t *Terminal) Event(ctx context.Context) terminalapi.Event { - ev := t.events.Pull(ctx) - if ev == nil { - return nil - } - return ev -} - -// Close closes the terminal, should be called when the terminal isn't required -// anymore to return the screen to a sane state. -// Implements terminalapi.Terminal.Close. -func (t *Terminal) Close() { - close(t.done) - tbx.Close() -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/color_mode.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/color_mode.go deleted file mode 100644 index 28c93277f..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/color_mode.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package terminalapi - -// color_mode.go defines the terminal color modes. - -// ColorMode represents a color mode of a terminal. -type ColorMode int - -// String implements fmt.Stringer() -func (cm ColorMode) String() string { - if n, ok := colorModeNames[cm]; ok { - return n - } - return "ColorModeUnknown" -} - -// colorModeNames maps ColorMode values to human readable names. -var colorModeNames = map[ColorMode]string{ - ColorModeNormal: "ColorModeNormal", - ColorMode256: "ColorMode256", - ColorMode216: "ColorMode216", - ColorModeGrayscale: "ColorModeGrayscale", -} - -// Supported color modes. -const ( - // ColorModeNormal supports 8 "system" colors. - // These are defined as constants in the cell package. - ColorModeNormal ColorMode = iota - - // ColorMode256 enables using any of the 256 terminal colors. - // 0-7: the 8 "system" colors accessible in ColorModeNormal. - // 8-15: the 8 "bright system" colors. - // 16-231: the 216 different terminal colors. - // 232-255: the 24 different shades of grey. - ColorMode256 - - // ColorMode216 supports only the third range of the ColorMode256, i.e the - // 216 different terminal colors. However in this mode the colors are zero - // based, so the caller doesn't need to provide an offset. - ColorMode216 - - // ColorModeGrayscale supports only the fourth range of the ColorMode256, - // i.e the 24 different shades of grey. However in this mode the colors are - // zero based, so the caller doesn't need to provide an offset. - ColorModeGrayscale -) diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/event.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/event.go deleted file mode 100644 index a543e8433..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/event.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package terminalapi - -import ( - "errors" - "fmt" - "image" - - "github.com/mum4k/termdash/keyboard" - "github.com/mum4k/termdash/mouse" -) - -// event.go defines events that can be received through the terminal API. - -// Event represents an input event. -type Event interface { - isEvent() -} - -// Keyboard is the event used when a key is pressed. -// Implements terminalapi.Event. -type Keyboard struct { - // Key is the pressed key. - Key keyboard.Key -} - -func (*Keyboard) isEvent() {} - -// String implements fmt.Stringer. -func (k Keyboard) String() string { - return fmt.Sprintf("Keyboard{Key: %v}", k.Key) -} - -// Resize is the event used when the terminal was resized. -// Implements terminalapi.Event. -type Resize struct { - // Size is the new size of the terminal. - Size image.Point -} - -func (*Resize) isEvent() {} - -// String implements fmt.Stringer. -func (r Resize) String() string { - return fmt.Sprintf("Resize{Size: %v}", r.Size) -} - -// Mouse is the event used when the mouse is moved or a mouse button is -// pressed. -// Implements terminalapi.Event. -type Mouse struct { - // Position of the mouse on the terminal. - Position image.Point - // Button identifies the pressed button if any. - Button mouse.Button -} - -func (*Mouse) isEvent() {} - -// String implements fmt.Stringer. -func (m Mouse) String() string { - return fmt.Sprintf("Mouse{Position: %v, Button: %v}", m.Position, m.Button) -} - -// Error is an event indicating an error while processing input. -type Error string - -// NewError returns a new Error event. -func NewError(e string) *Error { - err := Error(e) - return &err -} - -// NewErrorf returns a new Error event, arguments are similar to fmt.Sprintf. -func NewErrorf(format string, args ...interface{}) *Error { - err := Error(fmt.Sprintf(format, args...)) - return &err -} - -func (*Error) isEvent() {} - -// Error returns the error that occurred. -func (e *Error) Error() error { - if e == nil || *e == "" { - return nil - } - return errors.New(string(*e)) -} - -// String implements fmt.Stringer. -func (e Error) String() string { - return string(e) -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/terminalapi.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/terminalapi.go deleted file mode 100644 index 831abc169..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/terminalapi.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package terminalapi defines the API of all terminal implementations. -package terminalapi - -import ( - "context" - "image" - - "github.com/mum4k/termdash/cell" -) - -// Terminal abstracts an implementation of a 2-D terminal. -// A terminal consists of a number of cells. -type Terminal interface { - // Size returns the terminal width and height in cells. - Size() image.Point - - // Clear clears the content of the internal back buffer, resetting all - // cells to their default content and attributes. Sets the provided options - // on all the cell. - Clear(opts ...cell.Option) error - // Flush flushes the internal back buffer to the terminal. - Flush() error - - // SetCursor sets the position of the cursor. - SetCursor(p image.Point) - // HideCursos hides the cursor. - HideCursor() - - // SetCell sets the value of the specified cell to the provided rune. - // Use the options to specify which attributes to modify, if an attribute - // option isn't specified, the attribute retains its previous value. - SetCell(p image.Point, r rune, opts ...cell.Option) error - - // Event waits for the next event and returns it. - // This call blocks until the next event or cancellation of the context. - // Returns nil when the context gets canceled. - Event(ctx context.Context) Event - - // Close closes the underlying terminal implementation and should be called when - // the terminal isn't required anymore to return the screen to a sane state. - Close() -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgetapi/widgetapi.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgetapi/widgetapi.go deleted file mode 100644 index ee27136a1..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgetapi/widgetapi.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package widgetapi defines the API of a widget on the dashboard. -package widgetapi - -import ( - "image" - - "github.com/mum4k/termdash/private/canvas" - "github.com/mum4k/termdash/terminal/terminalapi" -) - -// KeyScope indicates the scope at which the widget wants to receive keyboard -// events. -type KeyScope int - -// String implements fmt.Stringer() -func (ks KeyScope) String() string { - if n, ok := keyScopeNames[ks]; ok { - return n - } - return "KeyScopeUnknown" -} - -// keyScopeNames maps KeyScope values to human readable names. -var keyScopeNames = map[KeyScope]string{ - KeyScopeNone: "KeyScopeNone", - KeyScopeFocused: "KeyScopeFocused", - KeyScopeGlobal: "KeyScopeGlobal", -} - -const ( - // KeyScopeNone is used when the widget doesn't want to receive any - // keyboard events. - KeyScopeNone KeyScope = iota - - // KeyScopeFocused is used when the widget wants to only receive keyboard - // events when its container is focused. - KeyScopeFocused - - // KeyScopeGlobal is used when the widget wants to receive all keyboard - // events regardless of which container is focused. - KeyScopeGlobal -) - -// MouseScope indicates the scope at which the widget wants to receive mouse -// events. -type MouseScope int - -// String implements fmt.Stringer() -func (ms MouseScope) String() string { - if n, ok := mouseScopeNames[ms]; ok { - return n - } - return "MouseScopeUnknown" -} - -// mouseScopeNames maps MouseScope values to human readable names. -var mouseScopeNames = map[MouseScope]string{ - MouseScopeNone: "MouseScopeNone", - MouseScopeWidget: "MouseScopeWidget", - MouseScopeContainer: "MouseScopeContainer", - MouseScopeGlobal: "MouseScopeGlobal", -} - -const ( - // MouseScopeNone is used when the widget doesn't want to receive any mouse - // events. - MouseScopeNone MouseScope = iota - - // MouseScopeWidget is used when the widget only wants mouse events that - // fall onto its canvas. - // The position of these widgets is always relative to widget's canvas. - MouseScopeWidget - - // MouseScopeContainer is used when the widget only wants mouse events that - // fall onto its container. The area size of a container is always larger - // or equal to the one of the widget's canvas. So a widget selecting - // MouseScopeContainer will either receive the same or larger amount of - // events as compared to MouseScopeWidget. - // The position of mouse events that fall outside of widget's canvas is - // reset to image.Point{-1, -1}. - // The widgets are allowed to process the button event. - MouseScopeContainer - - // MouseScopeGlobal is used when the widget wants to receive all mouse - // events regardless on where on the terminal they land. - // The position of mouse events that fall outside of widget's canvas is - // reset to image.Point{-1, -1} and must not be used by the widgets. - // The widgets are allowed to process the button event. - MouseScopeGlobal -) - -// Options contains registration options for a widget. -// This is how the widget indicates its needs to the infrastructure. -type Options struct { - // Ratio allows a widget to request a canvas whose size will always have - // the specified ratio of width:height (Ratio.X:Ratio.Y). - // The zero value i.e. image.Point{0, 0} indicates that the widget accepts - // canvas of any ratio. - Ratio image.Point - - // MinimumSize allows a widget to specify the smallest allowed canvas size. - // If the terminal size and/or splits cause the assigned canvas to be - // smaller than this, the widget will be skipped. I.e. The Draw() method - // won't be called until a resize above the specified minimum. - MinimumSize image.Point - - // MaximumSize allows a widget to specify the largest allowed canvas size. - // If the terminal size and/or splits cause the assigned canvas to be larger - // than this, the widget will only receive a canvas of this size within its - // container. Setting any of the two coordinates to zero indicates - // unlimited. - MaximumSize image.Point - - // WantKeyboard allows a widget to request keyboard events and specify - // their desired scope. If set to KeyScopeNone, no keyboard events are - // forwarded to the widget. - WantKeyboard KeyScope - - // WantMouse allows a widget to request mouse events and specify their - // desired scope. If set to MouseScopeNone, no mouse events are forwarded - // to the widget. - // Note that the widget is only able to see the position of the mouse event - // if it falls onto its canvas. See the documentation next to individual - // MouseScope values for details. - WantMouse MouseScope -} - -// Meta provide additional metadata to widgets. -type Meta struct { - // Focused asserts whether the widget's container is focused. - Focused bool -} - -// Widget is a single widget on the dashboard. -// Implementations must be thread safe. -type Widget interface { - // When the infrastructure calls Draw(), the widget must block on the call - // until it finishes drawing onto the provided canvas. When given the - // canvas, the widget must first determine its size by calling - // Canvas.Size(), then limit all its drawing to this area. - // - // The widget must not assume that the size of the canvas or its content - // remains the same between calls. - // - // The argument meta is guaranteed to be valid (i.e. non-nil). - Draw(cvs *canvas.Canvas, meta *Meta) error - - // Keyboard is called when the widget is focused on the dashboard and a key - // shortcut the widget registered for was pressed. Only called if the widget - // registered for keyboard events. - Keyboard(k *terminalapi.Keyboard) error - - // Mouse is called when the widget is focused on the dashboard and a mouse - // event happens on its canvas. Only called if the widget registered for mouse - // events. - Mouse(m *terminalapi.Mouse) error - - // Options returns registration options for the widget. - // This is how the widget indicates to the infrastructure whether it is - // interested in keyboard or mouse shortcuts, what is its minimum canvas - // size, etc. - // - // Most widgets will return statically compiled options (minimum and - // maximum size, etc.). If the returned options depend on the runtime state - // of the widget (e.g. the user data provided to the widget), the widget - // must protect against a case where the infrastructure calls the Draw - // method with a canvas that doesn't meet the requested options. This is - // because the data in the widget might change between calls to Options and - // Draw. - Options() Options -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/line_trim.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/line_trim.go deleted file mode 100644 index 6ca8c83aa..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/line_trim.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package text - -import ( - "fmt" - "image" - - "github.com/mum4k/termdash/private/canvas" - "github.com/mum4k/termdash/private/runewidth" - "github.com/mum4k/termdash/private/wrap" -) - -// line_trim.go contains code that trims lines that are too long. - -type trimResult struct { - // trimmed is set to true if the current and the following runes on this - // line are trimmed. - trimmed bool - - // curPoint is the updated current point the drawing should continue on. - curPoint image.Point -} - -// drawTrimChar draws the horizontal ellipsis '…' character as the last -// character in the canvas on the specified line. -func drawTrimChar(cvs *canvas.Canvas, line int) error { - lastPoint := image.Point{cvs.Area().Dx() - 1, line} - // If the penultimate cell contains a full-width rune, we need to clear it - // first. Otherwise the trim char would cover just half of it. - if width := cvs.Area().Dx(); width > 1 { - penUlt := image.Point{width - 2, line} - prev, err := cvs.Cell(penUlt) - if err != nil { - return err - } - - if runewidth.RuneWidth(prev.Rune) == 2 { - if _, err := cvs.SetCell(penUlt, 0); err != nil { - return err - } - } - } - - cells, err := cvs.SetCell(lastPoint, '…') - if err != nil { - return err - } - if cells != 1 { - panic(fmt.Errorf("invalid trim character, it occupies %d cells, the implementation only supports scroll markers that occupy exactly one cell", cells)) - } - return nil -} - -// lineTrim determines if the current line needs to be trimmed. The cvs is the -// canvas assigned to the widget, the curPoint is the current point the widget -// is going to place the curRune at. If line trimming is needed, this function -// replaces the last character with the horizontal ellipsis '…' character. -func lineTrim(cvs *canvas.Canvas, curPoint image.Point, curRune rune, opts *options) (*trimResult, error) { - if opts.wrapMode == wrap.AtRunes { - // Don't trim if the widget is configured to wrap lines. - return &trimResult{ - trimmed: false, - curPoint: curPoint, - }, nil - } - - // Newline characters are never trimmed, they start the next line. - if curRune == '\n' { - return &trimResult{ - trimmed: false, - curPoint: curPoint, - }, nil - } - - width := cvs.Area().Dx() - rw := runewidth.RuneWidth(curRune) - switch { - case rw == 1: - if curPoint.X == width { - if err := drawTrimChar(cvs, curPoint.Y); err != nil { - return nil, err - } - } - - case rw == 2: - if curPoint.X == width || curPoint.X == width-1 { - if err := drawTrimChar(cvs, curPoint.Y); err != nil { - return nil, err - } - } - - default: - return nil, fmt.Errorf("unable to decide line trimming at position %v for rune %q which has an unsupported width %d", curPoint, curRune, rw) - } - - trimmed := curPoint.X > width-rw - if trimmed { - curPoint = image.Point{curPoint.X + rw, curPoint.Y} - } - return &trimResult{ - trimmed: trimmed, - curPoint: curPoint, - }, nil -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/options.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/options.go deleted file mode 100644 index b91cec85e..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/options.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package text - -import ( - "fmt" - - "github.com/mum4k/termdash/keyboard" - "github.com/mum4k/termdash/mouse" - "github.com/mum4k/termdash/private/wrap" -) - -// options.go contains configurable options for Text. - -// Option is used to provide options to New(). -type Option interface { - // set sets the provided option. - set(*options) -} - -// options stores the provided options. -type options struct { - wrapMode wrap.Mode - rollContent bool - disableScrolling bool - mouseUpButton mouse.Button - mouseDownButton mouse.Button - keyUp keyboard.Key - keyDown keyboard.Key - keyPgUp keyboard.Key - keyPgDown keyboard.Key -} - -// newOptions returns a new options instance. -func newOptions(opts ...Option) *options { - opt := &options{ - mouseUpButton: DefaultScrollMouseButtonUp, - mouseDownButton: DefaultScrollMouseButtonDown, - keyUp: DefaultScrollKeyUp, - keyDown: DefaultScrollKeyDown, - keyPgUp: DefaultScrollKeyPageUp, - keyPgDown: DefaultScrollKeyPageDown, - } - for _, o := range opts { - o.set(opt) - } - return opt -} - -// validate validates the provided options. -func (o *options) validate() error { - keys := map[keyboard.Key]bool{ - o.keyUp: true, - o.keyDown: true, - o.keyPgUp: true, - o.keyPgDown: true, - } - if len(keys) != 4 { - return fmt.Errorf("invalid ScrollKeys(up:%v, down:%v, pageUp:%v, pageDown:%v), the keys must be unique", o.keyUp, o.keyDown, o.keyPgUp, o.keyPgDown) - } - if o.mouseUpButton == o.mouseDownButton { - return fmt.Errorf("invalid ScrollMouseButtons(up:%v, down:%v), the buttons must be unique", o.mouseUpButton, o.mouseDownButton) - } - return nil -} - -// option implements Option. -type option func(*options) - -// set implements Option.set. -func (o option) set(opts *options) { - o(opts) -} - -// WrapAtWords configures the text widget so that it automatically wraps lines -// that are longer than the width of the widget at word boundaries. If not -// provided, long lines are trimmed instead. -func WrapAtWords() Option { - return option(func(opts *options) { - opts.wrapMode = wrap.AtWords - }) -} - -// WrapAtRunes configures the text widget so that it automatically wraps lines -// that are longer than the width of the widget at rune boundaries. If not -// provided, long lines are trimmed instead. -func WrapAtRunes() Option { - return option(func(opts *options) { - opts.wrapMode = wrap.AtRunes - }) -} - -// RollContent configures the text widget so that it rolls the text content up -// if more text than the size of the container is added. If not provided, the -// content is trimmed instead. -func RollContent() Option { - return option(func(opts *options) { - opts.rollContent = true - }) -} - -// DisableScrolling disables the scrolling of the content using keyboard and -// mouse. -func DisableScrolling() Option { - return option(func(opts *options) { - opts.disableScrolling = true - }) -} - -// The default mouse buttons for content scrolling. -const ( - DefaultScrollMouseButtonUp = mouse.ButtonWheelUp - DefaultScrollMouseButtonDown = mouse.ButtonWheelDown -) - -// ScrollMouseButtons configures the mouse buttons that scroll the content. -// The provided buttons must be unique, e.g. the same button cannot be both up -// and down. -func ScrollMouseButtons(up, down mouse.Button) Option { - return option(func(opts *options) { - opts.mouseUpButton = up - opts.mouseDownButton = down - }) -} - -// The default keys for content scrolling. -const ( - DefaultScrollKeyUp = keyboard.KeyArrowUp - DefaultScrollKeyDown = keyboard.KeyArrowDown - DefaultScrollKeyPageUp = keyboard.KeyPgUp - DefaultScrollKeyPageDown = keyboard.KeyPgDn -) - -// ScrollKeys configures the keyboard keys that scroll the content. -// The provided keys must be unique, e.g. the same key cannot be both up and -// down. -func ScrollKeys(up, down, pageUp, pageDown keyboard.Key) Option { - return option(func(opts *options) { - opts.keyUp = up - opts.keyDown = down - opts.keyPgUp = pageUp - opts.keyPgDown = pageDown - }) -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/scroll.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/scroll.go deleted file mode 100644 index f63092fdb..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/scroll.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package text - -// scroll.go contains code that tracks the current scrolling position. - -import "math" - -// scrollTracker tracks the current scrolling position for the Text widget. -// -// The text widget displays the contained text buffer as lines of text that fit -// the widget's canvas. The main goal of this object is to inform the text -// widget which should be the first drawn line from the buffer. This depends on -// two things, the scrolling position based on user inputs and whether the text -// widget is configured to roll the content up as new text is added by the -// client. -// -// The rolling Vs. scrolling state is tracked in an FSM implemented in this -// file. -// -// The client can scroll the content by either a keyboard or a mouse event. The -// widget receives these events concurrently with requests to redraw the -// content, so this objects keeps a track of all the scrolling events that -// happened since the last redraw and consumes them when calculating which is -// the first drawn line on the next redraw event. -// -// This is not thread safe. -type scrollTracker struct { - // scroll stores user requests to scroll up (negative) or down (positive). - // E.g. -1 means up by one line and 2 means down by two lines. - scroll int - - // scrollPage stores user requests to scroll up (negative) or down - // (positive) by a page of content. E.g. -1 means up by one page and 2 - // means down by two pages. - scrollPage int - - // first tracks the first line that will be printed. - first int - - // state is the state of the scrolling FSM. - state rollState -} - -// newScrollTracker returns a new scroll tracker. -func newScrollTracker(opts *options) *scrollTracker { - if opts.rollContent { - return &scrollTracker{state: rollToEnd} - } - return &scrollTracker{state: rollingDisabled} -} - -// upOneLine processes a user request to scroll up by one line. -func (st *scrollTracker) upOneLine() { - st.scroll-- -} - -// downOneLine processes a user request to scroll down by one line. -func (st *scrollTracker) downOneLine() { - st.scroll++ -} - -// upOnePage processes a user request to scroll up by one page. -func (st *scrollTracker) upOnePage() { - st.scrollPage-- -} - -// downOnePage processes a user request to scroll down by one page. -func (st *scrollTracker) downOnePage() { - st.scrollPage++ -} - -// doScroll processes any outstanding scroll requests and calculates the -// resulting first line. -func (st *scrollTracker) doScroll(lines, height int) int { - first := st.first + st.scroll + st.scrollPage*height - st.scroll = 0 - st.scrollPage = 0 - return normalizeScroll(first, lines, height) -} - -// firstLine returns the number of the first line that should be drawn on a -// canvas of the specified height if there is the provided number of lines of -// text. -func (st *scrollTracker) firstLine(lines, height int) int { - // Execute the scrolling FSM. - st.state = st.state(st, lines, height) - return st.first -} - -// rollState is a state in the scrolling FSM. -type rollState func(st *scrollTracker, lines, height int) rollState - -// rollingDisabled is a state where content rolling was disabled by the -// configuration of the Text widget. -func rollingDisabled(st *scrollTracker, lines, height int) rollState { - st.first = st.doScroll(lines, height) - return rollingDisabled -} - -// rollToEnd is a state in which the last line of the content is always -// visible. When new content arrives, it is rolled upwards. -func rollToEnd(st *scrollTracker, lines, height int) rollState { - // If the user didn't scroll, just roll the content so that the last line - // is visible. - if st.scroll == 0 && st.scrollPage == 0 { - st.first = normalizeScroll(math.MaxInt32, lines, height) - return rollToEnd - } - - st.first = st.doScroll(lines, height) - if lastLineVisible(st.first, lines, height) { - return rollToEnd - } - return rollingPaused -} - -// rollingPaused is a state in which the user scrolled up and made the last -// line scroll out of the view, so the content rolling is paused. -func rollingPaused(st *scrollTracker, lines, height int) rollState { - st.first = st.doScroll(lines, height) - if lastLineVisible(st.first, lines, height) { - return rollToEnd - } - return rollingPaused -} - -// lastLineVisible returns true if the last text line from within the buffer of -// the text widget is visible on the canvas when drawing of the text starts -// from the specified start line, there is the provided total amount of lines -// and the canvas has the height. -func lastLineVisible(start, lines, height int) bool { - return lines-start <= height -} - -// normalizeScroll returns normalized position of the first line that should be -// drawn when drawing the specified number of lines on a canvas with the -// provided height. -func normalizeScroll(first, lines, height int) int { - if first < 0 || lines <= 0 || height <= 0 { - return 0 - } - - if lines <= height { - return 0 // Scrolling not necessary if the content fits. - } - - max := lines - height - if first > max { - return max - } - return first -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/text.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/text.go deleted file mode 100644 index 0712cbcbe..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/text.go +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package text contains a widget that displays textual data. -package text - -import ( - "fmt" - "image" - "sync" - - "github.com/mum4k/termdash/private/canvas" - "github.com/mum4k/termdash/private/canvas/buffer" - "github.com/mum4k/termdash/private/wrap" - "github.com/mum4k/termdash/terminal/terminalapi" - "github.com/mum4k/termdash/widgetapi" -) - -// Text displays a block of text. -// -// Each line of the text is either trimmed or wrapped according to the provided -// options. The entire text content is either trimmed or rolled up through the -// canvas according to the provided options. -// -// By default the widget supports scrolling of content with either the keyboard -// or mouse. See the options for the default keys and mouse buttons. -// -// Implements widgetapi.Widget. This object is thread-safe. -type Text struct { - // content is the text content that will be displayed in the widget as - // provided by the caller (i.e. not wrapped or pre-processed). - content []*buffer.Cell - // wrapped is the content wrapped to the current width of the canvas. - wrapped [][]*buffer.Cell - - // scroll tracks scrolling the position. - scroll *scrollTracker - - // lastWidth stores the width of the last canvas the widget drew on. - // Used to determine if the previous line wrapping was invalidated. - lastWidth int - // contentChanged indicates if the text content of the widget changed since - // the last drawing. Used to determine if the previous line wrapping was - // invalidated. - contentChanged bool - - // mu protects the Text widget. - mu sync.Mutex - - // opts are the provided options. - opts *options -} - -// New returns a new text widget. -func New(opts ...Option) (*Text, error) { - opt := newOptions(opts...) - if err := opt.validate(); err != nil { - return nil, err - } - return &Text{ - scroll: newScrollTracker(opt), - opts: opt, - }, nil -} - -// Reset resets the widget back to empty content. -func (t *Text) Reset() { - t.mu.Lock() - defer t.mu.Unlock() - t.reset() -} - -// reset implements Reset, caller must hold t.mu. -func (t *Text) reset() { - t.content = nil - t.wrapped = nil - t.scroll = newScrollTracker(t.opts) - t.lastWidth = 0 - t.contentChanged = true -} - -// Write writes text for the widget to display. Multiple calls append -// additional text. The text contain cannot control characters -// (unicode.IsControl) or space character (unicode.IsSpace) other than: -// ' ', '\n' -// Any newline ('\n') characters are interpreted as newlines when displaying -// the text. -func (t *Text) Write(text string, wOpts ...WriteOption) error { - t.mu.Lock() - defer t.mu.Unlock() - - if err := wrap.ValidText(text); err != nil { - return err - } - - opts := newWriteOptions(wOpts...) - if opts.replace { - t.reset() - } - for _, r := range text { - t.content = append(t.content, buffer.NewCell(r, opts.cellOpts)) - } - t.contentChanged = true - return nil -} - -// minLinesForMarkers are the minimum amount of lines required on the canvas in -// order to draw the scroll markers ('⇧' and '⇩'). -const minLinesForMarkers = 3 - -// drawScrollUp draws the scroll up marker on the first line if there is more -// text "above" the canvas due to the scrolling position. Returns true if the -// marker was drawn. -func (t *Text) drawScrollUp(cvs *canvas.Canvas, cur image.Point, fromLine int) (bool, error) { - height := cvs.Area().Dy() - if cur.Y == 0 && height >= minLinesForMarkers && fromLine > 0 { - cells, err := cvs.SetCell(cur, '⇧') - if err != nil { - return false, err - } - if cells != 1 { - panic(fmt.Errorf("invalid scroll up marker, it occupies %d cells, the implementation only supports scroll markers that occupy exactly one cell", cells)) - } - return true, nil - } - return false, nil -} - -// drawScrollDown draws the scroll down marker on the last line if there is -// more text "below" the canvas due to the scrolling position. Returns true if -// the marker was drawn. -func (t *Text) drawScrollDown(cvs *canvas.Canvas, cur image.Point, fromLine int) (bool, error) { - height := cvs.Area().Dy() - lines := len(t.wrapped) - if cur.Y == height-1 && height >= minLinesForMarkers && height < lines-fromLine { - cells, err := cvs.SetCell(cur, '⇩') - if err != nil { - return false, err - } - if cells != 1 { - panic(fmt.Errorf("invalid scroll down marker, it occupies %d cells, the implementation only supports scroll markers that occupy exactly one cell", cells)) - } - return true, nil - } - return false, nil -} - -// draw draws the text context on the canvas starting at the specified line. -func (t *Text) draw(cvs *canvas.Canvas) error { - var cur image.Point // Tracks the current drawing position on the canvas. - height := cvs.Area().Dy() - fromLine := t.scroll.firstLine(len(t.wrapped), height) - - for _, line := range t.wrapped[fromLine:] { - // Scroll up marker. - scrlUp, err := t.drawScrollUp(cvs, cur, fromLine) - if err != nil { - return err - } - if scrlUp { - cur = image.Point{0, cur.Y + 1} // Move to the next line. - // Skip one line of text, the marker replaced it. - continue - } - - // Scroll down marker. - scrlDown, err := t.drawScrollDown(cvs, cur, fromLine) - if err != nil { - return err - } - if scrlDown || cur.Y >= height { - break // Skip all lines falling after (under) the canvas. - } - - for _, cell := range line { - tr, err := lineTrim(cvs, cur, cell.Rune, t.opts) - if err != nil { - return err - } - cur = tr.curPoint - if tr.trimmed { - break // Skip over any characters trimmed on the current line. - } - - cells, err := cvs.SetCell(cur, cell.Rune, cell.Opts) - if err != nil { - return err - } - cur = image.Point{cur.X + cells, cur.Y} // Move within the same line. - } - cur = image.Point{0, cur.Y + 1} // Move to the next line. - } - return nil -} - -// Draw draws the text onto the canvas. -// Implements widgetapi.Widget.Draw. -func (t *Text) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error { - t.mu.Lock() - defer t.mu.Unlock() - - width := cvs.Area().Dx() - if len(t.content) > 0 && (t.contentChanged || t.lastWidth != width) { - // The previous text preprocessing (line wrapping) is invalidated when - // new text is added or the width of the canvas changed. - wr, err := wrap.Cells(t.content, width, t.opts.wrapMode) - if err != nil { - return err - } - t.wrapped = wr - } - t.lastWidth = width - - if len(t.wrapped) == 0 { - return nil // Nothing to draw if there's no text. - } - - if err := t.draw(cvs); err != nil { - return err - } - t.contentChanged = false - return nil -} - -// Keyboard implements widgetapi.Widget.Keyboard. -func (t *Text) Keyboard(k *terminalapi.Keyboard) error { - t.mu.Lock() - defer t.mu.Unlock() - - switch { - case k.Key == t.opts.keyUp: - t.scroll.upOneLine() - case k.Key == t.opts.keyDown: - t.scroll.downOneLine() - case k.Key == t.opts.keyPgUp: - t.scroll.upOnePage() - case k.Key == t.opts.keyPgDown: - t.scroll.downOnePage() - } - return nil -} - -// Mouse implements widgetapi.Widget.Mouse. -func (t *Text) Mouse(m *terminalapi.Mouse) error { - t.mu.Lock() - defer t.mu.Unlock() - - switch b := m.Button; { - case b == t.opts.mouseUpButton: - t.scroll.upOneLine() - case b == t.opts.mouseDownButton: - t.scroll.downOneLine() - } - return nil -} - -// Options of the widget -func (t *Text) Options() widgetapi.Options { - var ks widgetapi.KeyScope - var ms widgetapi.MouseScope - if t.opts.disableScrolling { - ks = widgetapi.KeyScopeNone - ms = widgetapi.MouseScopeNone - } else { - ks = widgetapi.KeyScopeFocused - ms = widgetapi.MouseScopeWidget - } - - return widgetapi.Options{ - // At least one line with at least one full-width rune. - MinimumSize: image.Point{1, 1}, - WantMouse: ms, - WantKeyboard: ks, - } -} diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/write_options.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/write_options.go deleted file mode 100644 index ddb5c40bc..000000000 --- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/write_options.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package text - -// write_options.go contains options used when writing content to the Text widget. - -import ( - "github.com/mum4k/termdash/cell" -) - -// WriteOption is used to provide options to Write(). -type WriteOption interface { - // set sets the provided option. - set(*writeOptions) -} - -// writeOptions stores the provided options. -type writeOptions struct { - cellOpts *cell.Options - replace bool -} - -// newWriteOptions returns new writeOptions instance. -func newWriteOptions(wOpts ...WriteOption) *writeOptions { - wo := &writeOptions{ - cellOpts: cell.NewOptions(), - } - for _, o := range wOpts { - o.set(wo) - } - return wo -} - -// writeOption implements WriteOption. -type writeOption func(*writeOptions) - -// set implements WriteOption.set. -func (wo writeOption) set(wOpts *writeOptions) { - wo(wOpts) -} - -// WriteCellOpts sets options on the cells that contain the text. -func WriteCellOpts(opts ...cell.Option) WriteOption { - return writeOption(func(wOpts *writeOptions) { - wOpts.cellOpts = cell.NewOptions(opts...) - }) -} - -// WriteReplace instructs the text widget to replace the entire text content on -// this write instead of appending. -func WriteReplace() WriteOption { - return writeOption(func(wOpts *writeOptions) { - wOpts.replace = true - }) -} diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/AUTHORS b/examples/go-dashboard/src/github.com/nsf/termbox-go/AUTHORS deleted file mode 100644 index fe26fb0fb..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/AUTHORS +++ /dev/null @@ -1,4 +0,0 @@ -# Please keep this file sorted. - -Georg Reinke <guelfey@googlemail.com> -nsf <no.smile.face@gmail.com> diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/LICENSE b/examples/go-dashboard/src/github.com/nsf/termbox-go/LICENSE deleted file mode 100644 index d9bc068ce..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -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. diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/README.md b/examples/go-dashboard/src/github.com/nsf/termbox-go/README.md deleted file mode 100644 index a54d21878..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/README.md +++ /dev/null @@ -1,51 +0,0 @@ -[](http://godoc.org/github.com/nsf/termbox-go) - -## IMPORTANT - -This library is somewhat not maintained anymore. But I'm glad that it did what I wanted the most. It moved people away from "ncurses" mindset and these days we see both re-implementations of termbox API in various languages and even possibly better libs with similar API design. If you're looking for a Go lib that provides terminal-based user interface facilities, I've heard that https://github.com/gdamore/tcell is good (never used it myself). Also for more complicated interfaces and/or computer games I recommend you to consider using HTML-based UI. Having said that, termbox still somewhat works. In fact I'm writing this line of text right now in godit (which is a text editor written using termbox-go). So, be aware. Good luck and have a nice day. - -## 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. - - [nonograminGo](https://github.com/N0RM4L15T/nonograminGo) is a nonogram(aka. picross) in Go - - [tower-of-go](https://github.com/kjirou/tower-of-go) is a tiny maze game that runs on the terminal. - -### API reference -[godoc.org/github.com/nsf/termbox-go](http://godoc.org/github.com/nsf/termbox-go) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/api.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/api.go deleted file mode 100644 index 3adfdc635..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/api.go +++ /dev/null @@ -1,500 +0,0 @@ -// +build !windows - -package termbox - -import ( - "fmt" - "os" - "os/signal" - "runtime" - "syscall" - "time" - - "github.com/mattn/go-runewidth" -) - -// 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" || runtime.GOOS == "freebsd" { - 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() -} diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/api_common.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/api_common.go deleted file mode 100644 index 5ca1371a5..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/api_common.go +++ /dev/null @@ -1,187 +0,0 @@ -// 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 -) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/api_windows.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/api_windows.go deleted file mode 100644 index 373e6c76c..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/api_windows.go +++ /dev/null @@ -1,257 +0,0 @@ -package termbox - -import ( - "syscall" - - "github.com/mattn/go-runewidth" -) - -// 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 { - chars := []char_info{} - for _, char := range diff.chars { - chars = append(chars, char) - if runewidth.RuneWidth(rune(char.char)) > 1 { - chars = append(chars, char_info{ - char: ' ', - attr: char.attr, - }) - } - } - r := small_rect{ - left: 0, - top: diff.pos, - right: term_size.x - 1, - bottom: diff.pos + diff.lines - 1, - } - write_console_output(out, 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 -} diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/collect_terminfo.py b/examples/go-dashboard/src/github.com/nsf/termbox-go/collect_terminfo.py deleted file mode 100644 index 5e50975e6..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/collect_terminfo.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/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) - diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/escwait.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/escwait.go deleted file mode 100644 index b7bbb891f..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/escwait.go +++ /dev/null @@ -1,11 +0,0 @@ -// +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 -} diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/escwait_darwin.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/escwait_darwin.go deleted file mode 100644 index dde69b6cb..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/escwait_darwin.go +++ /dev/null @@ -1,9 +0,0 @@ -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 -} diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_darwin.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_darwin.go deleted file mode 100644 index 25b78f7ab..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_darwin.go +++ /dev/null @@ -1,41 +0,0 @@ -// 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 -) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_darwin_amd64.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_darwin_amd64.go deleted file mode 100644 index 11f25be79..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_darwin_amd64.go +++ /dev/null @@ -1,40 +0,0 @@ -// 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 -) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_dragonfly.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_dragonfly.go deleted file mode 100644 index e03624ebc..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_dragonfly.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 -) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_freebsd.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_freebsd.go deleted file mode 100644 index e03624ebc..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_freebsd.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 -) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_linux.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_linux.go deleted file mode 100644 index b88960de6..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_linux.go +++ /dev/null @@ -1,33 +0,0 @@ -// 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 -) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_netbsd.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_netbsd.go deleted file mode 100644 index 49a3355b9..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_netbsd.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 -) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_openbsd.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_openbsd.go deleted file mode 100644 index 49a3355b9..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_openbsd.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 -) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_windows.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_windows.go deleted file mode 100644 index 472d002a5..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/syscalls_windows.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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 -) diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/termbox.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/termbox.go deleted file mode 100644 index fbe4c3de9..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/termbox.go +++ /dev/null @@ -1,529 +0,0 @@ -// +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 -} diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/termbox_common.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/termbox_common.go deleted file mode 100644 index c3355cc25..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/termbox_common.go +++ /dev/null @@ -1,59 +0,0 @@ -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 -} diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/termbox_windows.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/termbox_windows.go deleted file mode 100644 index d46eb043e..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/termbox_windows.go +++ /dev/null @@ -1,952 +0,0 @@ -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: - if ctrlpressed { - e.Key = KeyCtrlJ - } else { - 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 - } - } - } -} diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/terminfo.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/terminfo.go deleted file mode 100644 index ab2e7a198..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/terminfo.go +++ /dev/null @@ -1,232 +0,0 @@ -// +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, -} diff --git a/examples/go-dashboard/src/github.com/nsf/termbox-go/terminfo_builtin.go b/examples/go-dashboard/src/github.com/nsf/termbox-go/terminfo_builtin.go deleted file mode 100644 index a94866067..000000000 --- a/examples/go-dashboard/src/github.com/nsf/termbox-go/terminfo_builtin.go +++ /dev/null @@ -1,64 +0,0 @@ -// +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}, -} diff --git a/examples/go-dashboard/src/ui/go.mod b/examples/go-dashboard/src/ui/go.mod deleted file mode 100644 index 358d49b79..000000000 --- a/examples/go-dashboard/src/ui/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/lnslbrty/nDPId/examples/go-dashboard/ui - -go 1.14 - -require ( - github.com/mum4k/termdash v0.12.3-0.20200901030524-fe3e97353191 - github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect -) diff --git a/examples/go-dashboard/src/ui/ui.go b/examples/go-dashboard/src/ui/ui.go deleted file mode 100644 index 8a42803dd..000000000 --- a/examples/go-dashboard/src/ui/ui.go +++ /dev/null @@ -1,104 +0,0 @@ -package ui - -import ( - "context" - "time" - - "github.com/mum4k/termdash" - "github.com/mum4k/termdash/container" - "github.com/mum4k/termdash/keyboard" - "github.com/mum4k/termdash/linestyle" - "github.com/mum4k/termdash/terminal/termbox" - "github.com/mum4k/termdash/terminal/terminalapi" - "github.com/mum4k/termdash/widgets/text" -) - -const rootID = "root" -const redrawInterval = 250 * time.Millisecond - -type Tui struct { - Term terminalapi.Terminal - Context context.Context - Cancel context.CancelFunc - Container *container.Container - MainTicker *time.Ticker -} - -type Widgets struct { - Menu *text.Text - RawJson *text.Text -} - -func newWidgets(ctx context.Context) (*Widgets, error) { - menu, err := text.New() - if err != nil { - panic(err) - } - - rawJson, err := text.New(text.RollContent(), text.WrapAtWords()) - if err != nil { - panic(err) - } - - return &Widgets{ - Menu: menu, - RawJson: rawJson, - }, nil -} - -func Init() (*Tui, *Widgets) { - var err error - - ui := Tui{} - - ui.Term, err = termbox.New(termbox.ColorMode(terminalapi.ColorMode256)) - if err != nil { - panic(err) - } - - ui.Context, ui.Cancel = context.WithCancel(context.Background()) - - wdgts, err := newWidgets(ui.Context) - if err != nil { - panic(err) - } - - ui.Container, err = container.New(ui.Term, - container.Border(linestyle.None), - container.BorderTitle("[ESC to Quit]"), - container.SplitHorizontal( - container.Top( - container.Border(linestyle.Light), - container.BorderTitle("Go nDPId Dashboard"), - container.PlaceWidget(wdgts.Menu), - ), - container.Bottom( - container.Border(linestyle.Light), - container.BorderTitle("Raw JSON"), - container.PlaceWidget(wdgts.RawJson), - ), - container.SplitFixed(3), - ), - ) - if err != nil { - panic(err) - } - - ui.MainTicker = time.NewTicker(1 * time.Second) - - return &ui, wdgts -} - -func Run(ui *Tui) { - defer ui.Term.Close() - - quitter := func(k *terminalapi.Keyboard) { - if k.Key == keyboard.KeyEsc || k.Key == keyboard.KeyCtrlC { - ui.Cancel() - } - } - - if err := termdash.Run(ui.Context, ui.Term, ui.Container, termdash.KeyboardSubscriber(quitter), termdash.RedrawInterval(redrawInterval)); err != nil { - panic(err) - } -} diff --git a/examples/go-dashboard/src/ui/ui/go.mod b/examples/go-dashboard/src/ui/ui/go.mod deleted file mode 100644 index 358d49b79..000000000 --- a/examples/go-dashboard/src/ui/ui/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/lnslbrty/nDPId/examples/go-dashboard/ui - -go 1.14 - -require ( - github.com/mum4k/termdash v0.12.3-0.20200901030524-fe3e97353191 - github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect -) diff --git a/examples/go-dashboard/src/ui/ui/ui.go b/examples/go-dashboard/src/ui/ui/ui.go deleted file mode 100644 index 8a42803dd..000000000 --- a/examples/go-dashboard/src/ui/ui/ui.go +++ /dev/null @@ -1,104 +0,0 @@ -package ui - -import ( - "context" - "time" - - "github.com/mum4k/termdash" - "github.com/mum4k/termdash/container" - "github.com/mum4k/termdash/keyboard" - "github.com/mum4k/termdash/linestyle" - "github.com/mum4k/termdash/terminal/termbox" - "github.com/mum4k/termdash/terminal/terminalapi" - "github.com/mum4k/termdash/widgets/text" -) - -const rootID = "root" -const redrawInterval = 250 * time.Millisecond - -type Tui struct { - Term terminalapi.Terminal - Context context.Context - Cancel context.CancelFunc - Container *container.Container - MainTicker *time.Ticker -} - -type Widgets struct { - Menu *text.Text - RawJson *text.Text -} - -func newWidgets(ctx context.Context) (*Widgets, error) { - menu, err := text.New() - if err != nil { - panic(err) - } - - rawJson, err := text.New(text.RollContent(), text.WrapAtWords()) - if err != nil { - panic(err) - } - - return &Widgets{ - Menu: menu, - RawJson: rawJson, - }, nil -} - -func Init() (*Tui, *Widgets) { - var err error - - ui := Tui{} - - ui.Term, err = termbox.New(termbox.ColorMode(terminalapi.ColorMode256)) - if err != nil { - panic(err) - } - - ui.Context, ui.Cancel = context.WithCancel(context.Background()) - - wdgts, err := newWidgets(ui.Context) - if err != nil { - panic(err) - } - - ui.Container, err = container.New(ui.Term, - container.Border(linestyle.None), - container.BorderTitle("[ESC to Quit]"), - container.SplitHorizontal( - container.Top( - container.Border(linestyle.Light), - container.BorderTitle("Go nDPId Dashboard"), - container.PlaceWidget(wdgts.Menu), - ), - container.Bottom( - container.Border(linestyle.Light), - container.BorderTitle("Raw JSON"), - container.PlaceWidget(wdgts.RawJson), - ), - container.SplitFixed(3), - ), - ) - if err != nil { - panic(err) - } - - ui.MainTicker = time.NewTicker(1 * time.Second) - - return &ui, wdgts -} - -func Run(ui *Tui) { - defer ui.Term.Close() - - quitter := func(k *terminalapi.Keyboard) { - if k.Key == keyboard.KeyEsc || k.Key == keyboard.KeyCtrlC { - ui.Cancel() - } - } - - if err := termdash.Run(ui.Context, ui.Term, ui.Container, termdash.KeyboardSubscriber(quitter), termdash.RedrawInterval(redrawInterval)); err != nil { - panic(err) - } -} |