diff --git a/LICENSE b/LICENSE index e31fe19..55617da 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License -Copyright (c) 2017 MichaelS11 +Original work Copyright (c) 2014 Takashi Kokubun +Modified work Copyright (c) 2017 MichaelS11 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md new file mode 100644 index 0000000..ead25d0 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# Go Tetris + +Golang Tetris for Linux shell or Windows Command Prompt + + +## Compile + +go get github.com/MichaelS11/tetris + +go install github.com/MichaelS11/tetris + + +## Play + +Then run the binary created, tetris or tetris.exe + + +## Keys + +| Key | Action | +| --- | --- | +| ← | left move | +| z | left rotate | +| x | right rotate | +| → | right move | +| ↓ | soft drop | +| ↑ | hard drop | +| spacebar | hard drop | +| p | pause | +| q | quit | + + +## Features include + +- AI (use i key to toggle) +- Lock delay +- Next piece +- Ghost piece +- Top scores + + +## Screenshots + +![alt text](https://raw.githubusercontent.com/MichaelS11/tetris/master/screenshots/screenshot1.png "Go Tetris") + +![alt text](https://raw.githubusercontent.com/MichaelS11/tetris/master/screenshots/screenshot2.png "Golang Tetris") + +![alt text](https://raw.githubusercontent.com/MichaelS11/tetris/master/screenshots/screenshot3.png "Tetris Top Scores") diff --git a/ai.go b/ai.go new file mode 100644 index 0000000..8453fde --- /dev/null +++ b/ai.go @@ -0,0 +1,270 @@ +package main + +const ( + aiQueueSize = (boardWidth+1)/2 + 6 + aiMoveLength = (boardWidth + 1) / 2 +) + +type Ai struct { + queue [aiQueueSize]rune + index int +} + +func NewAi() *Ai { + ai := Ai{} + for i := 0; i < aiQueueSize; i++ { + ai.queue[i] = 'x' + } + return &ai +} + +func (ai *Ai) ProcessQueue() bool { + // wasd + qe keyboard keys + switch ai.queue[ai.index] { + case 'w': + board.MinoDrop() + case 'a': + board.MinoMoveLeft() + case 'd': + board.MinoMoveRight() + case 'q': + board.MinoRotateLeft() + case 'e': + board.MinoRotateRight() + case 'x': + return false + } + ai.index++ + if ai.index == aiQueueSize { + ai.index = 0 + } + view.RefreshScreen() + return true +} + +func (ai *Ai) GetBestQueue() { + ai.addMovesToQueue(ai.getBestQueue()) +} + +func (ai *Ai) addMovesToQueue(queue []rune) { + insertIndex := ai.index + for _, char := range queue { + ai.queue[insertIndex] = char + insertIndex++ + if insertIndex == aiQueueSize { + insertIndex = 0 + } + } +} + +func (ai *Ai) getBestQueue() []rune { + bestQueue := make([]rune, 0, 0) + bestScore := -9999999 + var slideScore int + bestSlide := 6 + + for move1 := 0; move1 <= boardWidth; move1++ { + for rotate1 := 0; rotate1 < 5; rotate1++ { + for slide1 := 0; slide1 <= 5; slide1++ { + + queue, mino1 := ai.getMovesforMino(rotate1, move1, slide1, nil) + if mino1 == nil { + continue + } + + for move2 := 0; move2 <= boardWidth; move2++ { + for rotate2 := 0; rotate2 < 5; rotate2++ { + for slide2 := 0; slide2 <= 5; slide2++ { + + _, mino2 := ai.getMovesforMino(rotate2, move2, slide2, mino1) + if mino2 == nil { + continue + } + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + score := ai.getScoreFromBoardStats(fullLines, holeDepth, bumpy) + + if slide1 < 3 { + slideScore = slide1 + } else { + slideScore = slide1 - 2 + } + + if score > bestScore || (score == bestScore && slideScore < bestSlide) { + bestScore = score + bestQueue = queue + bestSlide = slideScore + } + + } + } + } + + } + } + } + + return bestQueue +} + +func (ai *Ai) getMovesforMino(rotate int, move int, slide int, mino1 *Mino) ([]rune, *Mino) { + queue := make([]rune, 0, 4) + var mino Mino + if mino1 != nil { + mino = *board.previewMino + } else { + mino = *board.currentMino + } + if rotate < 3 { + for i := 0; i < rotate; i++ { + mino.RotateRight() + queue = append(queue, 'e') + if !mino.ValidLocation(false) || (mino1 != nil && mino1.minoOverlap(&mino)) { + return queue, nil + } + } + } else { + for i := 0; i < rotate-2; i++ { + mino.RotateLeft() + queue = append(queue, 'q') + if !mino.ValidLocation(false) || (mino1 != nil && mino1.minoOverlap(&mino)) { + return queue, nil + } + } + } + if move <= aiMoveLength { + move = aiMoveLength - move + for i := 0; i < move; i++ { + mino.MoveLeft() + queue = append(queue, 'a') + if !mino.ValidLocation(false) || (mino1 != nil && mino1.minoOverlap(&mino)) { + return queue, nil + } + } + } else { + move = move - aiMoveLength + 1 + for i := 0; i < move; i++ { + mino.MoveRight() + queue = append(queue, 'd') + if !mino.ValidLocation(false) || (mino1 != nil && mino1.minoOverlap(&mino)) { + return queue, nil + } + } + } + for mino.ValidLocation(false) && (mino1 == nil || !mino1.minoOverlap(&mino)) { + mino.MoveDown() + } + mino.MoveUp() + queue = append(queue, 'w') + if slide < 3 { + for i := 0; i < slide; i++ { + mino.MoveLeft() + queue = append(queue, 'a') + if !mino.ValidLocation(false) || (mino1 != nil && mino1.minoOverlap(&mino)) { + return queue, nil + } + } + } else { + slide = slide - 2 + for i := 0; i < slide; i++ { + mino.MoveRight() + queue = append(queue, 'd') + if !mino.ValidLocation(false) || (mino1 != nil && mino1.minoOverlap(&mino)) { + return queue, nil + } + } + } + queue = append(queue, 'x') + return queue, &mino +} + +func (mino *Mino) minoOverlap(mino1 *Mino) bool { + minoBlocks := mino.minoRotation[mino.rotation] + for i := 0; i < mino.length; i++ { + for j := 0; j < mino.length; j++ { + if minoBlocks[i][j] != blankColor { + if mino1.isMinoAtLocation(mino.x+i, mino.y+j) { + return true + } + } + } + } + return false +} + +func (board *Board) boardStatsWithMinos(mino1 *Mino, mino2 *Mino) (fullLines int, holeDepth int, bumpy int) { + // fullLines + fullLinesY := make(map[int]bool, 2) + for j := 0; j < boardHeight; j++ { + isFullLine := true + for i := 0; i < boardWidth; i++ { + if board.colors[i][j] == blankColor && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) { + isFullLine = false + break + } + } + if isFullLine { + fullLinesY[j] = true + fullLines++ + } + } + + // holeDepth and bumpy + indexLast := 0 + for i := 0; i < boardWidth; i++ { + index := boardHeight + indexOffset := 0 + for j := 0; j < boardHeight; j++ { + if _, found := fullLinesY[j]; found { + indexOffset++ + } else { + if board.colors[i][j] != blankColor || mino1.isMinoAtLocation(i, j) || mino2.isMinoAtLocation(i, j) { + index = j + break + } + } + } + + if i != 0 { + diffrence := (index + fullLines - indexOffset) - indexLast + if diffrence < 0 { + diffrence = -diffrence + } + bumpy += diffrence + + } + indexLast = index + fullLines - indexOffset + + index++ + for j := index; j < boardHeight; j++ { + if board.colors[i][j] == blankColor && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) { + holeDepth += 3 + j - index + } + } + } + return +} + +func (mino *Mino) isMinoAtLocation(x int, y int) bool { + xIndex := x - mino.x + yIndex := y - mino.y + if xIndex < 0 || xIndex >= mino.length || yIndex < 0 || yIndex >= mino.length { + return false + } + + minoBlocks := mino.minoRotation[mino.rotation] + if minoBlocks[xIndex][yIndex] != blankColor { + return true + } + + return false +} + +func (ai *Ai) getScoreFromBoardStats(fullLines int, holeDepth int, bumpy int) (score int) { + if fullLines == 4 { + score += 16 + } + score -= holeDepth + score -= bumpy + return score +} diff --git a/ai_test.go b/ai_test.go new file mode 100644 index 0000000..9903a3b --- /dev/null +++ b/ai_test.go @@ -0,0 +1,498 @@ +package main + +import ( + "gopkg.in/inconshreveable/log15.v2" + "os" + "testing" +) + +func TestMain(m *testing.M) { + setupForTesting() + retCode := m.Run() + os.Exit(retCode) +} + +func setupForTesting() { + logger = log15.New() + logger.SetHandler(log15.StreamHandler(os.Stdout, log15.LogfmtFormat())) + + engine = NewEngine() +} + +func TestBoardStatsFullLines1(t *testing.T) { + board = NewBoard() + + // minoO + mino1 := &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 0 + mino1.y = 18 + mino1.SetOnBoard() + + // minoI + mino1 = &Mino{ + minoRotation: minoBag[0], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 2 + mino1.y = 18 + + // minoI + mino2 := &Mino{ + minoRotation: minoBag[0], + } + mino2.length = len(mino2.minoRotation[0]) + mino2.x = 6 + mino2.y = 18 + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + expected := 1 + if fullLines != expected { + t.Error("fullLines expected", expected, "got", fullLines) + } + expected = 0 + if holeDepth != expected { + t.Error("holeDepth expected", expected, "got", holeDepth) + } + expected = 1 + if bumpy != expected { + t.Error("bumpy expected", expected, "got", bumpy) + } + + // for debuging + // mino1.SetOnBoard() + // mino2.SetOnBoard() + // board.drawDebugBoard() +} + +func TestBoardStatsFullLines2(t *testing.T) { + board = NewBoard() + + // minoO + mino1 := &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 0 + mino1.y = 18 + mino1.SetOnBoard() + + // minoO + mino1 = &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 2 + mino1.y = 18 + mino1.SetOnBoard() + + // minoO + mino1 = &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 4 + mino1.y = 18 + mino1.SetOnBoard() + + // minoO + mino1 = &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 6 + mino1.y = 18 + mino1.SetOnBoard() + + // minoO + mino1 = &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 8 + mino1.y = 18 + mino1.SetOnBoard() + + // minoO + mino1 = &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 0 + mino1.y = 16 + mino1.SetOnBoard() + + // minoI + mino1 = &Mino{ + minoRotation: minoBag[0], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 2 + mino1.y = 16 + + // minoI + mino2 := &Mino{ + minoRotation: minoBag[0], + } + mino2.length = len(mino2.minoRotation[0]) + mino2.x = 6 + mino2.y = 16 + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + expected := 3 + if fullLines != expected { + t.Error("fullLines expected", expected, "got", fullLines) + } + expected = 0 + if holeDepth != expected { + t.Error("holeDepth expected", expected, "got", holeDepth) + } + expected = 1 + if bumpy != expected { + t.Error("bumpy expected", expected, "got", bumpy) + } + + // for debuging + // mino1.SetOnBoard() + // mino2.SetOnBoard() + // board.drawDebugBoard() +} + +func TestBoardStatsFullLines3(t *testing.T) { + board = NewBoard() + + // minoO + mino1 := &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 0 + mino1.y = 18 + mino1.SetOnBoard() + + // minoI + mino1 = &Mino{ + minoRotation: minoBag[0], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 0 + mino1.y = 16 + mino1.SetOnBoard() + + // minoO + mino1 = &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 4 + mino1.y = 18 + mino1.SetOnBoard() + + // minoI + mino1 = &Mino{ + minoRotation: minoBag[0], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 4 + mino1.y = 16 + mino1.SetOnBoard() + + // minoO + mino1 = &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 8 + mino1.y = 18 + + // minoO + mino2 := &Mino{ + minoRotation: minoBag[3], + } + mino2.length = len(mino2.minoRotation[0]) + mino2.x = 8 + mino2.y = 16 + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + expected := 1 + if fullLines != expected { + t.Error("fullLines expected", expected, "got", fullLines) + } + expected = 0 + if holeDepth != expected { + t.Error("holeDepth expected", expected, "got", holeDepth) + } + expected = 9 + if bumpy != expected { + t.Error("bumpy expected", expected, "got", bumpy) + } + + // for debuging + // mino1.SetOnBoard() + // mino2.SetOnBoard() + // board.drawDebugBoard() +} + +func TestBoardStatsBumpy1(t *testing.T) { + board = NewBoard() + + // minoO + mino1 := &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 0 + mino1.y = 18 + + // minoO + mino2 := &Mino{ + minoRotation: minoBag[3], + } + mino2.length = len(mino2.minoRotation[0]) + mino2.x = 0 + mino2.y = 16 + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + expected := 0 + if fullLines != expected { + t.Error("fullLines expected", expected, "got", fullLines) + } + expected = 0 + if holeDepth != expected { + t.Error("holeDepth expected", expected, "got", holeDepth) + } + expected = 4 + if bumpy != expected { + t.Error("bumpy expected", expected, "got", bumpy) + } + + // for debuging + // mino1.SetOnBoard() + // mino2.SetOnBoard() + // board.drawDebugBoard() +} + +func TestBoardStatsBumpy2(t *testing.T) { + board = NewBoard() + + // minoO + mino1 := &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 0 + mino1.y = 18 + mino1.SetOnBoard() + + // minoI + mino1 = &Mino{ + minoRotation: minoBag[0], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 0 + mino1.y = 16 + + // minoI + mino2 := &Mino{ + minoRotation: minoBag[0], + } + mino2.length = len(mino2.minoRotation[0]) + mino2.x = 0 + mino2.y = 15 + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + expected := 0 + if fullLines != expected { + t.Error("fullLines expected", expected, "got", fullLines) + } + // 9 + 9 = 18 + expected = 18 + if holeDepth != expected { + t.Error("holeDepth expected", expected, "got", holeDepth) + } + expected = 4 + if bumpy != expected { + t.Error("bumpy expected", expected, "got", bumpy) + } + + // for debuging + // mino1.SetOnBoard() + // mino2.SetOnBoard() + // board.drawDebugBoard() +} + +func TestBoardStatsBumpy3(t *testing.T) { + board = NewBoard() + + // minoO + mino1 := &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 0 + mino1.y = 18 + mino1.SetOnBoard() + + // minoO + mino1 = &Mino{ + minoRotation: minoBag[3], + } + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 1 + mino1.y = 16 + + // minoO + mino2 := &Mino{ + minoRotation: minoBag[3], + } + mino2.length = len(mino2.minoRotation[0]) + mino2.x = 2 + mino2.y = 14 + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + expected := 0 + if fullLines != expected { + t.Error("fullLines expected", expected, "got", fullLines) + } + // 6 + 7 + 4 + 5 + 6 + 7 = 35 + expected = 35 + if holeDepth != expected { + t.Error("holeDepth expected", expected, "got", holeDepth) + } + expected = 10 + if bumpy != expected { + t.Error("bumpy expected", expected, "got", bumpy) + } + + // for debuging + // mino1.SetOnBoard() + // mino2.SetOnBoard() + // board.drawDebugBoard() +} + +func TestBoardStatsBumpy4(t *testing.T) { + board = NewBoard() + + // minoI + mino1 := &Mino{ + minoRotation: minoBag[0], + } + mino1.rotation = 1 + mino1.length = len(mino1.minoRotation[0]) + mino1.x = 6 + mino1.y = 16 + + // minoI + mino2 := &Mino{ + minoRotation: minoBag[0], + } + mino2.rotation = 1 + mino2.length = len(mino2.minoRotation[0]) + mino2.x = 6 + mino2.y = 12 + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + expected := 0 + if fullLines != expected { + t.Error("fullLines expected", expected, "got", fullLines) + } + expected = 0 + if holeDepth != expected { + t.Error("holeDepth expected", expected, "got", holeDepth) + } + expected = 16 + if bumpy != expected { + t.Error("bumpy expected", expected, "got", bumpy) + } + + // for debuging + // mino1.SetOnBoard() + // mino2.SetOnBoard() + // board.drawDebugBoard() +} + +func TestBoardStatsHoleDepth1(t *testing.T) { + board = NewBoard() + + // minoJ + mino1 := &Mino{ + minoRotation: minoBag[1], + } + mino1.rotation = 1 + mino1.length = len(mino1.minoRotation[0]) + mino1.x = -1 + mino1.y = 17 + + // minoJ + mino2 := &Mino{ + minoRotation: minoBag[1], + } + mino2.rotation = 1 + mino2.length = len(mino2.minoRotation[0]) + mino2.x = 1 + mino2.y = 17 + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + expected := 0 + if fullLines != expected { + t.Error("fullLines expected", expected, "got", fullLines) + } + // 3 + 4 + 3 + 4 = 14 + expected = 14 + if holeDepth != expected { + t.Error("holeDepth expected", expected, "got", holeDepth) + } + expected = 3 + if bumpy != expected { + t.Error("bumpy expected", expected, "got", bumpy) + } + + // for debuging + // mino1.SetOnBoard() + // mino2.SetOnBoard() + // board.drawDebugBoard() +} + +func TestBoardStatsHoleDepth2(t *testing.T) { + board = NewBoard() + + // minoJ + mino1 := &Mino{ + minoRotation: minoBag[1], + } + mino1.rotation = 1 + mino1.length = len(mino1.minoRotation[0]) + mino1.x = -1 + mino1.y = 17 + + // minoJ + mino2 := &Mino{ + minoRotation: minoBag[1], + } + mino2.rotation = 1 + mino2.length = len(mino2.minoRotation[0]) + mino2.x = -1 + mino2.y = 14 + + fullLines, holeDepth, bumpy := board.boardStatsWithMinos(mino1, mino2) + expected := 0 + if fullLines != expected { + t.Error("fullLines expected", expected, "got", fullLines) + } + // 3 + 4 + 6 + 7 = 20 + expected = 20 + if holeDepth != expected { + t.Error("holeDepth expected", expected, "got", holeDepth) + } + expected = 6 + if bumpy != expected { + t.Error("bumpy expected", expected, "got", bumpy) + } + + // for debuging + // mino1.SetOnBoard() + // mino2.SetOnBoard() + // board.drawDebugBoard() +} diff --git a/board.go b/board.go new file mode 100644 index 0000000..cea9bb5 --- /dev/null +++ b/board.go @@ -0,0 +1,300 @@ +package main + +import ( + "fmt" + "github.com/nsf/termbox-go" + "time" +) + +type Board struct { + colors [boardWidth][boardHeight]termbox.Attribute + rotation [boardWidth][boardHeight]int + previewMino *Mino + currentMino *Mino + dropDistance int +} + +func NewBoard() *Board { + board := &Board{} + for i := 0; i < boardWidth; i++ { + for j := 0; j < boardHeight; j++ { + board.colors[i][j] = blankColor + } + } + for i := 0; i < boardWidth; i++ { + for j := 0; j < boardHeight; j++ { + board.rotation[i][j] = 0 + } + } + board.previewMino = NewMino() + board.currentMino = NewMino() + return board +} + +func (board *Board) MinoMoveLeft() { + board.dropDistance = 0 + mino := board.currentMino.CloneMoveLeft() + if mino.ValidLocation(false) { + board.currentMino = mino + board.StartLockDelayIfBottom() + } +} + +func (board *Board) MinoMoveRight() { + board.dropDistance = 0 + mino := board.currentMino.CloneMoveRight() + if mino.ValidLocation(false) { + board.currentMino = mino + board.StartLockDelayIfBottom() + } +} + +func (board *Board) MinoRotateRight() { + board.dropDistance = 0 + mino := board.currentMino.CloneRotateRight() + if mino.ValidLocation(false) { + board.currentMino = mino + board.StartLockDelayIfBottom() + return + } + mino.MoveLeft() + if mino.ValidLocation(false) { + board.currentMino = mino + board.StartLockDelayIfBottom() + return + } + mino.MoveRight() + mino.MoveRight() + if mino.ValidLocation(false) { + board.currentMino = mino + board.StartLockDelayIfBottom() + return + } +} + +func (board *Board) MinoRotateLeft() { + board.dropDistance = 0 + mino := board.currentMino.CloneRotateLeft() + if mino.ValidLocation(false) { + board.currentMino = mino + board.StartLockDelayIfBottom() + return + } + mino.MoveLeft() + if mino.ValidLocation(false) { + board.currentMino = mino + board.StartLockDelayIfBottom() + return + } + mino.MoveRight() + mino.MoveRight() + if mino.ValidLocation(false) { + board.currentMino = mino + board.StartLockDelayIfBottom() + return + } +} + +func (board *Board) MinoMoveDown() { + mino := board.currentMino.CloneMoveDown() + if mino.ValidLocation(false) { + board.dropDistance = 0 + board.currentMino = mino + if !board.StartLockDelayIfBottom() { + engine.ResetTimer(0) + } + return + } + if !board.currentMino.ValidLocation(true) { + engine.GameOver() + return + } + board.nextMino() +} + +func (board *Board) MinoDrop() { + board.dropDistance = 0 + mino := board.currentMino.CloneMoveDown() + for mino.ValidLocation(false) { + board.dropDistance++ + mino.MoveDown() + } + for i := 0; i < board.dropDistance; i++ { + board.currentMino.MoveDown() + } + if !board.currentMino.ValidLocation(true) { + engine.GameOver() + return + } + if board.dropDistance < 1 { + return + } + if !board.StartLockDelayIfBottom() { + engine.ResetTimer(0) + } +} + +func (board *Board) StartLockDelayIfBottom() bool { + mino := board.currentMino.CloneMoveDown() + if mino.ValidLocation(false) { + return false + } + engine.ResetTimer(300 * time.Millisecond) + return true +} + +func (board *Board) nextMino() { + engine.AddScore(board.dropDistance) + + board.currentMino.SetOnBoard() + + board.deleteCheck() + + if !board.previewMino.ValidLocation(false) { + board.previewMino.MoveUp() + if !board.previewMino.ValidLocation(false) { + engine.GameOver() + return + } + } + + board.currentMino = board.previewMino + board.previewMino = NewMino() + engine.ResetAiTimer() + engine.ResetTimer(0) +} + +func (board *Board) deleteCheck() { + lines := board.fullLines() + if len(lines) < 1 { + return + } + + view.ShowDeleteAnimation(lines) + for _, line := range lines { + board.deleteLine(line) + } + + engine.AddDeleteLines(len(lines)) +} + +func (board *Board) fullLines() []int { + fullLines := make([]int, 0, 1) + for j := 0; j < boardHeight; j++ { + if board.isFullLine(j) { + fullLines = append(fullLines, j) + } + } + return fullLines +} + +func (board *Board) isFullLine(j int) bool { + for i := 0; i < boardWidth; i++ { + if board.colors[i][j] == blankColor { + return false + } + } + return true +} + +func (board *Board) deleteLine(line int) { + for i := 0; i < boardWidth; i++ { + board.colors[i][line] = blankColor + } + for j := line; j > 0; j-- { + for i := 0; i < boardWidth; i++ { + board.colors[i][j] = board.colors[i][j-1] + board.rotation[i][j] = board.rotation[i][j-1] + } + } + for i := 0; i < boardWidth; i++ { + board.colors[i][0] = blankColor + } +} + +func (board *Board) SetColor(x int, y int, color termbox.Attribute, rotation int) { + board.colors[x][y] = color + board.rotation[x][y] = rotation +} + +func ValidBlockLocation(x int, y int, mustBeOnBoard bool) bool { + if x < 0 || x >= boardWidth || y >= boardHeight { + return false + } + if mustBeOnBoard { + if y < 0 { + return false + } + } else { + if y < -2 { + return false + } + } + if y > -1 { + if board.colors[x][y] != blankColor { + return false + } + } + return true +} + +func ValidDisplayLocation(x int, y int) bool { + return x >= 0 && x < boardWidth && y >= 0 && y < boardHeight +} + +func (board *Board) DrawBoard() { + for i := 0; i < boardWidth; i++ { + for j := 0; j < boardHeight; j++ { + if board.colors[i][j] != blankColor { + view.DrawBlock(i, j, board.colors[i][j], board.rotation[i][j]) + } + } + } +} + +func (board *Board) DrawPreviewMino() { + board.previewMino.DrawMino(MinoPreview) +} + +func (board *Board) DrawCurrentMino() { + board.currentMino.DrawMino(MinoCurrent) +} + +func (board *Board) DrawDropMino() { + mino := board.currentMino.CloneMoveDown() + if !mino.ValidLocation(false) { + return + } + for mino.ValidLocation(false) { + mino.MoveDown() + } + mino.MoveUp() + mino.DrawMino(MinoDrop) +} + +// for debuging +func (board *Board) drawDebugBoard() { + for j := 0; j < boardHeight; j++ { + for i := 0; i < boardWidth; i++ { + switch board.colors[i][j] { + case blankColor: + fmt.Print(".") + case termbox.ColorBlue: + fmt.Print("B") + case termbox.ColorCyan: + fmt.Print("C") + case termbox.ColorGreen: + fmt.Print("G") + case termbox.ColorMagenta: + fmt.Print("M") + case termbox.ColorRed: + fmt.Print("R") + case termbox.ColorWhite: + fmt.Print("W") + case termbox.ColorYellow: + fmt.Print("Y") + } + } + fmt.Println("") + } +} diff --git a/engine.go b/engine.go new file mode 100644 index 0000000..0a3711f --- /dev/null +++ b/engine.go @@ -0,0 +1,259 @@ +package main + +import ( + "github.com/nsf/termbox-go" + "time" +) + +type Engine struct { + stopped bool + chanStop chan struct{} + keyInput *KeyInput + ranking *Ranking + timer *time.Timer + tickTime time.Duration + paused bool + gameOver bool + score int + level int + deleteLines int + ai *Ai + aiEnabled bool + aiTimer *time.Timer +} + +func NewEngine() *Engine { + return &Engine{ + chanStop: make(chan struct{}, 1), + gameOver: true, + tickTime: time.Hour, + ai: NewAi(), + } +} + +func (engine *Engine) Run() { + logger.Info("Engine Run start") + + var event *termbox.Event + + engine.timer = time.NewTimer(engine.tickTime) + engine.timer.Stop() + engine.aiTimer = time.NewTimer(engine.tickTime) + engine.aiTimer.Stop() + + engine.ranking = NewRanking() + board = NewBoard() + view.RefreshScreen() + + engine.keyInput = NewKeyInput() + go engine.keyInput.Run() + +loop: + for { + select { + case <-engine.chanStop: + break loop + default: + select { + case event = <-engine.keyInput.chanKeyInput: + engine.keyInput.ProcessEvent(event) + view.RefreshScreen() + case <-engine.timer.C: + engine.tick() + case <-engine.aiTimer.C: + if engine.ai.ProcessQueue() { + engine.aiTimer.Reset(engine.tickTime / 4) + } + case <-engine.chanStop: + break loop + } + } + } + + logger.Info("Engine Run end") +} + +func (engine *Engine) Stop() { + logger.Info("Engine Stop start") + + if !engine.stopped { + engine.stopped = true + close(engine.chanStop) + } + engine.timer.Stop() + engine.aiTimer.Stop() + + logger.Info("Engine Stop end") +} + +func (engine *Engine) Pause() { + if !engine.timer.Stop() { + select { + case <-engine.timer.C: + default: + } + } + if !engine.aiTimer.Stop() { + select { + case <-engine.aiTimer.C: + default: + } + } + engine.paused = true +} + +func (engine *Engine) UnPause() { + engine.timer.Reset(engine.tickTime) + if engine.aiEnabled { + engine.aiTimer.Reset(engine.tickTime / 4) + } + engine.paused = false +} + +func (engine *Engine) NewGame() { + logger.Info("Engine NewGame start") + + board = NewBoard() + engine.tickTime = 480 * time.Millisecond + engine.score = 0 + engine.level = 1 + engine.deleteLines = 0 + +loop: + for { + select { + case <-engine.keyInput.chanKeyInput: + default: + break loop + } + } + + engine.gameOver = false + if engine.aiEnabled { + engine.ai.GetBestQueue() + } + engine.UnPause() + + logger.Info("Engine NewGame end") +} + +func (engine *Engine) ResetTimer(duration time.Duration) { + if !engine.timer.Stop() { + select { + case <-engine.timer.C: + default: + } + } + if duration == 0 { + // duration 0 means tick time + engine.timer.Reset(engine.tickTime) + } else { + // duration is lock delay + engine.timer.Reset(duration) + } +} + +func (engine *Engine) ResetAiTimer() { + if !engine.aiEnabled { + return + } + engine.ai.GetBestQueue() + engine.aiTimer.Reset(engine.tickTime / 4) +} + +func (engine *Engine) tick() { + board.MinoMoveDown() + view.RefreshScreen() +} + +func (engine *Engine) AddDeleteLines(lines int) { + engine.deleteLines += lines + if engine.deleteLines > 999999 { + engine.deleteLines = 999999 + } + + switch lines { + case 1: + engine.AddScore(40 * (engine.level + 1)) + case 2: + engine.AddScore(100 * (engine.level + 1)) + case 3: + engine.AddScore(300 * (engine.level + 1)) + case 4: + engine.AddScore(1200 * (engine.level + 1)) + } + + if engine.level < engine.deleteLines/10 { + engine.LevelUp() + } +} + +func (engine *Engine) AddScore(add int) { + engine.score += add + if engine.score > 999999 { + engine.score = 999999 + } +} + +func (engine *Engine) LevelUp() { + if engine.level >= 30 { + return + } + + engine.level++ + switch { + case engine.level > 29: + engine.tickTime = 10 * time.Millisecond + case engine.level > 25: + engine.tickTime = 20 * time.Millisecond + case engine.level > 19: + // 50 to 30 + engine.tickTime = time.Duration(10*(15-engine.level/2)) * time.Millisecond + case engine.level > 9: + // 150 to 60 + engine.tickTime = time.Duration(10*(25-engine.level)) * time.Millisecond + default: + // 480 to 160 + engine.tickTime = time.Duration(10*(52-4*engine.level)) * time.Millisecond + } +} + +func (engine *Engine) GameOver() { + logger.Info("Engine GameOver start") + + engine.Pause() + + view.ShowGameOverAnimation() + + engine.gameOver = true + + engine.ranking.InsertScore(uint64(engine.score)) + engine.ranking.Save() + +loop: + for { + select { + case <-engine.keyInput.chanKeyInput: + default: + break loop + } + } + + logger.Info("Engine GameOver end") +} + +func (engine *Engine) EnabledAi() { + engine.aiEnabled = true + engine.ai.GetBestQueue() + engine.aiTimer.Reset(engine.tickTime / 4) +} + +func (engine *Engine) DisableAi() { + if !engine.aiTimer.Stop() { + select { + case <-engine.aiTimer.C: + default: + } + } + engine.aiEnabled = false +} diff --git a/key_input.go b/key_input.go new file mode 100644 index 0000000..821bb2b --- /dev/null +++ b/key_input.go @@ -0,0 +1,117 @@ +package main + +import ( + "github.com/nsf/termbox-go" + "runtime" +) + +type KeyInput struct { + stopped bool + chanStop chan struct{} + chanKeyInput chan *termbox.Event +} + +func NewKeyInput() *KeyInput { + return &KeyInput{ + chanStop: make(chan struct{}, 1), + chanKeyInput: make(chan *termbox.Event, 8), + } +} + +func (keyInput *KeyInput) Run() { + logger.Info("KeyInput Run start") + +loop: + for { + select { + case <-keyInput.chanStop: + break loop + default: + } + event := termbox.PollEvent() + if event.Type == termbox.EventKey { + select { + case <-keyInput.chanStop: + break loop + default: + select { + case keyInput.chanKeyInput <- &event: + case <-keyInput.chanStop: + break loop + } + } + } + } + + logger.Info("KeyInput Run end") +} + +func (keyInput *KeyInput) ProcessEvent(event *termbox.Event) { + + if event.Key == termbox.KeyCtrlI { + // ctrl i to log stack trace + buffer := make([]byte, 1<<16) + length := runtime.Stack(buffer, true) + logger.Debug("Stack trace", "buffer", string(buffer[:length])) + return + } + + if event.Ch == 'q' || event.Key == termbox.KeyCtrlC { + if !keyInput.stopped { + keyInput.stopped = true + close(keyInput.chanStop) + } + engine.Stop() + return + } + + if engine.gameOver { + if event.Key == termbox.KeySpace { + engine.NewGame() + } + return + } + + if engine.paused { + if event.Ch == 'p' { + engine.UnPause() + } + return + } + + if event.Ch != 0 { + switch event.Ch { + case 'p': + engine.Pause() + case 'i': + engine.EnabledAi() + } + } + + if engine.aiEnabled { + return + } + + if event.Ch == 0 { + switch event.Key { + case termbox.KeySpace: + board.MinoDrop() + case termbox.KeyArrowUp: + board.MinoDrop() + case termbox.KeyArrowDown: + board.MinoMoveDown() + case termbox.KeyArrowLeft: + board.MinoMoveLeft() + case termbox.KeyArrowRight: + board.MinoMoveRight() + } + } else { + switch event.Ch { + case 'z': + board.MinoRotateLeft() + case 'x': + board.MinoRotateRight() + } + } + +} diff --git a/mino.go b/mino.go new file mode 100644 index 0000000..e6cd68c --- /dev/null +++ b/mino.go @@ -0,0 +1,154 @@ +package main + +import ( + "math/rand" + "time" +) + +type MinoType int + +const ( + MinoPreview MinoType = iota + MinoCurrent = iota + MinoDrop = iota +) + +var ( + bagRand []int + bagIndex int +) + +func init() { + rand.Seed(time.Now().UnixNano()) + bagRand = rand.Perm(7) +} + +type Mino struct { + x int + y int + length int + rotation int + minoRotation MinoRotation +} + +func NewMino() *Mino { + minoRotation := minoBag[bagRand[bagIndex]] + bagIndex++ + if bagIndex > 6 { + bagIndex = 0 + bagRand = rand.Perm(7) + } + mino := &Mino{ + minoRotation: minoRotation, + length: len(minoRotation[0]), + } + mino.x = boardWidth/2 - (mino.length+1)/2 + mino.y = -1 + return mino +} + +func (mino *Mino) CloneMoveLeft() *Mino { + newMino := *mino + newMino.MoveLeft() + return &newMino +} + +func (mino *Mino) MoveLeft() { + mino.x-- +} + +func (mino *Mino) CloneMoveRight() *Mino { + newMino := *mino + newMino.MoveRight() + return &newMino +} + +func (mino *Mino) MoveRight() { + mino.x++ +} + +func (mino *Mino) CloneRotateRight() *Mino { + newMino := *mino + newMino.RotateRight() + return &newMino +} + +func (mino *Mino) RotateRight() { + mino.rotation++ + if mino.rotation > 3 { + mino.rotation = 0 + } +} + +func (mino *Mino) CloneRotateLeft() *Mino { + newMino := *mino + newMino.RotateLeft() + return &newMino +} + +func (mino *Mino) RotateLeft() { + if mino.rotation < 1 { + mino.rotation = 3 + return + } + mino.rotation-- +} + +func (mino *Mino) CloneMoveDown() *Mino { + newMino := *mino + newMino.MoveDown() + return &newMino +} + +func (mino *Mino) MoveDown() { + mino.y++ +} + +func (mino *Mino) MoveUp() { + mino.y-- +} + +func (mino *Mino) ValidLocation(mustBeOnBoard bool) bool { + minoBlocks := mino.minoRotation[mino.rotation] + for i := 0; i < mino.length; i++ { + for j := 0; j < mino.length; j++ { + if minoBlocks[i][j] != blankColor { + if !ValidBlockLocation(mino.x+i, mino.y+j, mustBeOnBoard) { + return false + } + } + } + } + return true +} + +func (mino *Mino) SetOnBoard() { + minoBlocks := mino.minoRotation[mino.rotation] + for i := 0; i < mino.length; i++ { + for j := 0; j < mino.length; j++ { + if minoBlocks[i][j] != blankColor { + board.SetColor(mino.x+i, mino.y+j, minoBlocks[i][j], mino.rotation) + } + } + } +} + +func (mino *Mino) DrawMino(minoType MinoType) { + minoBlocks := mino.minoRotation[mino.rotation] + for i := 0; i < mino.length; i++ { + for j := 0; j < mino.length; j++ { + if minoBlocks[i][j] != blankColor { + switch minoType { + case MinoPreview: + view.DrawPreviewMinoBlock(i, j, minoBlocks[i][j], mino.rotation, mino.length) + case MinoDrop: + view.DrawBlock(mino.x+i, mino.y+j, blankColor, mino.rotation) + case MinoCurrent: + if ValidDisplayLocation(mino.x+i, mino.y+j) { + view.DrawBlock(mino.x+i, mino.y+j, minoBlocks[i][j], mino.rotation) + } + } + } + } + } +} diff --git a/minos.go b/minos.go new file mode 100644 index 0000000..2bafd4c --- /dev/null +++ b/minos.go @@ -0,0 +1,103 @@ +package main + +import ( + "github.com/nsf/termbox-go" +) + +type MinoBlocks [][]termbox.Attribute + +type MinoRotation [4]MinoBlocks + +var minoBag [7]MinoRotation + +func init() { + minoI := MinoBlocks{ + []termbox.Attribute{blankColor, termbox.ColorCyan, blankColor, blankColor}, + []termbox.Attribute{blankColor, termbox.ColorCyan, blankColor, blankColor}, + []termbox.Attribute{blankColor, termbox.ColorCyan, blankColor, blankColor}, + []termbox.Attribute{blankColor, termbox.ColorCyan, blankColor, blankColor}, + } + minoJ := MinoBlocks{ + []termbox.Attribute{termbox.ColorBlue, termbox.ColorBlue, blankColor}, + []termbox.Attribute{blankColor, termbox.ColorBlue, blankColor}, + []termbox.Attribute{blankColor, termbox.ColorBlue, blankColor}, + } + minoL := MinoBlocks{ + []termbox.Attribute{blankColor, termbox.ColorWhite, blankColor}, + []termbox.Attribute{blankColor, termbox.ColorWhite, blankColor}, + []termbox.Attribute{termbox.ColorWhite, termbox.ColorWhite, blankColor}, + } + minoO := MinoBlocks{ + []termbox.Attribute{termbox.ColorYellow, termbox.ColorYellow}, + []termbox.Attribute{termbox.ColorYellow, termbox.ColorYellow}, + } + minoS := MinoBlocks{ + []termbox.Attribute{blankColor, termbox.ColorGreen, blankColor}, + []termbox.Attribute{termbox.ColorGreen, termbox.ColorGreen, blankColor}, + []termbox.Attribute{termbox.ColorGreen, blankColor, blankColor}, + } + minoT := MinoBlocks{ + []termbox.Attribute{blankColor, termbox.ColorMagenta, blankColor}, + []termbox.Attribute{termbox.ColorMagenta, termbox.ColorMagenta, blankColor}, + []termbox.Attribute{blankColor, termbox.ColorMagenta, blankColor}, + } + minoZ := MinoBlocks{ + []termbox.Attribute{termbox.ColorRed, blankColor, blankColor}, + []termbox.Attribute{termbox.ColorRed, termbox.ColorRed, blankColor}, + []termbox.Attribute{blankColor, termbox.ColorRed, blankColor}, + } + + var minoRotationI MinoRotation + minoRotationI[0] = minoI + for i := 1; i < 4; i++ { + minoRotationI[i] = initCloneRotateRight(minoRotationI[i-1]) + } + var minoRotationJ MinoRotation + minoRotationJ[0] = minoJ + for i := 1; i < 4; i++ { + minoRotationJ[i] = initCloneRotateRight(minoRotationJ[i-1]) + } + var minoRotationL MinoRotation + minoRotationL[0] = minoL + for i := 1; i < 4; i++ { + minoRotationL[i] = initCloneRotateRight(minoRotationL[i-1]) + } + var minoRotationO MinoRotation + minoRotationO[0] = minoO + minoRotationO[1] = minoO + minoRotationO[2] = minoO + minoRotationO[3] = minoO + var minoRotationS MinoRotation + minoRotationS[0] = minoS + for i := 1; i < 4; i++ { + minoRotationS[i] = initCloneRotateRight(minoRotationS[i-1]) + } + var minoRotationT MinoRotation + minoRotationT[0] = minoT + for i := 1; i < 4; i++ { + minoRotationT[i] = initCloneRotateRight(minoRotationT[i-1]) + } + var minoRotationZ MinoRotation + minoRotationZ[0] = minoZ + for i := 1; i < 4; i++ { + minoRotationZ[i] = initCloneRotateRight(minoRotationZ[i-1]) + } + + minoBag = [7]MinoRotation{minoRotationI, minoRotationJ, minoRotationL, minoRotationO, minoRotationS, minoRotationT, minoRotationZ} +} + +func initCloneRotateRight(minoBlocks MinoBlocks) MinoBlocks { + length := len(minoBlocks) + newMinoBlocks := make(MinoBlocks, length, length) + for i := 0; i < length; i++ { + newMinoBlocks[i] = make([]termbox.Attribute, length, length) + } + + for i := 0; i < length; i++ { + for j := 0; j < length; j++ { + newMinoBlocks[length-j-1][i] = minoBlocks[i][j] + } + } + + return newMinoBlocks +} diff --git a/ranking.go b/ranking.go new file mode 100644 index 0000000..4500607 --- /dev/null +++ b/ranking.go @@ -0,0 +1,76 @@ +package main + +import ( + "bytes" + "io/ioutil" + "os" + "strconv" + "strings" +) + +const ( + rankingFileName = "/tetris.db" +) + +type Ranking struct { + scores []uint64 +} + +func NewRanking() *Ranking { + ranking := &Ranking{ + scores: make([]uint64, 10), + } + + if _, err := os.Stat(baseDir + rankingFileName); os.IsNotExist(err) { + for i := 0; i < 10; i++ { + ranking.scores[i] = 0 + } + return ranking + } + + scoreBytes, err := ioutil.ReadFile(baseDir + rankingFileName) + if err != nil { + logger.Error("NewRanking ReadFile", "error", err.Error()) + } + + scoreStrings := strings.Split(string(scoreBytes), ",") + for index, scoreString := range scoreStrings { + score, err := strconv.ParseUint(scoreString, 10, 64) + if err != nil { + logger.Error("NewRanking ParseUint", "error", err.Error()) + score = 0 + } + ranking.scores[index] = score + } + + return ranking +} + +func (ranking *Ranking) Save() { + var buffer bytes.Buffer + + for i := 0; i < 10; i++ { + if i != 0 { + buffer.WriteRune(',') + } + buffer.WriteString(strconv.FormatUint(ranking.scores[i], 10)) + } + + ioutil.WriteFile(baseDir+rankingFileName, buffer.Bytes(), 0644) +} + +func (ranking *Ranking) InsertScore(newScore uint64) { + for index, score := range ranking.scores { + if newScore > score { + ranking.slideScores(index) + ranking.scores[index] = newScore + return + } + } +} + +func (ranking *Ranking) slideScores(index int) { + for i := len(ranking.scores) - 1; i > index; i-- { + ranking.scores[i] = ranking.scores[i-1] + } +} diff --git a/screenshots/screenshot1.png b/screenshots/screenshot1.png new file mode 100644 index 0000000..daddf01 Binary files /dev/null and b/screenshots/screenshot1.png differ diff --git a/screenshots/screenshot2.png b/screenshots/screenshot2.png new file mode 100644 index 0000000..6356c2f Binary files /dev/null and b/screenshots/screenshot2.png differ diff --git a/screenshots/screenshot3.png b/screenshots/screenshot3.png new file mode 100644 index 0000000..dd423a9 Binary files /dev/null and b/screenshots/screenshot3.png differ diff --git a/tetris.go b/tetris.go new file mode 100644 index 0000000..c3909fa --- /dev/null +++ b/tetris.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/nsf/termbox-go" + "gopkg.in/inconshreveable/log15.v2" + "os" + "path/filepath" +) + +const ( + boardWidth = 10 + boardHeight = 20 + blankColor = termbox.ColorBlack +) + +var ( + baseDir string + logger log15.Logger + view *View + engine *Engine + board *Board +) + +func main() { + + baseDir, _ = filepath.Abs(filepath.Dir(os.Args[0])) + logger = log15.New() + if baseDir != "" { + logger.SetHandler(log15.Must.FileHandler(baseDir+"/tetris.log", log15.LogfmtFormat())) + } + + view = NewView() + engine = NewEngine() + + engine.Run() + + view.Stop() + +} diff --git a/vendor/github.com/mattn/go-colorable/LICENSE b/vendor/github.com/mattn/go-colorable/LICENSE new file mode 100644 index 0000000..91b5cef --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-colorable/README.md b/vendor/github.com/mattn/go-colorable/README.md new file mode 100644 index 0000000..e84226a --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/README.md @@ -0,0 +1,43 @@ +# go-colorable + +Colorable writer for windows. + +For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) +This package is possible to handle escape sequence for ansi color on windows. + +## Too Bad! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) + + +## So Good! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) + +## Usage + +```go +logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) +logrus.SetOutput(colorable.NewColorableStdout()) + +logrus.Info("succeeded") +logrus.Warn("not correct") +logrus.Error("something error") +logrus.Fatal("panic") +``` + +You can compile above code on non-windows OSs. + +## Installation + +``` +$ go get github.com/mattn/go-colorable +``` + +# License + +MIT + +# Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-colorable/_example/main.go b/vendor/github.com/mattn/go-colorable/_example/main.go new file mode 100644 index 0000000..0da0906 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/_example/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/Sirupsen/logrus" + "github.com/mattn/go-colorable" +) + +func main() { + logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) + logrus.SetOutput(colorable.NewColorableStdout()) + + logrus.Info("succeeded") + logrus.Warn("not correct") + logrus.Error("something error") + logrus.Fatal("panic") +} diff --git a/vendor/github.com/mattn/go-colorable/_example2/main.go b/vendor/github.com/mattn/go-colorable/_example2/main.go new file mode 100644 index 0000000..8cbcb90 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/_example2/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "bufio" + "fmt" + + "github.com/mattn/go-colorable" +) + +func main() { + stdOut := bufio.NewWriter(colorable.NewColorableStdout()) + + fmt.Fprint(stdOut, "\x1B[3GMove to 3rd Column\n") + fmt.Fprint(stdOut, "\x1B[1;2HMove to 2nd Column on 1st Line\n") + stdOut.Flush() +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/mattn/go-colorable/colorable_others.go new file mode 100644 index 0000000..52d6653 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_others.go @@ -0,0 +1,24 @@ +// +build !windows + +package colorable + +import ( + "io" + "os" +) + +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + return file +} + +func NewColorableStdout() io.Writer { + return os.Stdout +} + +func NewColorableStderr() io.Writer { + return os.Stderr +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_test.go b/vendor/github.com/mattn/go-colorable/colorable_test.go new file mode 100644 index 0000000..49a4ffa --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_test.go @@ -0,0 +1,30 @@ +package colorable + +import ( + "bytes" + "testing" +) + +// checkEncoding checks that colorable is output encoding agnostic as long as +// the encoding is a superset of ASCII. This implies that one byte not part of +// an ANSI sequence must give exactly one byte in output +func checkEncoding(t *testing.T, data []byte) { + // Send non-UTF8 data to colorable + b := bytes.NewBuffer(make([]byte, 0, 10)) + if b.Len() != 0 { + t.FailNow() + } + // TODO move colorable wrapping outside the test + c := NewNonColorable(b) + c.Write(data) + if b.Len() != len(data) { + t.Fatalf("%d bytes expected, got %d", len(data), b.Len()) + } +} + +func TestEncoding(t *testing.T) { + checkEncoding(t, []byte{}) // Empty + checkEncoding(t, []byte(`abc`)) // "abc" + checkEncoding(t, []byte(`é`)) // "é" in UTF-8 + checkEncoding(t, []byte{233}) // 'é' in Latin-1 +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go new file mode 100644 index 0000000..bc84adf --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -0,0 +1,810 @@ +package colorable + +import ( + "bytes" + "io" + "math" + "os" + "strconv" + "strings" + "syscall" + "unsafe" + + "github.com/mattn/go-isatty" +) + +const ( + foregroundBlue = 0x1 + foregroundGreen = 0x2 + foregroundRed = 0x4 + foregroundIntensity = 0x8 + foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) + backgroundBlue = 0x10 + backgroundGreen = 0x20 + backgroundRed = 0x40 + backgroundIntensity = 0x80 + backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) +) + +type wchar uint16 +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +type consoleCursorInfo struct { + size dword + visible int32 +} + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") + procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") +) + +type Writer struct { + out io.Writer + handle syscall.Handle + lastbuf bytes.Buffer + oldattr word +} + +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + if isatty.IsTerminal(file.Fd()) { + var csbi consoleScreenBufferInfo + handle := syscall.Handle(file.Fd()) + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + return &Writer{out: file, handle: handle, oldattr: csbi.attributes} + } else { + return file + } +} + +func NewColorableStdout() io.Writer { + return NewColorable(os.Stdout) +} + +func NewColorableStderr() io.Writer { + return NewColorable(os.Stderr) +} + +var color256 = map[int]int{ + 0: 0x000000, + 1: 0x800000, + 2: 0x008000, + 3: 0x808000, + 4: 0x000080, + 5: 0x800080, + 6: 0x008080, + 7: 0xc0c0c0, + 8: 0x808080, + 9: 0xff0000, + 10: 0x00ff00, + 11: 0xffff00, + 12: 0x0000ff, + 13: 0xff00ff, + 14: 0x00ffff, + 15: 0xffffff, + 16: 0x000000, + 17: 0x00005f, + 18: 0x000087, + 19: 0x0000af, + 20: 0x0000d7, + 21: 0x0000ff, + 22: 0x005f00, + 23: 0x005f5f, + 24: 0x005f87, + 25: 0x005faf, + 26: 0x005fd7, + 27: 0x005fff, + 28: 0x008700, + 29: 0x00875f, + 30: 0x008787, + 31: 0x0087af, + 32: 0x0087d7, + 33: 0x0087ff, + 34: 0x00af00, + 35: 0x00af5f, + 36: 0x00af87, + 37: 0x00afaf, + 38: 0x00afd7, + 39: 0x00afff, + 40: 0x00d700, + 41: 0x00d75f, + 42: 0x00d787, + 43: 0x00d7af, + 44: 0x00d7d7, + 45: 0x00d7ff, + 46: 0x00ff00, + 47: 0x00ff5f, + 48: 0x00ff87, + 49: 0x00ffaf, + 50: 0x00ffd7, + 51: 0x00ffff, + 52: 0x5f0000, + 53: 0x5f005f, + 54: 0x5f0087, + 55: 0x5f00af, + 56: 0x5f00d7, + 57: 0x5f00ff, + 58: 0x5f5f00, + 59: 0x5f5f5f, + 60: 0x5f5f87, + 61: 0x5f5faf, + 62: 0x5f5fd7, + 63: 0x5f5fff, + 64: 0x5f8700, + 65: 0x5f875f, + 66: 0x5f8787, + 67: 0x5f87af, + 68: 0x5f87d7, + 69: 0x5f87ff, + 70: 0x5faf00, + 71: 0x5faf5f, + 72: 0x5faf87, + 73: 0x5fafaf, + 74: 0x5fafd7, + 75: 0x5fafff, + 76: 0x5fd700, + 77: 0x5fd75f, + 78: 0x5fd787, + 79: 0x5fd7af, + 80: 0x5fd7d7, + 81: 0x5fd7ff, + 82: 0x5fff00, + 83: 0x5fff5f, + 84: 0x5fff87, + 85: 0x5fffaf, + 86: 0x5fffd7, + 87: 0x5fffff, + 88: 0x870000, + 89: 0x87005f, + 90: 0x870087, + 91: 0x8700af, + 92: 0x8700d7, + 93: 0x8700ff, + 94: 0x875f00, + 95: 0x875f5f, + 96: 0x875f87, + 97: 0x875faf, + 98: 0x875fd7, + 99: 0x875fff, + 100: 0x878700, + 101: 0x87875f, + 102: 0x878787, + 103: 0x8787af, + 104: 0x8787d7, + 105: 0x8787ff, + 106: 0x87af00, + 107: 0x87af5f, + 108: 0x87af87, + 109: 0x87afaf, + 110: 0x87afd7, + 111: 0x87afff, + 112: 0x87d700, + 113: 0x87d75f, + 114: 0x87d787, + 115: 0x87d7af, + 116: 0x87d7d7, + 117: 0x87d7ff, + 118: 0x87ff00, + 119: 0x87ff5f, + 120: 0x87ff87, + 121: 0x87ffaf, + 122: 0x87ffd7, + 123: 0x87ffff, + 124: 0xaf0000, + 125: 0xaf005f, + 126: 0xaf0087, + 127: 0xaf00af, + 128: 0xaf00d7, + 129: 0xaf00ff, + 130: 0xaf5f00, + 131: 0xaf5f5f, + 132: 0xaf5f87, + 133: 0xaf5faf, + 134: 0xaf5fd7, + 135: 0xaf5fff, + 136: 0xaf8700, + 137: 0xaf875f, + 138: 0xaf8787, + 139: 0xaf87af, + 140: 0xaf87d7, + 141: 0xaf87ff, + 142: 0xafaf00, + 143: 0xafaf5f, + 144: 0xafaf87, + 145: 0xafafaf, + 146: 0xafafd7, + 147: 0xafafff, + 148: 0xafd700, + 149: 0xafd75f, + 150: 0xafd787, + 151: 0xafd7af, + 152: 0xafd7d7, + 153: 0xafd7ff, + 154: 0xafff00, + 155: 0xafff5f, + 156: 0xafff87, + 157: 0xafffaf, + 158: 0xafffd7, + 159: 0xafffff, + 160: 0xd70000, + 161: 0xd7005f, + 162: 0xd70087, + 163: 0xd700af, + 164: 0xd700d7, + 165: 0xd700ff, + 166: 0xd75f00, + 167: 0xd75f5f, + 168: 0xd75f87, + 169: 0xd75faf, + 170: 0xd75fd7, + 171: 0xd75fff, + 172: 0xd78700, + 173: 0xd7875f, + 174: 0xd78787, + 175: 0xd787af, + 176: 0xd787d7, + 177: 0xd787ff, + 178: 0xd7af00, + 179: 0xd7af5f, + 180: 0xd7af87, + 181: 0xd7afaf, + 182: 0xd7afd7, + 183: 0xd7afff, + 184: 0xd7d700, + 185: 0xd7d75f, + 186: 0xd7d787, + 187: 0xd7d7af, + 188: 0xd7d7d7, + 189: 0xd7d7ff, + 190: 0xd7ff00, + 191: 0xd7ff5f, + 192: 0xd7ff87, + 193: 0xd7ffaf, + 194: 0xd7ffd7, + 195: 0xd7ffff, + 196: 0xff0000, + 197: 0xff005f, + 198: 0xff0087, + 199: 0xff00af, + 200: 0xff00d7, + 201: 0xff00ff, + 202: 0xff5f00, + 203: 0xff5f5f, + 204: 0xff5f87, + 205: 0xff5faf, + 206: 0xff5fd7, + 207: 0xff5fff, + 208: 0xff8700, + 209: 0xff875f, + 210: 0xff8787, + 211: 0xff87af, + 212: 0xff87d7, + 213: 0xff87ff, + 214: 0xffaf00, + 215: 0xffaf5f, + 216: 0xffaf87, + 217: 0xffafaf, + 218: 0xffafd7, + 219: 0xffafff, + 220: 0xffd700, + 221: 0xffd75f, + 222: 0xffd787, + 223: 0xffd7af, + 224: 0xffd7d7, + 225: 0xffd7ff, + 226: 0xffff00, + 227: 0xffff5f, + 228: 0xffff87, + 229: 0xffffaf, + 230: 0xffffd7, + 231: 0xffffff, + 232: 0x080808, + 233: 0x121212, + 234: 0x1c1c1c, + 235: 0x262626, + 236: 0x303030, + 237: 0x3a3a3a, + 238: 0x444444, + 239: 0x4e4e4e, + 240: 0x585858, + 241: 0x626262, + 242: 0x6c6c6c, + 243: 0x767676, + 244: 0x808080, + 245: 0x8a8a8a, + 246: 0x949494, + 247: 0x9e9e9e, + 248: 0xa8a8a8, + 249: 0xb2b2b2, + 250: 0xbcbcbc, + 251: 0xc6c6c6, + 252: 0xd0d0d0, + 253: 0xdadada, + 254: 0xe4e4e4, + 255: 0xeeeeee, +} + +func (w *Writer) Write(data []byte) (n int, err error) { + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + er := bytes.NewReader(data) + var bw [1]byte +loop: + for { + r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + if r1 == 0 { + break loop + } + + c1, err := er.ReadByte() + if err != nil { + break loop + } + if c1 != 0x1b { + bw[0] = c1 + w.out.Write(bw[:]) + continue + } + c2, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + break loop + } + if c2 != 0x5b { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + continue + } + + var buf bytes.Buffer + var m byte + for { + c, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + w.lastbuf.Write(buf.Bytes()) + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + m = c + break + } + buf.Write([]byte(string(c))) + } + + var csbi consoleScreenBufferInfo + switch m { + case 'A': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'B': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'C': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'D': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + if n, err = strconv.Atoi(buf.String()); err == nil { + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + } + case 'E': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'F': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'G': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = short(n - 1) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'H': + token := strings.Split(buf.String(), ";") + if len(token) != 2 { + continue + } + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + n2, err := strconv.Atoi(token[1]) + if err != nil { + continue + } + csbi.cursorPosition.x = short(n2 - 1) + csbi.cursorPosition.y = short(n1 - 1) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'J': + n, err := strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + } + var count, written dword + count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x) + procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'K': + n, err := strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} + } + var count, written dword + count = dword(csbi.size.x - csbi.cursorPosition.x) + procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'm': + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + attr := csbi.attributes + cs := buf.String() + if cs == "" { + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr)) + continue + } + token := strings.Split(cs, ";") + for i := 0; i < len(token); i++ { + ns := token[i] + if n, err = strconv.Atoi(ns); err == nil { + switch { + case n == 0 || n == 100: + attr = w.oldattr + case 1 <= n && n <= 5: + attr |= foregroundIntensity + case n == 7: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case 22 == n || n == 25 || n == 25: + attr |= foregroundIntensity + case n == 27: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case 30 <= n && n <= 37: + attr &= backgroundMask + if (n-30)&1 != 0 { + attr |= foregroundRed + } + if (n-30)&2 != 0 { + attr |= foregroundGreen + } + if (n-30)&4 != 0 { + attr |= foregroundBlue + } + case n == 38: // set foreground color. + if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256foreAttr == nil { + n256setup() + } + attr &= backgroundMask + attr |= n256foreAttr[n256] + i += 2 + } + } else { + attr = attr & (w.oldattr & backgroundMask) + } + case n == 39: // reset foreground color. + attr &= backgroundMask + attr |= w.oldattr & foregroundMask + case 40 <= n && n <= 47: + attr &= foregroundMask + if (n-40)&1 != 0 { + attr |= backgroundRed + } + if (n-40)&2 != 0 { + attr |= backgroundGreen + } + if (n-40)&4 != 0 { + attr |= backgroundBlue + } + case n == 48: // set background color. + if i < len(token)-2 && token[i+1] == "5" { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256backAttr == nil { + n256setup() + } + attr &= foregroundMask + attr |= n256backAttr[n256] + i += 2 + } + } else { + attr = attr & (w.oldattr & foregroundMask) + } + case n == 49: // reset foreground color. + attr &= foregroundMask + attr |= w.oldattr & backgroundMask + case 90 <= n && n <= 97: + attr = (attr & backgroundMask) + attr |= foregroundIntensity + if (n-90)&1 != 0 { + attr |= foregroundRed + } + if (n-90)&2 != 0 { + attr |= foregroundGreen + } + if (n-90)&4 != 0 { + attr |= foregroundBlue + } + case 100 <= n && n <= 107: + attr = (attr & foregroundMask) + attr |= backgroundIntensity + if (n-100)&1 != 0 { + attr |= backgroundRed + } + if (n-100)&2 != 0 { + attr |= backgroundGreen + } + if (n-100)&4 != 0 { + attr |= backgroundBlue + } + } + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) + } + } + case 'h': + cs := buf.String() + if cs == "?25" { + var ci consoleCursorInfo + procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 1 + procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + } + case 'l': + cs := buf.String() + if cs == "?25" { + var ci consoleCursorInfo + procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 0 + procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + } + } + } + return len(data) - w.lastbuf.Len(), nil +} + +type consoleColor struct { + rgb int + red bool + green bool + blue bool + intensity bool +} + +func (c consoleColor) foregroundAttr() (attr word) { + if c.red { + attr |= foregroundRed + } + if c.green { + attr |= foregroundGreen + } + if c.blue { + attr |= foregroundBlue + } + if c.intensity { + attr |= foregroundIntensity + } + return +} + +func (c consoleColor) backgroundAttr() (attr word) { + if c.red { + attr |= backgroundRed + } + if c.green { + attr |= backgroundGreen + } + if c.blue { + attr |= backgroundBlue + } + if c.intensity { + attr |= backgroundIntensity + } + return +} + +var color16 = []consoleColor{ + consoleColor{0x000000, false, false, false, false}, + consoleColor{0x000080, false, false, true, false}, + consoleColor{0x008000, false, true, false, false}, + consoleColor{0x008080, false, true, true, false}, + consoleColor{0x800000, true, false, false, false}, + consoleColor{0x800080, true, false, true, false}, + consoleColor{0x808000, true, true, false, false}, + consoleColor{0xc0c0c0, true, true, true, false}, + consoleColor{0x808080, false, false, false, true}, + consoleColor{0x0000ff, false, false, true, true}, + consoleColor{0x00ff00, false, true, false, true}, + consoleColor{0x00ffff, false, true, true, true}, + consoleColor{0xff0000, true, false, false, true}, + consoleColor{0xff00ff, true, false, true, true}, + consoleColor{0xffff00, true, true, false, true}, + consoleColor{0xffffff, true, true, true, true}, +} + +type hsv struct { + h, s, v float32 +} + +func (a hsv) dist(b hsv) float32 { + dh := a.h - b.h + switch { + case dh > 0.5: + dh = 1 - dh + case dh < -0.5: + dh = -1 - dh + } + ds := a.s - b.s + dv := a.v - b.v + return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) +} + +func toHSV(rgb int) hsv { + r, g, b := float32((rgb&0xFF0000)>>16)/256.0, + float32((rgb&0x00FF00)>>8)/256.0, + float32(rgb&0x0000FF)/256.0 + min, max := minmax3f(r, g, b) + h := max - min + if h > 0 { + if max == r { + h = (g - b) / h + if h < 0 { + h += 6 + } + } else if max == g { + h = 2 + (b-r)/h + } else { + h = 4 + (r-g)/h + } + } + h /= 6.0 + s := max - min + if max != 0 { + s /= max + } + v := max + return hsv{h: h, s: s, v: v} +} + +type hsvTable []hsv + +func toHSVTable(rgbTable []consoleColor) hsvTable { + t := make(hsvTable, len(rgbTable)) + for i, c := range rgbTable { + t[i] = toHSV(c.rgb) + } + return t +} + +func (t hsvTable) find(rgb int) consoleColor { + hsv := toHSV(rgb) + n := 7 + l := float32(5.0) + for i, p := range t { + d := hsv.dist(p) + if d < l { + l, n = d, i + } + } + return color16[n] +} + +func minmax3f(a, b, c float32) (min, max float32) { + if a < b { + if b < c { + return a, c + } else if a < c { + return a, b + } else { + return c, b + } + } else { + if a < c { + return b, c + } else if b < c { + return b, a + } else { + return c, a + } + } +} + +var n256foreAttr []word +var n256backAttr []word + +func n256setup() { + n256foreAttr = make([]word, 256) + n256backAttr = make([]word, 256) + t := toHSVTable(color16) + for i, rgb := range color256 { + c := t.find(rgb) + n256foreAttr[i] = c.foregroundAttr() + n256backAttr[i] = c.backgroundAttr() + } +} diff --git a/vendor/github.com/mattn/go-colorable/noncolorable.go b/vendor/github.com/mattn/go-colorable/noncolorable.go new file mode 100644 index 0000000..b60801d --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/noncolorable.go @@ -0,0 +1,58 @@ +package colorable + +import ( + "bytes" + "io" +) + +type NonColorable struct { + out io.Writer + lastbuf bytes.Buffer +} + +func NewNonColorable(w io.Writer) io.Writer { + return &NonColorable{out: w} +} + +func (w *NonColorable) Write(data []byte) (n int, err error) { + er := bytes.NewReader(data) + var bw [1]byte +loop: + for { + c1, err := er.ReadByte() + if err != nil { + break loop + } + if c1 != 0x1b { + bw[0] = c1 + w.out.Write(bw[:]) + continue + } + c2, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + break loop + } + if c2 != 0x5b { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + continue + } + + var buf bytes.Buffer + for { + c, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + w.lastbuf.Write(buf.Bytes()) + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + break + } + buf.Write([]byte(string(c))) + } + } + return len(data) - w.lastbuf.Len(), nil +} diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE new file mode 100644 index 0000000..65dc692 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) Yasuhiro MATSUMOTO + +MIT License (Expat) + +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/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md new file mode 100644 index 0000000..74845de --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/README.md @@ -0,0 +1,37 @@ +# go-isatty + +isatty for golang + +## Usage + +```go +package main + +import ( + "fmt" + "github.com/mattn/go-isatty" + "os" +) + +func main() { + if isatty.IsTerminal(os.Stdout.Fd()) { + fmt.Println("Is Terminal") + } else { + fmt.Println("Is Not Terminal") + } +} +``` + +## Installation + +``` +$ go get github.com/mattn/go-isatty +``` + +# License + +MIT + +# Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-isatty/_example/example.go b/vendor/github.com/mattn/go-isatty/_example/example.go new file mode 100644 index 0000000..4d66754 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/_example/example.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "github.com/mattn/go-isatty" + "os" +) + +func main() { + if isatty.IsTerminal(int(os.Stdout.Fd())) { + fmt.Println("Is Terminal") + } else { + fmt.Println("Is Not Terminal") + } +} diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go new file mode 100644 index 0000000..17d4f90 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/doc.go @@ -0,0 +1,2 @@ +// Package isatty implements interface to isatty +package isatty diff --git a/vendor/github.com/mattn/go-isatty/isatty_appengine.go b/vendor/github.com/mattn/go-isatty/isatty_appengine.go new file mode 100644 index 0000000..83c5887 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_appengine.go @@ -0,0 +1,9 @@ +// +build appengine + +package isatty + +// IsTerminal returns true if the file descriptor is terminal which +// is always false on on appengine classic which is a sandboxed PaaS. +func IsTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go new file mode 100644 index 0000000..42f2514 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -0,0 +1,18 @@ +// +build darwin freebsd openbsd netbsd dragonfly +// +build !appengine + +package isatty + +import ( + "syscall" + "unsafe" +) + +const ioctlReadTermios = syscall.TIOCGETA + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_linux.go b/vendor/github.com/mattn/go-isatty/isatty_linux.go new file mode 100644 index 0000000..9d24bac --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_linux.go @@ -0,0 +1,18 @@ +// +build linux +// +build !appengine + +package isatty + +import ( + "syscall" + "unsafe" +) + +const ioctlReadTermios = syscall.TCGETS + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go new file mode 100644 index 0000000..1f0c6bf --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_solaris.go @@ -0,0 +1,16 @@ +// +build solaris +// +build !appengine + +package isatty + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c +func IsTerminal(fd uintptr) bool { + var termio unix.Termio + err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) + return err == nil +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go new file mode 100644 index 0000000..83c398b --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -0,0 +1,19 @@ +// +build windows +// +build !appengine + +package isatty + +import ( + "syscall" + "unsafe" +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") +var procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml new file mode 100644 index 0000000..5c9c2a3 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - tip +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL diff --git a/vendor/github.com/mattn/go-runewidth/LICENSE b/vendor/github.com/mattn/go-runewidth/LICENSE new file mode 100644 index 0000000..91b5cef --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-runewidth/README.mkd b/vendor/github.com/mattn/go-runewidth/README.mkd new file mode 100644 index 0000000..66663a9 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/README.mkd @@ -0,0 +1,27 @@ +go-runewidth +============ + +[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) +[![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD) +[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) +[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) + +Provides functions to get fixed width of the character or string. + +Usage +----- + +```go +runewidth.StringWidth("つのだ☆HIRO") == 12 +``` + + +Author +------ + +Yasuhiro Matsumoto + +License +------- + +under the MIT License: http://mattn.mit-license.org/2013 diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go new file mode 100644 index 0000000..2164497 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -0,0 +1,1223 @@ +package runewidth + +var ( + // EastAsianWidth will be set true if the current locale is CJK + EastAsianWidth = IsEastAsian() + + // DefaultCondition is a condition in current locale + DefaultCondition = &Condition{EastAsianWidth} +) + +type interval struct { + first rune + last rune +} + +type table []interval + +func inTables(r rune, ts ...table) bool { + for _, t := range ts { + if inTable(r, t) { + return true + } + } + return false +} + +func inTable(r rune, t table) bool { + // func (t table) IncludesRune(r rune) bool { + if r < t[0].first { + return false + } + + bot := 0 + top := len(t) - 1 + for top >= bot { + mid := (bot + top) / 2 + + 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}, + {0x202A, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, +} + +var combining = table{ + {0x0300, 0x036F}, {0x0483, 0x0489}, {0x0591, 0x05BD}, + {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5}, + {0x05C7, 0x05C7}, {0x0610, 0x061A}, {0x064B, 0x065F}, + {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DF, 0x06E4}, + {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x0711, 0x0711}, + {0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, + {0x0816, 0x0819}, {0x081B, 0x0823}, {0x0825, 0x0827}, + {0x0829, 0x082D}, {0x0859, 0x085B}, {0x08D4, 0x08E1}, + {0x08E3, 0x0903}, {0x093A, 0x093C}, {0x093E, 0x094F}, + {0x0951, 0x0957}, {0x0962, 0x0963}, {0x0981, 0x0983}, + {0x09BC, 0x09BC}, {0x09BE, 0x09C4}, {0x09C7, 0x09C8}, + {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, {0x09E2, 0x09E3}, + {0x0A01, 0x0A03}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, + {0x0A70, 0x0A71}, {0x0A75, 0x0A75}, {0x0A81, 0x0A83}, + {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, + {0x0ACB, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0B01, 0x0B03}, + {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B44}, {0x0B47, 0x0B48}, + {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, {0x0B62, 0x0B63}, + {0x0B82, 0x0B82}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, + {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, {0x0C00, 0x0C03}, + {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, {0x0C62, 0x0C63}, {0x0C81, 0x0C83}, + {0x0CBC, 0x0CBC}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, + {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CE2, 0x0CE3}, + {0x0D01, 0x0D03}, {0x0D3E, 0x0D44}, {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, {0x0D62, 0x0D63}, + {0x0D82, 0x0D83}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, + {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DF2, 0x0DF3}, + {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, + {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, + {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, + {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F3E, 0x0F3F}, + {0x0F71, 0x0F84}, {0x0F86, 0x0F87}, {0x0F8D, 0x0F97}, + {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102B, 0x103E}, + {0x1056, 0x1059}, {0x105E, 0x1060}, {0x1062, 0x1064}, + {0x1067, 0x106D}, {0x1071, 0x1074}, {0x1082, 0x108D}, + {0x108F, 0x108F}, {0x109A, 0x109D}, {0x135D, 0x135F}, + {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753}, + {0x1772, 0x1773}, {0x17B4, 0x17D3}, {0x17DD, 0x17DD}, + {0x180B, 0x180D}, {0x1885, 0x1886}, {0x18A9, 0x18A9}, + {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1A17, 0x1A1B}, + {0x1A55, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A7F}, + {0x1AB0, 0x1ABE}, {0x1B00, 0x1B04}, {0x1B34, 0x1B44}, + {0x1B6B, 0x1B73}, {0x1B80, 0x1B82}, {0x1BA1, 0x1BAD}, + {0x1BE6, 0x1BF3}, {0x1C24, 0x1C37}, {0x1CD0, 0x1CD2}, + {0x1CD4, 0x1CE8}, {0x1CED, 0x1CED}, {0x1CF2, 0x1CF4}, + {0x1CF8, 0x1CF9}, {0x1DC0, 0x1DF5}, {0x1DFB, 0x1DFF}, + {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F}, + {0x2DE0, 0x2DFF}, {0x302A, 0x302F}, {0x3099, 0x309A}, + {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, + {0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806}, + {0xA80B, 0xA80B}, {0xA823, 0xA827}, {0xA880, 0xA881}, + {0xA8B4, 0xA8C5}, {0xA8E0, 0xA8F1}, {0xA926, 0xA92D}, + {0xA947, 0xA953}, {0xA980, 0xA983}, {0xA9B3, 0xA9C0}, + {0xA9E5, 0xA9E5}, {0xAA29, 0xAA36}, {0xAA43, 0xAA43}, + {0xAA4C, 0xAA4D}, {0xAA7B, 0xAA7D}, {0xAAB0, 0xAAB0}, + {0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF}, + {0xAAC1, 0xAAC1}, {0xAAEB, 0xAAEF}, {0xAAF5, 0xAAF6}, + {0xABE3, 0xABEA}, {0xABEC, 0xABED}, {0xFB1E, 0xFB1E}, + {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, + {0x102E0, 0x102E0}, {0x10376, 0x1037A}, {0x10A01, 0x10A03}, + {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, {0x10A38, 0x10A3A}, + {0x10A3F, 0x10A3F}, {0x10AE5, 0x10AE6}, {0x11000, 0x11002}, + {0x11038, 0x11046}, {0x1107F, 0x11082}, {0x110B0, 0x110BA}, + {0x11100, 0x11102}, {0x11127, 0x11134}, {0x11173, 0x11173}, + {0x11180, 0x11182}, {0x111B3, 0x111C0}, {0x111CA, 0x111CC}, + {0x1122C, 0x11237}, {0x1123E, 0x1123E}, {0x112DF, 0x112EA}, + {0x11300, 0x11303}, {0x1133C, 0x1133C}, {0x1133E, 0x11344}, + {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11357, 0x11357}, + {0x11362, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, + {0x11435, 0x11446}, {0x114B0, 0x114C3}, {0x115AF, 0x115B5}, + {0x115B8, 0x115C0}, {0x115DC, 0x115DD}, {0x11630, 0x11640}, + {0x116AB, 0x116B7}, {0x1171D, 0x1172B}, {0x11C2F, 0x11C36}, + {0x11C38, 0x11C3F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, + {0x16AF0, 0x16AF4}, {0x16B30, 0x16B36}, {0x16F51, 0x16F7E}, + {0x16F8F, 0x16F92}, {0x1BC9D, 0x1BC9E}, {0x1D165, 0x1D169}, + {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, + {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1DA00, 0x1DA36}, + {0x1DA3B, 0x1DA6C}, {0x1DA75, 0x1DA75}, {0x1DA84, 0x1DA84}, + {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, + {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, + {0x1E026, 0x1E02A}, {0x1E8D0, 0x1E8D6}, {0x1E944, 0x1E94A}, + {0xE0100, 0xE01EF}, +} + +var doublewidth = table{ + {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, + {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, + {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, + {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, + {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, + {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, + {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, + {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, + {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, + {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, + {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, + {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, + {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, + {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, + {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA}, + {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, + {0x3250, 0x32FE}, {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, + {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, + {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, + {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, + {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE0}, {0x17000, 0x187EC}, + {0x18800, 0x18AF2}, {0x1B000, 0x1B001}, {0x1F004, 0x1F004}, + {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, + {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, + {0x1F250, 0x1F251}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, + {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, + {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, + {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC}, + {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, + {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, + {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, + {0x1F6D0, 0x1F6D2}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6F6}, + {0x1F910, 0x1F91E}, {0x1F920, 0x1F927}, {0x1F930, 0x1F930}, + {0x1F933, 0x1F93E}, {0x1F940, 0x1F94B}, {0x1F950, 0x1F95E}, + {0x1F980, 0x1F991}, {0x1F9C0, 0x1F9C0}, {0x20000, 0x2FFFD}, + {0x30000, 0x3FFFD}, +} + +var ambiguous = table{ + {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, + {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, + {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, + {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, + {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, + {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, + {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, + {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, + {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, + {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, + {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, + {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, + {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, + {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, + {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, + {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, + {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, + {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, + {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, + {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, + {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, + {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, + {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, + {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, + {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, + {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, + {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, + {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, + {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, + {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, + {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, + {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, + {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, + {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, + {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, + {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, + {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, + {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, + {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, + {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, + {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, + {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, + {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, + {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, + {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, + {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, + {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, + {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, + {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, + {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, + {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, + {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, + {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, + {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, + {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, + {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, + {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, + {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, + {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, + {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, +} + +var emoji = table{ + {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F321}, {0x1F324, 0x1F32C}, + {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F396, 0x1F397}, + {0x1F399, 0x1F39B}, {0x1F39E, 0x1F39F}, {0x1F3CB, 0x1F3CE}, + {0x1F3D4, 0x1F3DF}, {0x1F3F3, 0x1F3F5}, {0x1F3F7, 0x1F3F7}, + {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FD}, + {0x1F549, 0x1F54A}, {0x1F56F, 0x1F570}, {0x1F573, 0x1F579}, + {0x1F587, 0x1F587}, {0x1F58A, 0x1F58D}, {0x1F590, 0x1F590}, + {0x1F5A5, 0x1F5A5}, {0x1F5A8, 0x1F5A8}, {0x1F5B1, 0x1F5B2}, + {0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4}, {0x1F5D1, 0x1F5D3}, + {0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1}, {0x1F5E3, 0x1F5E3}, + {0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF}, {0x1F5F3, 0x1F5F3}, + {0x1F5FA, 0x1F5FA}, {0x1F6CB, 0x1F6CF}, {0x1F6E0, 0x1F6E5}, + {0x1F6E9, 0x1F6E9}, {0x1F6F0, 0x1F6F0}, {0x1F6F3, 0x1F6F3}, +} + +var notassigned = table{ + {0x0378, 0x0379}, {0x0380, 0x0383}, {0x038B, 0x038B}, + {0x038D, 0x038D}, {0x03A2, 0x03A2}, {0x0530, 0x0530}, + {0x0557, 0x0558}, {0x0560, 0x0560}, {0x0588, 0x0588}, + {0x058B, 0x058C}, {0x0590, 0x0590}, {0x05C8, 0x05CF}, + {0x05EB, 0x05EF}, {0x05F5, 0x05FF}, {0x061D, 0x061D}, + {0x070E, 0x070E}, {0x074B, 0x074C}, {0x07B2, 0x07BF}, + {0x07FB, 0x07FF}, {0x082E, 0x082F}, {0x083F, 0x083F}, + {0x085C, 0x085D}, {0x085F, 0x089F}, {0x08B5, 0x08B5}, + {0x08BE, 0x08D3}, {0x0984, 0x0984}, {0x098D, 0x098E}, + {0x0991, 0x0992}, {0x09A9, 0x09A9}, {0x09B1, 0x09B1}, + {0x09B3, 0x09B5}, {0x09BA, 0x09BB}, {0x09C5, 0x09C6}, + {0x09C9, 0x09CA}, {0x09CF, 0x09D6}, {0x09D8, 0x09DB}, + {0x09DE, 0x09DE}, {0x09E4, 0x09E5}, {0x09FC, 0x0A00}, + {0x0A04, 0x0A04}, {0x0A0B, 0x0A0E}, {0x0A11, 0x0A12}, + {0x0A29, 0x0A29}, {0x0A31, 0x0A31}, {0x0A34, 0x0A34}, + {0x0A37, 0x0A37}, {0x0A3A, 0x0A3B}, {0x0A3D, 0x0A3D}, + {0x0A43, 0x0A46}, {0x0A49, 0x0A4A}, {0x0A4E, 0x0A50}, + {0x0A52, 0x0A58}, {0x0A5D, 0x0A5D}, {0x0A5F, 0x0A65}, + {0x0A76, 0x0A80}, {0x0A84, 0x0A84}, {0x0A8E, 0x0A8E}, + {0x0A92, 0x0A92}, {0x0AA9, 0x0AA9}, {0x0AB1, 0x0AB1}, + {0x0AB4, 0x0AB4}, {0x0ABA, 0x0ABB}, {0x0AC6, 0x0AC6}, + {0x0ACA, 0x0ACA}, {0x0ACE, 0x0ACF}, {0x0AD1, 0x0ADF}, + {0x0AE4, 0x0AE5}, {0x0AF2, 0x0AF8}, {0x0AFA, 0x0B00}, + {0x0B04, 0x0B04}, {0x0B0D, 0x0B0E}, {0x0B11, 0x0B12}, + {0x0B29, 0x0B29}, {0x0B31, 0x0B31}, {0x0B34, 0x0B34}, + {0x0B3A, 0x0B3B}, {0x0B45, 0x0B46}, {0x0B49, 0x0B4A}, + {0x0B4E, 0x0B55}, {0x0B58, 0x0B5B}, {0x0B5E, 0x0B5E}, + {0x0B64, 0x0B65}, {0x0B78, 0x0B81}, {0x0B84, 0x0B84}, + {0x0B8B, 0x0B8D}, {0x0B91, 0x0B91}, {0x0B96, 0x0B98}, + {0x0B9B, 0x0B9B}, {0x0B9D, 0x0B9D}, {0x0BA0, 0x0BA2}, + {0x0BA5, 0x0BA7}, {0x0BAB, 0x0BAD}, {0x0BBA, 0x0BBD}, + {0x0BC3, 0x0BC5}, {0x0BC9, 0x0BC9}, {0x0BCE, 0x0BCF}, + {0x0BD1, 0x0BD6}, {0x0BD8, 0x0BE5}, {0x0BFB, 0x0BFF}, + {0x0C04, 0x0C04}, {0x0C0D, 0x0C0D}, {0x0C11, 0x0C11}, + {0x0C29, 0x0C29}, {0x0C3A, 0x0C3C}, {0x0C45, 0x0C45}, + {0x0C49, 0x0C49}, {0x0C4E, 0x0C54}, {0x0C57, 0x0C57}, + {0x0C5B, 0x0C5F}, {0x0C64, 0x0C65}, {0x0C70, 0x0C77}, + {0x0C84, 0x0C84}, {0x0C8D, 0x0C8D}, {0x0C91, 0x0C91}, + {0x0CA9, 0x0CA9}, {0x0CB4, 0x0CB4}, {0x0CBA, 0x0CBB}, + {0x0CC5, 0x0CC5}, {0x0CC9, 0x0CC9}, {0x0CCE, 0x0CD4}, + {0x0CD7, 0x0CDD}, {0x0CDF, 0x0CDF}, {0x0CE4, 0x0CE5}, + {0x0CF0, 0x0CF0}, {0x0CF3, 0x0D00}, {0x0D04, 0x0D04}, + {0x0D0D, 0x0D0D}, {0x0D11, 0x0D11}, {0x0D3B, 0x0D3C}, + {0x0D45, 0x0D45}, {0x0D49, 0x0D49}, {0x0D50, 0x0D53}, + {0x0D64, 0x0D65}, {0x0D80, 0x0D81}, {0x0D84, 0x0D84}, + {0x0D97, 0x0D99}, {0x0DB2, 0x0DB2}, {0x0DBC, 0x0DBC}, + {0x0DBE, 0x0DBF}, {0x0DC7, 0x0DC9}, {0x0DCB, 0x0DCE}, + {0x0DD5, 0x0DD5}, {0x0DD7, 0x0DD7}, {0x0DE0, 0x0DE5}, + {0x0DF0, 0x0DF1}, {0x0DF5, 0x0E00}, {0x0E3B, 0x0E3E}, + {0x0E5C, 0x0E80}, {0x0E83, 0x0E83}, {0x0E85, 0x0E86}, + {0x0E89, 0x0E89}, {0x0E8B, 0x0E8C}, {0x0E8E, 0x0E93}, + {0x0E98, 0x0E98}, {0x0EA0, 0x0EA0}, {0x0EA4, 0x0EA4}, + {0x0EA6, 0x0EA6}, {0x0EA8, 0x0EA9}, {0x0EAC, 0x0EAC}, + {0x0EBA, 0x0EBA}, {0x0EBE, 0x0EBF}, {0x0EC5, 0x0EC5}, + {0x0EC7, 0x0EC7}, {0x0ECE, 0x0ECF}, {0x0EDA, 0x0EDB}, + {0x0EE0, 0x0EFF}, {0x0F48, 0x0F48}, {0x0F6D, 0x0F70}, + {0x0F98, 0x0F98}, {0x0FBD, 0x0FBD}, {0x0FCD, 0x0FCD}, + {0x0FDB, 0x0FFF}, {0x10C6, 0x10C6}, {0x10C8, 0x10CC}, + {0x10CE, 0x10CF}, {0x1249, 0x1249}, {0x124E, 0x124F}, + {0x1257, 0x1257}, {0x1259, 0x1259}, {0x125E, 0x125F}, + {0x1289, 0x1289}, {0x128E, 0x128F}, {0x12B1, 0x12B1}, + {0x12B6, 0x12B7}, {0x12BF, 0x12BF}, {0x12C1, 0x12C1}, + {0x12C6, 0x12C7}, {0x12D7, 0x12D7}, {0x1311, 0x1311}, + {0x1316, 0x1317}, {0x135B, 0x135C}, {0x137D, 0x137F}, + {0x139A, 0x139F}, {0x13F6, 0x13F7}, {0x13FE, 0x13FF}, + {0x169D, 0x169F}, {0x16F9, 0x16FF}, {0x170D, 0x170D}, + {0x1715, 0x171F}, {0x1737, 0x173F}, {0x1754, 0x175F}, + {0x176D, 0x176D}, {0x1771, 0x1771}, {0x1774, 0x177F}, + {0x17DE, 0x17DF}, {0x17EA, 0x17EF}, {0x17FA, 0x17FF}, + {0x180F, 0x180F}, {0x181A, 0x181F}, {0x1878, 0x187F}, + {0x18AB, 0x18AF}, {0x18F6, 0x18FF}, {0x191F, 0x191F}, + {0x192C, 0x192F}, {0x193C, 0x193F}, {0x1941, 0x1943}, + {0x196E, 0x196F}, {0x1975, 0x197F}, {0x19AC, 0x19AF}, + {0x19CA, 0x19CF}, {0x19DB, 0x19DD}, {0x1A1C, 0x1A1D}, + {0x1A5F, 0x1A5F}, {0x1A7D, 0x1A7E}, {0x1A8A, 0x1A8F}, + {0x1A9A, 0x1A9F}, {0x1AAE, 0x1AAF}, {0x1ABF, 0x1AFF}, + {0x1B4C, 0x1B4F}, {0x1B7D, 0x1B7F}, {0x1BF4, 0x1BFB}, + {0x1C38, 0x1C3A}, {0x1C4A, 0x1C4C}, {0x1C89, 0x1CBF}, + {0x1CC8, 0x1CCF}, {0x1CF7, 0x1CF7}, {0x1CFA, 0x1CFF}, + {0x1DF6, 0x1DFA}, {0x1F16, 0x1F17}, {0x1F1E, 0x1F1F}, + {0x1F46, 0x1F47}, {0x1F4E, 0x1F4F}, {0x1F58, 0x1F58}, + {0x1F5A, 0x1F5A}, {0x1F5C, 0x1F5C}, {0x1F5E, 0x1F5E}, + {0x1F7E, 0x1F7F}, {0x1FB5, 0x1FB5}, {0x1FC5, 0x1FC5}, + {0x1FD4, 0x1FD5}, {0x1FDC, 0x1FDC}, {0x1FF0, 0x1FF1}, + {0x1FF5, 0x1FF5}, {0x1FFF, 0x1FFF}, {0x2065, 0x2065}, + {0x2072, 0x2073}, {0x208F, 0x208F}, {0x209D, 0x209F}, + {0x20BF, 0x20CF}, {0x20F1, 0x20FF}, {0x218C, 0x218F}, + {0x23FF, 0x23FF}, {0x2427, 0x243F}, {0x244B, 0x245F}, + {0x2B74, 0x2B75}, {0x2B96, 0x2B97}, {0x2BBA, 0x2BBC}, + {0x2BC9, 0x2BC9}, {0x2BD2, 0x2BEB}, {0x2BF0, 0x2BFF}, + {0x2C2F, 0x2C2F}, {0x2C5F, 0x2C5F}, {0x2CF4, 0x2CF8}, + {0x2D26, 0x2D26}, {0x2D28, 0x2D2C}, {0x2D2E, 0x2D2F}, + {0x2D68, 0x2D6E}, {0x2D71, 0x2D7E}, {0x2D97, 0x2D9F}, + {0x2DA7, 0x2DA7}, {0x2DAF, 0x2DAF}, {0x2DB7, 0x2DB7}, + {0x2DBF, 0x2DBF}, {0x2DC7, 0x2DC7}, {0x2DCF, 0x2DCF}, + {0x2DD7, 0x2DD7}, {0x2DDF, 0x2DDF}, {0x2E45, 0x2E7F}, + {0x2E9A, 0x2E9A}, {0x2EF4, 0x2EFF}, {0x2FD6, 0x2FEF}, + {0x2FFC, 0x2FFF}, {0x3040, 0x3040}, {0x3097, 0x3098}, + {0x3100, 0x3104}, {0x312E, 0x3130}, {0x318F, 0x318F}, + {0x31BB, 0x31BF}, {0x31E4, 0x31EF}, {0x321F, 0x321F}, + {0x32FF, 0x32FF}, {0x4DB6, 0x4DBF}, {0x9FD6, 0x9FFF}, + {0xA48D, 0xA48F}, {0xA4C7, 0xA4CF}, {0xA62C, 0xA63F}, + {0xA6F8, 0xA6FF}, {0xA7AF, 0xA7AF}, {0xA7B8, 0xA7F6}, + {0xA82C, 0xA82F}, {0xA83A, 0xA83F}, {0xA878, 0xA87F}, + {0xA8C6, 0xA8CD}, {0xA8DA, 0xA8DF}, {0xA8FE, 0xA8FF}, + {0xA954, 0xA95E}, {0xA97D, 0xA97F}, {0xA9CE, 0xA9CE}, + {0xA9DA, 0xA9DD}, {0xA9FF, 0xA9FF}, {0xAA37, 0xAA3F}, + {0xAA4E, 0xAA4F}, {0xAA5A, 0xAA5B}, {0xAAC3, 0xAADA}, + {0xAAF7, 0xAB00}, {0xAB07, 0xAB08}, {0xAB0F, 0xAB10}, + {0xAB17, 0xAB1F}, {0xAB27, 0xAB27}, {0xAB2F, 0xAB2F}, + {0xAB66, 0xAB6F}, {0xABEE, 0xABEF}, {0xABFA, 0xABFF}, + {0xD7A4, 0xD7AF}, {0xD7C7, 0xD7CA}, {0xD7FC, 0xD7FF}, + {0xFA6E, 0xFA6F}, {0xFADA, 0xFAFF}, {0xFB07, 0xFB12}, + {0xFB18, 0xFB1C}, {0xFB37, 0xFB37}, {0xFB3D, 0xFB3D}, + {0xFB3F, 0xFB3F}, {0xFB42, 0xFB42}, {0xFB45, 0xFB45}, + {0xFBC2, 0xFBD2}, {0xFD40, 0xFD4F}, {0xFD90, 0xFD91}, + {0xFDC8, 0xFDEF}, {0xFDFE, 0xFDFF}, {0xFE1A, 0xFE1F}, + {0xFE53, 0xFE53}, {0xFE67, 0xFE67}, {0xFE6C, 0xFE6F}, + {0xFE75, 0xFE75}, {0xFEFD, 0xFEFE}, {0xFF00, 0xFF00}, + {0xFFBF, 0xFFC1}, {0xFFC8, 0xFFC9}, {0xFFD0, 0xFFD1}, + {0xFFD8, 0xFFD9}, {0xFFDD, 0xFFDF}, {0xFFE7, 0xFFE7}, + {0xFFEF, 0xFFF8}, {0xFFFE, 0xFFFF}, {0x1000C, 0x1000C}, + {0x10027, 0x10027}, {0x1003B, 0x1003B}, {0x1003E, 0x1003E}, + {0x1004E, 0x1004F}, {0x1005E, 0x1007F}, {0x100FB, 0x100FF}, + {0x10103, 0x10106}, {0x10134, 0x10136}, {0x1018F, 0x1018F}, + {0x1019C, 0x1019F}, {0x101A1, 0x101CF}, {0x101FE, 0x1027F}, + {0x1029D, 0x1029F}, {0x102D1, 0x102DF}, {0x102FC, 0x102FF}, + {0x10324, 0x1032F}, {0x1034B, 0x1034F}, {0x1037B, 0x1037F}, + {0x1039E, 0x1039E}, {0x103C4, 0x103C7}, {0x103D6, 0x103FF}, + {0x1049E, 0x1049F}, {0x104AA, 0x104AF}, {0x104D4, 0x104D7}, + {0x104FC, 0x104FF}, {0x10528, 0x1052F}, {0x10564, 0x1056E}, + {0x10570, 0x105FF}, {0x10737, 0x1073F}, {0x10756, 0x1075F}, + {0x10768, 0x107FF}, {0x10806, 0x10807}, {0x10809, 0x10809}, + {0x10836, 0x10836}, {0x10839, 0x1083B}, {0x1083D, 0x1083E}, + {0x10856, 0x10856}, {0x1089F, 0x108A6}, {0x108B0, 0x108DF}, + {0x108F3, 0x108F3}, {0x108F6, 0x108FA}, {0x1091C, 0x1091E}, + {0x1093A, 0x1093E}, {0x10940, 0x1097F}, {0x109B8, 0x109BB}, + {0x109D0, 0x109D1}, {0x10A04, 0x10A04}, {0x10A07, 0x10A0B}, + {0x10A14, 0x10A14}, {0x10A18, 0x10A18}, {0x10A34, 0x10A37}, + {0x10A3B, 0x10A3E}, {0x10A48, 0x10A4F}, {0x10A59, 0x10A5F}, + {0x10AA0, 0x10ABF}, {0x10AE7, 0x10AEA}, {0x10AF7, 0x10AFF}, + {0x10B36, 0x10B38}, {0x10B56, 0x10B57}, {0x10B73, 0x10B77}, + {0x10B92, 0x10B98}, {0x10B9D, 0x10BA8}, {0x10BB0, 0x10BFF}, + {0x10C49, 0x10C7F}, {0x10CB3, 0x10CBF}, {0x10CF3, 0x10CF9}, + {0x10D00, 0x10E5F}, {0x10E7F, 0x10FFF}, {0x1104E, 0x11051}, + {0x11070, 0x1107E}, {0x110C2, 0x110CF}, {0x110E9, 0x110EF}, + {0x110FA, 0x110FF}, {0x11135, 0x11135}, {0x11144, 0x1114F}, + {0x11177, 0x1117F}, {0x111CE, 0x111CF}, {0x111E0, 0x111E0}, + {0x111F5, 0x111FF}, {0x11212, 0x11212}, {0x1123F, 0x1127F}, + {0x11287, 0x11287}, {0x11289, 0x11289}, {0x1128E, 0x1128E}, + {0x1129E, 0x1129E}, {0x112AA, 0x112AF}, {0x112EB, 0x112EF}, + {0x112FA, 0x112FF}, {0x11304, 0x11304}, {0x1130D, 0x1130E}, + {0x11311, 0x11312}, {0x11329, 0x11329}, {0x11331, 0x11331}, + {0x11334, 0x11334}, {0x1133A, 0x1133B}, {0x11345, 0x11346}, + {0x11349, 0x1134A}, {0x1134E, 0x1134F}, {0x11351, 0x11356}, + {0x11358, 0x1135C}, {0x11364, 0x11365}, {0x1136D, 0x1136F}, + {0x11375, 0x113FF}, {0x1145A, 0x1145A}, {0x1145C, 0x1145C}, + {0x1145E, 0x1147F}, {0x114C8, 0x114CF}, {0x114DA, 0x1157F}, + {0x115B6, 0x115B7}, {0x115DE, 0x115FF}, {0x11645, 0x1164F}, + {0x1165A, 0x1165F}, {0x1166D, 0x1167F}, {0x116B8, 0x116BF}, + {0x116CA, 0x116FF}, {0x1171A, 0x1171C}, {0x1172C, 0x1172F}, + {0x11740, 0x1189F}, {0x118F3, 0x118FE}, {0x11900, 0x11ABF}, + {0x11AF9, 0x11BFF}, {0x11C09, 0x11C09}, {0x11C37, 0x11C37}, + {0x11C46, 0x11C4F}, {0x11C6D, 0x11C6F}, {0x11C90, 0x11C91}, + {0x11CA8, 0x11CA8}, {0x11CB7, 0x11FFF}, {0x1239A, 0x123FF}, + {0x1246F, 0x1246F}, {0x12475, 0x1247F}, {0x12544, 0x12FFF}, + {0x1342F, 0x143FF}, {0x14647, 0x167FF}, {0x16A39, 0x16A3F}, + {0x16A5F, 0x16A5F}, {0x16A6A, 0x16A6D}, {0x16A70, 0x16ACF}, + {0x16AEE, 0x16AEF}, {0x16AF6, 0x16AFF}, {0x16B46, 0x16B4F}, + {0x16B5A, 0x16B5A}, {0x16B62, 0x16B62}, {0x16B78, 0x16B7C}, + {0x16B90, 0x16EFF}, {0x16F45, 0x16F4F}, {0x16F7F, 0x16F8E}, + {0x16FA0, 0x16FDF}, {0x16FE1, 0x16FFF}, {0x187ED, 0x187FF}, + {0x18AF3, 0x1AFFF}, {0x1B002, 0x1BBFF}, {0x1BC6B, 0x1BC6F}, + {0x1BC7D, 0x1BC7F}, {0x1BC89, 0x1BC8F}, {0x1BC9A, 0x1BC9B}, + {0x1BCA4, 0x1CFFF}, {0x1D0F6, 0x1D0FF}, {0x1D127, 0x1D128}, + {0x1D1E9, 0x1D1FF}, {0x1D246, 0x1D2FF}, {0x1D357, 0x1D35F}, + {0x1D372, 0x1D3FF}, {0x1D455, 0x1D455}, {0x1D49D, 0x1D49D}, + {0x1D4A0, 0x1D4A1}, {0x1D4A3, 0x1D4A4}, {0x1D4A7, 0x1D4A8}, + {0x1D4AD, 0x1D4AD}, {0x1D4BA, 0x1D4BA}, {0x1D4BC, 0x1D4BC}, + {0x1D4C4, 0x1D4C4}, {0x1D506, 0x1D506}, {0x1D50B, 0x1D50C}, + {0x1D515, 0x1D515}, {0x1D51D, 0x1D51D}, {0x1D53A, 0x1D53A}, + {0x1D53F, 0x1D53F}, {0x1D545, 0x1D545}, {0x1D547, 0x1D549}, + {0x1D551, 0x1D551}, {0x1D6A6, 0x1D6A7}, {0x1D7CC, 0x1D7CD}, + {0x1DA8C, 0x1DA9A}, {0x1DAA0, 0x1DAA0}, {0x1DAB0, 0x1DFFF}, + {0x1E007, 0x1E007}, {0x1E019, 0x1E01A}, {0x1E022, 0x1E022}, + {0x1E025, 0x1E025}, {0x1E02B, 0x1E7FF}, {0x1E8C5, 0x1E8C6}, + {0x1E8D7, 0x1E8FF}, {0x1E94B, 0x1E94F}, {0x1E95A, 0x1E95D}, + {0x1E960, 0x1EDFF}, {0x1EE04, 0x1EE04}, {0x1EE20, 0x1EE20}, + {0x1EE23, 0x1EE23}, {0x1EE25, 0x1EE26}, {0x1EE28, 0x1EE28}, + {0x1EE33, 0x1EE33}, {0x1EE38, 0x1EE38}, {0x1EE3A, 0x1EE3A}, + {0x1EE3C, 0x1EE41}, {0x1EE43, 0x1EE46}, {0x1EE48, 0x1EE48}, + {0x1EE4A, 0x1EE4A}, {0x1EE4C, 0x1EE4C}, {0x1EE50, 0x1EE50}, + {0x1EE53, 0x1EE53}, {0x1EE55, 0x1EE56}, {0x1EE58, 0x1EE58}, + {0x1EE5A, 0x1EE5A}, {0x1EE5C, 0x1EE5C}, {0x1EE5E, 0x1EE5E}, + {0x1EE60, 0x1EE60}, {0x1EE63, 0x1EE63}, {0x1EE65, 0x1EE66}, + {0x1EE6B, 0x1EE6B}, {0x1EE73, 0x1EE73}, {0x1EE78, 0x1EE78}, + {0x1EE7D, 0x1EE7D}, {0x1EE7F, 0x1EE7F}, {0x1EE8A, 0x1EE8A}, + {0x1EE9C, 0x1EEA0}, {0x1EEA4, 0x1EEA4}, {0x1EEAA, 0x1EEAA}, + {0x1EEBC, 0x1EEEF}, {0x1EEF2, 0x1EFFF}, {0x1F02C, 0x1F02F}, + {0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0}, + {0x1F0D0, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F10D, 0x1F10F}, + {0x1F12F, 0x1F12F}, {0x1F16C, 0x1F16F}, {0x1F1AD, 0x1F1E5}, + {0x1F203, 0x1F20F}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F24F}, + {0x1F252, 0x1F2FF}, {0x1F6D3, 0x1F6DF}, {0x1F6ED, 0x1F6EF}, + {0x1F6F7, 0x1F6FF}, {0x1F774, 0x1F77F}, {0x1F7D5, 0x1F7FF}, + {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, + {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F90F}, {0x1F91F, 0x1F91F}, + {0x1F928, 0x1F92F}, {0x1F931, 0x1F932}, {0x1F93F, 0x1F93F}, + {0x1F94C, 0x1F94F}, {0x1F95F, 0x1F97F}, {0x1F992, 0x1F9BF}, + {0x1F9C1, 0x1FFFF}, {0x2A6D7, 0x2A6FF}, {0x2B735, 0x2B73F}, + {0x2B81E, 0x2B81F}, {0x2CEA2, 0x2F7FF}, {0x2FA1E, 0xE0000}, + {0xE0002, 0xE001F}, {0xE0080, 0xE00FF}, {0xE01F0, 0xEFFFF}, + {0xFFFFE, 0xFFFFF}, +} + +var neutral = table{ + {0x0000, 0x001F}, {0x007F, 0x007F}, {0x0080, 0x009F}, + {0x00A0, 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, 0x00FF}, + {0x0100, 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, 0x017F}, {0x0180, 0x01BA}, {0x01BB, 0x01BB}, + {0x01BC, 0x01BF}, {0x01C0, 0x01C3}, {0x01C4, 0x01CD}, + {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, {0x01D3, 0x01D3}, + {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, {0x01D9, 0x01D9}, + {0x01DB, 0x01DB}, {0x01DD, 0x024F}, {0x0250, 0x0250}, + {0x0252, 0x0260}, {0x0262, 0x0293}, {0x0294, 0x0294}, + {0x0295, 0x02AF}, {0x02B0, 0x02C1}, {0x02C2, 0x02C3}, + {0x02C5, 0x02C5}, {0x02C6, 0x02C6}, {0x02C8, 0x02C8}, + {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, {0x02D1, 0x02D1}, + {0x02D2, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, + {0x02E0, 0x02E4}, {0x02E5, 0x02EB}, {0x02EC, 0x02EC}, + {0x02ED, 0x02ED}, {0x02EE, 0x02EE}, {0x02EF, 0x02FF}, + {0x0370, 0x0373}, {0x0374, 0x0374}, {0x0375, 0x0375}, + {0x0376, 0x0377}, {0x037A, 0x037A}, {0x037B, 0x037D}, + {0x037E, 0x037E}, {0x037F, 0x037F}, {0x0384, 0x0385}, + {0x0386, 0x0386}, {0x0387, 0x0387}, {0x0388, 0x038A}, + {0x038C, 0x038C}, {0x038E, 0x0390}, {0x03AA, 0x03B0}, + {0x03C2, 0x03C2}, {0x03CA, 0x03F5}, {0x03F6, 0x03F6}, + {0x03F7, 0x03FF}, {0x0400, 0x0400}, {0x0402, 0x040F}, + {0x0450, 0x0450}, {0x0452, 0x0481}, {0x0482, 0x0482}, + {0x0483, 0x0487}, {0x0488, 0x0489}, {0x048A, 0x04FF}, + {0x0500, 0x052F}, {0x0531, 0x0556}, {0x0559, 0x0559}, + {0x055A, 0x055F}, {0x0561, 0x0587}, {0x0589, 0x0589}, + {0x058A, 0x058A}, {0x058D, 0x058E}, {0x058F, 0x058F}, + {0x0591, 0x05BD}, {0x05BE, 0x05BE}, {0x05BF, 0x05BF}, + {0x05C0, 0x05C0}, {0x05C1, 0x05C2}, {0x05C3, 0x05C3}, + {0x05C4, 0x05C5}, {0x05C6, 0x05C6}, {0x05C7, 0x05C7}, + {0x05D0, 0x05EA}, {0x05F0, 0x05F2}, {0x05F3, 0x05F4}, + {0x0600, 0x0605}, {0x0606, 0x0608}, {0x0609, 0x060A}, + {0x060B, 0x060B}, {0x060C, 0x060D}, {0x060E, 0x060F}, + {0x0610, 0x061A}, {0x061B, 0x061B}, {0x061C, 0x061C}, + {0x061E, 0x061F}, {0x0620, 0x063F}, {0x0640, 0x0640}, + {0x0641, 0x064A}, {0x064B, 0x065F}, {0x0660, 0x0669}, + {0x066A, 0x066D}, {0x066E, 0x066F}, {0x0670, 0x0670}, + {0x0671, 0x06D3}, {0x06D4, 0x06D4}, {0x06D5, 0x06D5}, + {0x06D6, 0x06DC}, {0x06DD, 0x06DD}, {0x06DE, 0x06DE}, + {0x06DF, 0x06E4}, {0x06E5, 0x06E6}, {0x06E7, 0x06E8}, + {0x06E9, 0x06E9}, {0x06EA, 0x06ED}, {0x06EE, 0x06EF}, + {0x06F0, 0x06F9}, {0x06FA, 0x06FC}, {0x06FD, 0x06FE}, + {0x06FF, 0x06FF}, {0x0700, 0x070D}, {0x070F, 0x070F}, + {0x0710, 0x0710}, {0x0711, 0x0711}, {0x0712, 0x072F}, + {0x0730, 0x074A}, {0x074D, 0x074F}, {0x0750, 0x077F}, + {0x0780, 0x07A5}, {0x07A6, 0x07B0}, {0x07B1, 0x07B1}, + {0x07C0, 0x07C9}, {0x07CA, 0x07EA}, {0x07EB, 0x07F3}, + {0x07F4, 0x07F5}, {0x07F6, 0x07F6}, {0x07F7, 0x07F9}, + {0x07FA, 0x07FA}, {0x0800, 0x0815}, {0x0816, 0x0819}, + {0x081A, 0x081A}, {0x081B, 0x0823}, {0x0824, 0x0824}, + {0x0825, 0x0827}, {0x0828, 0x0828}, {0x0829, 0x082D}, + {0x0830, 0x083E}, {0x0840, 0x0858}, {0x0859, 0x085B}, + {0x085E, 0x085E}, {0x08A0, 0x08B4}, {0x08B6, 0x08BD}, + {0x08D4, 0x08E1}, {0x08E2, 0x08E2}, {0x08E3, 0x08FF}, + {0x0900, 0x0902}, {0x0903, 0x0903}, {0x0904, 0x0939}, + {0x093A, 0x093A}, {0x093B, 0x093B}, {0x093C, 0x093C}, + {0x093D, 0x093D}, {0x093E, 0x0940}, {0x0941, 0x0948}, + {0x0949, 0x094C}, {0x094D, 0x094D}, {0x094E, 0x094F}, + {0x0950, 0x0950}, {0x0951, 0x0957}, {0x0958, 0x0961}, + {0x0962, 0x0963}, {0x0964, 0x0965}, {0x0966, 0x096F}, + {0x0970, 0x0970}, {0x0971, 0x0971}, {0x0972, 0x097F}, + {0x0980, 0x0980}, {0x0981, 0x0981}, {0x0982, 0x0983}, + {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, + {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, + {0x09BC, 0x09BC}, {0x09BD, 0x09BD}, {0x09BE, 0x09C0}, + {0x09C1, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CC}, + {0x09CD, 0x09CD}, {0x09CE, 0x09CE}, {0x09D7, 0x09D7}, + {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, {0x09E2, 0x09E3}, + {0x09E6, 0x09EF}, {0x09F0, 0x09F1}, {0x09F2, 0x09F3}, + {0x09F4, 0x09F9}, {0x09FA, 0x09FA}, {0x09FB, 0x09FB}, + {0x0A01, 0x0A02}, {0x0A03, 0x0A03}, {0x0A05, 0x0A0A}, + {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, + {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, + {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A40}, {0x0A41, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, + {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A6F}, + {0x0A70, 0x0A71}, {0x0A72, 0x0A74}, {0x0A75, 0x0A75}, + {0x0A81, 0x0A82}, {0x0A83, 0x0A83}, {0x0A85, 0x0A8D}, + {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, + {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0ABC}, + {0x0ABD, 0x0ABD}, {0x0ABE, 0x0AC0}, {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, {0x0AC9, 0x0AC9}, {0x0ACB, 0x0ACC}, + {0x0ACD, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE1}, + {0x0AE2, 0x0AE3}, {0x0AE6, 0x0AEF}, {0x0AF0, 0x0AF0}, + {0x0AF1, 0x0AF1}, {0x0AF9, 0x0AF9}, {0x0B01, 0x0B01}, + {0x0B02, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, + {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, + {0x0B35, 0x0B39}, {0x0B3C, 0x0B3C}, {0x0B3D, 0x0B3D}, + {0x0B3E, 0x0B3E}, {0x0B3F, 0x0B3F}, {0x0B40, 0x0B40}, + {0x0B41, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4C}, + {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B57, 0x0B57}, + {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B62, 0x0B63}, + {0x0B66, 0x0B6F}, {0x0B70, 0x0B70}, {0x0B71, 0x0B71}, + {0x0B72, 0x0B77}, {0x0B82, 0x0B82}, {0x0B83, 0x0B83}, + {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, + {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, + {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, + {0x0BBE, 0x0BBF}, {0x0BC0, 0x0BC0}, {0x0BC1, 0x0BC2}, + {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCC}, {0x0BCD, 0x0BCD}, + {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BEF}, + {0x0BF0, 0x0BF2}, {0x0BF3, 0x0BF8}, {0x0BF9, 0x0BF9}, + {0x0BFA, 0x0BFA}, {0x0C00, 0x0C00}, {0x0C01, 0x0C03}, + {0x0C05, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, + {0x0C2A, 0x0C39}, {0x0C3D, 0x0C3D}, {0x0C3E, 0x0C40}, + {0x0C41, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C61}, + {0x0C62, 0x0C63}, {0x0C66, 0x0C6F}, {0x0C78, 0x0C7E}, + {0x0C7F, 0x0C7F}, {0x0C80, 0x0C80}, {0x0C81, 0x0C81}, + {0x0C82, 0x0C83}, {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90}, + {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CBC, 0x0CBC}, {0x0CBD, 0x0CBD}, {0x0CBE, 0x0CBE}, + {0x0CBF, 0x0CBF}, {0x0CC0, 0x0CC4}, {0x0CC6, 0x0CC6}, + {0x0CC7, 0x0CC8}, {0x0CCA, 0x0CCB}, {0x0CCC, 0x0CCD}, + {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE1}, + {0x0CE2, 0x0CE3}, {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, + {0x0D01, 0x0D01}, {0x0D02, 0x0D03}, {0x0D05, 0x0D0C}, + {0x0D0E, 0x0D10}, {0x0D12, 0x0D3A}, {0x0D3D, 0x0D3D}, + {0x0D3E, 0x0D40}, {0x0D41, 0x0D44}, {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4C}, {0x0D4D, 0x0D4D}, {0x0D4E, 0x0D4E}, + {0x0D4F, 0x0D4F}, {0x0D54, 0x0D56}, {0x0D57, 0x0D57}, + {0x0D58, 0x0D5E}, {0x0D5F, 0x0D61}, {0x0D62, 0x0D63}, + {0x0D66, 0x0D6F}, {0x0D70, 0x0D78}, {0x0D79, 0x0D79}, + {0x0D7A, 0x0D7F}, {0x0D82, 0x0D83}, {0x0D85, 0x0D96}, + {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, + {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD1}, + {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, + {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF3}, {0x0DF4, 0x0DF4}, + {0x0E01, 0x0E30}, {0x0E31, 0x0E31}, {0x0E32, 0x0E33}, + {0x0E34, 0x0E3A}, {0x0E3F, 0x0E3F}, {0x0E40, 0x0E45}, + {0x0E46, 0x0E46}, {0x0E47, 0x0E4E}, {0x0E4F, 0x0E4F}, + {0x0E50, 0x0E59}, {0x0E5A, 0x0E5B}, {0x0E81, 0x0E82}, + {0x0E84, 0x0E84}, {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, + {0x0E8D, 0x0E8D}, {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, + {0x0EA1, 0x0EA3}, {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EA7}, + {0x0EAA, 0x0EAB}, {0x0EAD, 0x0EB0}, {0x0EB1, 0x0EB1}, + {0x0EB2, 0x0EB3}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, + {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, + {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF}, + {0x0F00, 0x0F00}, {0x0F01, 0x0F03}, {0x0F04, 0x0F12}, + {0x0F13, 0x0F13}, {0x0F14, 0x0F14}, {0x0F15, 0x0F17}, + {0x0F18, 0x0F19}, {0x0F1A, 0x0F1F}, {0x0F20, 0x0F29}, + {0x0F2A, 0x0F33}, {0x0F34, 0x0F34}, {0x0F35, 0x0F35}, + {0x0F36, 0x0F36}, {0x0F37, 0x0F37}, {0x0F38, 0x0F38}, + {0x0F39, 0x0F39}, {0x0F3A, 0x0F3A}, {0x0F3B, 0x0F3B}, + {0x0F3C, 0x0F3C}, {0x0F3D, 0x0F3D}, {0x0F3E, 0x0F3F}, + {0x0F40, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F71, 0x0F7E}, + {0x0F7F, 0x0F7F}, {0x0F80, 0x0F84}, {0x0F85, 0x0F85}, + {0x0F86, 0x0F87}, {0x0F88, 0x0F8C}, {0x0F8D, 0x0F97}, + {0x0F99, 0x0FBC}, {0x0FBE, 0x0FC5}, {0x0FC6, 0x0FC6}, + {0x0FC7, 0x0FCC}, {0x0FCE, 0x0FCF}, {0x0FD0, 0x0FD4}, + {0x0FD5, 0x0FD8}, {0x0FD9, 0x0FDA}, {0x1000, 0x102A}, + {0x102B, 0x102C}, {0x102D, 0x1030}, {0x1031, 0x1031}, + {0x1032, 0x1037}, {0x1038, 0x1038}, {0x1039, 0x103A}, + {0x103B, 0x103C}, {0x103D, 0x103E}, {0x103F, 0x103F}, + {0x1040, 0x1049}, {0x104A, 0x104F}, {0x1050, 0x1055}, + {0x1056, 0x1057}, {0x1058, 0x1059}, {0x105A, 0x105D}, + {0x105E, 0x1060}, {0x1061, 0x1061}, {0x1062, 0x1064}, + {0x1065, 0x1066}, {0x1067, 0x106D}, {0x106E, 0x1070}, + {0x1071, 0x1074}, {0x1075, 0x1081}, {0x1082, 0x1082}, + {0x1083, 0x1084}, {0x1085, 0x1086}, {0x1087, 0x108C}, + {0x108D, 0x108D}, {0x108E, 0x108E}, {0x108F, 0x108F}, + {0x1090, 0x1099}, {0x109A, 0x109C}, {0x109D, 0x109D}, + {0x109E, 0x109F}, {0x10A0, 0x10C5}, {0x10C7, 0x10C7}, + {0x10CD, 0x10CD}, {0x10D0, 0x10FA}, {0x10FB, 0x10FB}, + {0x10FC, 0x10FC}, {0x10FD, 0x10FF}, {0x1160, 0x11FF}, + {0x1200, 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, 0x135F}, {0x1360, 0x1368}, + {0x1369, 0x137C}, {0x1380, 0x138F}, {0x1390, 0x1399}, + {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1400, 0x1400}, + {0x1401, 0x166C}, {0x166D, 0x166E}, {0x166F, 0x167F}, + {0x1680, 0x1680}, {0x1681, 0x169A}, {0x169B, 0x169B}, + {0x169C, 0x169C}, {0x16A0, 0x16EA}, {0x16EB, 0x16ED}, + {0x16EE, 0x16F0}, {0x16F1, 0x16F8}, {0x1700, 0x170C}, + {0x170E, 0x1711}, {0x1712, 0x1714}, {0x1720, 0x1731}, + {0x1732, 0x1734}, {0x1735, 0x1736}, {0x1740, 0x1751}, + {0x1752, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770}, + {0x1772, 0x1773}, {0x1780, 0x17B3}, {0x17B4, 0x17B5}, + {0x17B6, 0x17B6}, {0x17B7, 0x17BD}, {0x17BE, 0x17C5}, + {0x17C6, 0x17C6}, {0x17C7, 0x17C8}, {0x17C9, 0x17D3}, + {0x17D4, 0x17D6}, {0x17D7, 0x17D7}, {0x17D8, 0x17DA}, + {0x17DB, 0x17DB}, {0x17DC, 0x17DC}, {0x17DD, 0x17DD}, + {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x1805}, + {0x1806, 0x1806}, {0x1807, 0x180A}, {0x180B, 0x180D}, + {0x180E, 0x180E}, {0x1810, 0x1819}, {0x1820, 0x1842}, + {0x1843, 0x1843}, {0x1844, 0x1877}, {0x1880, 0x1884}, + {0x1885, 0x1886}, {0x1887, 0x18A8}, {0x18A9, 0x18A9}, + {0x18AA, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E}, + {0x1920, 0x1922}, {0x1923, 0x1926}, {0x1927, 0x1928}, + {0x1929, 0x192B}, {0x1930, 0x1931}, {0x1932, 0x1932}, + {0x1933, 0x1938}, {0x1939, 0x193B}, {0x1940, 0x1940}, + {0x1944, 0x1945}, {0x1946, 0x194F}, {0x1950, 0x196D}, + {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, + {0x19D0, 0x19D9}, {0x19DA, 0x19DA}, {0x19DE, 0x19DF}, + {0x19E0, 0x19FF}, {0x1A00, 0x1A16}, {0x1A17, 0x1A18}, + {0x1A19, 0x1A1A}, {0x1A1B, 0x1A1B}, {0x1A1E, 0x1A1F}, + {0x1A20, 0x1A54}, {0x1A55, 0x1A55}, {0x1A56, 0x1A56}, + {0x1A57, 0x1A57}, {0x1A58, 0x1A5E}, {0x1A60, 0x1A60}, + {0x1A61, 0x1A61}, {0x1A62, 0x1A62}, {0x1A63, 0x1A64}, + {0x1A65, 0x1A6C}, {0x1A6D, 0x1A72}, {0x1A73, 0x1A7C}, + {0x1A7F, 0x1A7F}, {0x1A80, 0x1A89}, {0x1A90, 0x1A99}, + {0x1AA0, 0x1AA6}, {0x1AA7, 0x1AA7}, {0x1AA8, 0x1AAD}, + {0x1AB0, 0x1ABD}, {0x1ABE, 0x1ABE}, {0x1B00, 0x1B03}, + {0x1B04, 0x1B04}, {0x1B05, 0x1B33}, {0x1B34, 0x1B34}, + {0x1B35, 0x1B35}, {0x1B36, 0x1B3A}, {0x1B3B, 0x1B3B}, + {0x1B3C, 0x1B3C}, {0x1B3D, 0x1B41}, {0x1B42, 0x1B42}, + {0x1B43, 0x1B44}, {0x1B45, 0x1B4B}, {0x1B50, 0x1B59}, + {0x1B5A, 0x1B60}, {0x1B61, 0x1B6A}, {0x1B6B, 0x1B73}, + {0x1B74, 0x1B7C}, {0x1B80, 0x1B81}, {0x1B82, 0x1B82}, + {0x1B83, 0x1BA0}, {0x1BA1, 0x1BA1}, {0x1BA2, 0x1BA5}, + {0x1BA6, 0x1BA7}, {0x1BA8, 0x1BA9}, {0x1BAA, 0x1BAA}, + {0x1BAB, 0x1BAD}, {0x1BAE, 0x1BAF}, {0x1BB0, 0x1BB9}, + {0x1BBA, 0x1BBF}, {0x1BC0, 0x1BE5}, {0x1BE6, 0x1BE6}, + {0x1BE7, 0x1BE7}, {0x1BE8, 0x1BE9}, {0x1BEA, 0x1BEC}, + {0x1BED, 0x1BED}, {0x1BEE, 0x1BEE}, {0x1BEF, 0x1BF1}, + {0x1BF2, 0x1BF3}, {0x1BFC, 0x1BFF}, {0x1C00, 0x1C23}, + {0x1C24, 0x1C2B}, {0x1C2C, 0x1C33}, {0x1C34, 0x1C35}, + {0x1C36, 0x1C37}, {0x1C3B, 0x1C3F}, {0x1C40, 0x1C49}, + {0x1C4D, 0x1C4F}, {0x1C50, 0x1C59}, {0x1C5A, 0x1C77}, + {0x1C78, 0x1C7D}, {0x1C7E, 0x1C7F}, {0x1C80, 0x1C88}, + {0x1CC0, 0x1CC7}, {0x1CD0, 0x1CD2}, {0x1CD3, 0x1CD3}, + {0x1CD4, 0x1CE0}, {0x1CE1, 0x1CE1}, {0x1CE2, 0x1CE8}, + {0x1CE9, 0x1CEC}, {0x1CED, 0x1CED}, {0x1CEE, 0x1CF1}, + {0x1CF2, 0x1CF3}, {0x1CF4, 0x1CF4}, {0x1CF5, 0x1CF6}, + {0x1CF8, 0x1CF9}, {0x1D00, 0x1D2B}, {0x1D2C, 0x1D6A}, + {0x1D6B, 0x1D77}, {0x1D78, 0x1D78}, {0x1D79, 0x1D7F}, + {0x1D80, 0x1D9A}, {0x1D9B, 0x1DBF}, {0x1DC0, 0x1DF5}, + {0x1DFB, 0x1DFF}, {0x1E00, 0x1EFF}, {0x1F00, 0x1F15}, + {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, + {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, + {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, + {0x1FB6, 0x1FBC}, {0x1FBD, 0x1FBD}, {0x1FBE, 0x1FBE}, + {0x1FBF, 0x1FC1}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, + {0x1FCD, 0x1FCF}, {0x1FD0, 0x1FD3}, {0x1FD6, 0x1FDB}, + {0x1FDD, 0x1FDF}, {0x1FE0, 0x1FEC}, {0x1FED, 0x1FEF}, + {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x1FFD, 0x1FFE}, + {0x2000, 0x200A}, {0x200B, 0x200F}, {0x2011, 0x2012}, + {0x2017, 0x2017}, {0x201A, 0x201A}, {0x201B, 0x201B}, + {0x201E, 0x201E}, {0x201F, 0x201F}, {0x2023, 0x2023}, + {0x2028, 0x2028}, {0x2029, 0x2029}, {0x202A, 0x202E}, + {0x202F, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034}, + {0x2036, 0x2038}, {0x2039, 0x2039}, {0x203A, 0x203A}, + {0x203C, 0x203D}, {0x203F, 0x2040}, {0x2041, 0x2043}, + {0x2044, 0x2044}, {0x2045, 0x2045}, {0x2046, 0x2046}, + {0x2047, 0x2051}, {0x2052, 0x2052}, {0x2053, 0x2053}, + {0x2054, 0x2054}, {0x2055, 0x205E}, {0x205F, 0x205F}, + {0x2060, 0x2064}, {0x2066, 0x206F}, {0x2070, 0x2070}, + {0x2071, 0x2071}, {0x2075, 0x2079}, {0x207A, 0x207C}, + {0x207D, 0x207D}, {0x207E, 0x207E}, {0x2080, 0x2080}, + {0x2085, 0x2089}, {0x208A, 0x208C}, {0x208D, 0x208D}, + {0x208E, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8}, + {0x20AA, 0x20AB}, {0x20AD, 0x20BE}, {0x20D0, 0x20DC}, + {0x20DD, 0x20E0}, {0x20E1, 0x20E1}, {0x20E2, 0x20E4}, + {0x20E5, 0x20F0}, {0x2100, 0x2101}, {0x2102, 0x2102}, + {0x2104, 0x2104}, {0x2106, 0x2106}, {0x2107, 0x2107}, + {0x2108, 0x2108}, {0x210A, 0x2112}, {0x2114, 0x2114}, + {0x2115, 0x2115}, {0x2117, 0x2117}, {0x2118, 0x2118}, + {0x2119, 0x211D}, {0x211E, 0x2120}, {0x2123, 0x2123}, + {0x2124, 0x2124}, {0x2125, 0x2125}, {0x2127, 0x2127}, + {0x2128, 0x2128}, {0x2129, 0x2129}, {0x212A, 0x212A}, + {0x212C, 0x212D}, {0x212E, 0x212E}, {0x212F, 0x2134}, + {0x2135, 0x2138}, {0x2139, 0x2139}, {0x213A, 0x213B}, + {0x213C, 0x213F}, {0x2140, 0x2144}, {0x2145, 0x2149}, + {0x214A, 0x214A}, {0x214B, 0x214B}, {0x214C, 0x214D}, + {0x214E, 0x214E}, {0x214F, 0x214F}, {0x2150, 0x2152}, + {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F}, + {0x217A, 0x2182}, {0x2183, 0x2184}, {0x2185, 0x2188}, + {0x218A, 0x218B}, {0x219A, 0x219B}, {0x219C, 0x219F}, + {0x21A0, 0x21A0}, {0x21A1, 0x21A2}, {0x21A3, 0x21A3}, + {0x21A4, 0x21A5}, {0x21A6, 0x21A6}, {0x21A7, 0x21AD}, + {0x21AE, 0x21AE}, {0x21AF, 0x21B7}, {0x21BA, 0x21CD}, + {0x21CE, 0x21CF}, {0x21D0, 0x21D1}, {0x21D3, 0x21D3}, + {0x21D5, 0x21E6}, {0x21E8, 0x21F3}, {0x21F4, 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, 0x22FF}, {0x2300, 0x2307}, {0x2308, 0x2308}, + {0x2309, 0x2309}, {0x230A, 0x230A}, {0x230B, 0x230B}, + {0x230C, 0x2311}, {0x2313, 0x2319}, {0x231C, 0x231F}, + {0x2320, 0x2321}, {0x2322, 0x2328}, {0x232B, 0x237B}, + {0x237C, 0x237C}, {0x237D, 0x239A}, {0x239B, 0x23B3}, + {0x23B4, 0x23DB}, {0x23DC, 0x23E1}, {0x23E2, 0x23E8}, + {0x23ED, 0x23EF}, {0x23F1, 0x23F2}, {0x23F4, 0x23FE}, + {0x2400, 0x2426}, {0x2440, 0x244A}, {0x24EA, 0x24EA}, + {0x254C, 0x254F}, {0x2574, 0x257F}, {0x2590, 0x2591}, + {0x2596, 0x259F}, {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, + {0x25B4, 0x25B5}, {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, + {0x25C2, 0x25C5}, {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, + {0x25D2, 0x25E1}, {0x25E6, 0x25EE}, {0x25F0, 0x25F7}, + {0x25F8, 0x25FC}, {0x25FF, 0x25FF}, {0x2600, 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, 0x2767}, {0x2768, 0x2768}, {0x2769, 0x2769}, + {0x276A, 0x276A}, {0x276B, 0x276B}, {0x276C, 0x276C}, + {0x276D, 0x276D}, {0x276E, 0x276E}, {0x276F, 0x276F}, + {0x2770, 0x2770}, {0x2771, 0x2771}, {0x2772, 0x2772}, + {0x2773, 0x2773}, {0x2774, 0x2774}, {0x2775, 0x2775}, + {0x2780, 0x2793}, {0x2794, 0x2794}, {0x2798, 0x27AF}, + {0x27B1, 0x27BE}, {0x27C0, 0x27C4}, {0x27C5, 0x27C5}, + {0x27C6, 0x27C6}, {0x27C7, 0x27E5}, {0x27EE, 0x27EE}, + {0x27EF, 0x27EF}, {0x27F0, 0x27FF}, {0x2800, 0x28FF}, + {0x2900, 0x297F}, {0x2980, 0x2982}, {0x2983, 0x2983}, + {0x2984, 0x2984}, {0x2987, 0x2987}, {0x2988, 0x2988}, + {0x2989, 0x2989}, {0x298A, 0x298A}, {0x298B, 0x298B}, + {0x298C, 0x298C}, {0x298D, 0x298D}, {0x298E, 0x298E}, + {0x298F, 0x298F}, {0x2990, 0x2990}, {0x2991, 0x2991}, + {0x2992, 0x2992}, {0x2993, 0x2993}, {0x2994, 0x2994}, + {0x2995, 0x2995}, {0x2996, 0x2996}, {0x2997, 0x2997}, + {0x2998, 0x2998}, {0x2999, 0x29D7}, {0x29D8, 0x29D8}, + {0x29D9, 0x29D9}, {0x29DA, 0x29DA}, {0x29DB, 0x29DB}, + {0x29DC, 0x29FB}, {0x29FC, 0x29FC}, {0x29FD, 0x29FD}, + {0x29FE, 0x29FF}, {0x2A00, 0x2AFF}, {0x2B00, 0x2B1A}, + {0x2B1D, 0x2B2F}, {0x2B30, 0x2B44}, {0x2B45, 0x2B46}, + {0x2B47, 0x2B4C}, {0x2B4D, 0x2B4F}, {0x2B51, 0x2B54}, + {0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B98, 0x2BB9}, + {0x2BBD, 0x2BC8}, {0x2BCA, 0x2BD1}, {0x2BEC, 0x2BEF}, + {0x2C00, 0x2C2E}, {0x2C30, 0x2C5E}, {0x2C60, 0x2C7B}, + {0x2C7C, 0x2C7D}, {0x2C7E, 0x2C7F}, {0x2C80, 0x2CE4}, + {0x2CE5, 0x2CEA}, {0x2CEB, 0x2CEE}, {0x2CEF, 0x2CF1}, + {0x2CF2, 0x2CF3}, {0x2CF9, 0x2CFC}, {0x2CFD, 0x2CFD}, + {0x2CFE, 0x2CFF}, {0x2D00, 0x2D25}, {0x2D27, 0x2D27}, + {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D6F}, + {0x2D70, 0x2D70}, {0x2D7F, 0x2D7F}, {0x2D80, 0x2D96}, + {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, + {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, + {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2DE0, 0x2DFF}, + {0x2E00, 0x2E01}, {0x2E02, 0x2E02}, {0x2E03, 0x2E03}, + {0x2E04, 0x2E04}, {0x2E05, 0x2E05}, {0x2E06, 0x2E08}, + {0x2E09, 0x2E09}, {0x2E0A, 0x2E0A}, {0x2E0B, 0x2E0B}, + {0x2E0C, 0x2E0C}, {0x2E0D, 0x2E0D}, {0x2E0E, 0x2E16}, + {0x2E17, 0x2E17}, {0x2E18, 0x2E19}, {0x2E1A, 0x2E1A}, + {0x2E1B, 0x2E1B}, {0x2E1C, 0x2E1C}, {0x2E1D, 0x2E1D}, + {0x2E1E, 0x2E1F}, {0x2E20, 0x2E20}, {0x2E21, 0x2E21}, + {0x2E22, 0x2E22}, {0x2E23, 0x2E23}, {0x2E24, 0x2E24}, + {0x2E25, 0x2E25}, {0x2E26, 0x2E26}, {0x2E27, 0x2E27}, + {0x2E28, 0x2E28}, {0x2E29, 0x2E29}, {0x2E2A, 0x2E2E}, + {0x2E2F, 0x2E2F}, {0x2E30, 0x2E39}, {0x2E3A, 0x2E3B}, + {0x2E3C, 0x2E3F}, {0x2E40, 0x2E40}, {0x2E41, 0x2E41}, + {0x2E42, 0x2E42}, {0x2E43, 0x2E44}, {0x303F, 0x303F}, + {0x4DC0, 0x4DFF}, {0xA4D0, 0xA4F7}, {0xA4F8, 0xA4FD}, + {0xA4FE, 0xA4FF}, {0xA500, 0xA60B}, {0xA60C, 0xA60C}, + {0xA60D, 0xA60F}, {0xA610, 0xA61F}, {0xA620, 0xA629}, + {0xA62A, 0xA62B}, {0xA640, 0xA66D}, {0xA66E, 0xA66E}, + {0xA66F, 0xA66F}, {0xA670, 0xA672}, {0xA673, 0xA673}, + {0xA674, 0xA67D}, {0xA67E, 0xA67E}, {0xA67F, 0xA67F}, + {0xA680, 0xA69B}, {0xA69C, 0xA69D}, {0xA69E, 0xA69F}, + {0xA6A0, 0xA6E5}, {0xA6E6, 0xA6EF}, {0xA6F0, 0xA6F1}, + {0xA6F2, 0xA6F7}, {0xA700, 0xA716}, {0xA717, 0xA71F}, + {0xA720, 0xA721}, {0xA722, 0xA76F}, {0xA770, 0xA770}, + {0xA771, 0xA787}, {0xA788, 0xA788}, {0xA789, 0xA78A}, + {0xA78B, 0xA78E}, {0xA78F, 0xA78F}, {0xA790, 0xA7AE}, + {0xA7B0, 0xA7B7}, {0xA7F7, 0xA7F7}, {0xA7F8, 0xA7F9}, + {0xA7FA, 0xA7FA}, {0xA7FB, 0xA7FF}, {0xA800, 0xA801}, + {0xA802, 0xA802}, {0xA803, 0xA805}, {0xA806, 0xA806}, + {0xA807, 0xA80A}, {0xA80B, 0xA80B}, {0xA80C, 0xA822}, + {0xA823, 0xA824}, {0xA825, 0xA826}, {0xA827, 0xA827}, + {0xA828, 0xA82B}, {0xA830, 0xA835}, {0xA836, 0xA837}, + {0xA838, 0xA838}, {0xA839, 0xA839}, {0xA840, 0xA873}, + {0xA874, 0xA877}, {0xA880, 0xA881}, {0xA882, 0xA8B3}, + {0xA8B4, 0xA8C3}, {0xA8C4, 0xA8C5}, {0xA8CE, 0xA8CF}, + {0xA8D0, 0xA8D9}, {0xA8E0, 0xA8F1}, {0xA8F2, 0xA8F7}, + {0xA8F8, 0xA8FA}, {0xA8FB, 0xA8FB}, {0xA8FC, 0xA8FC}, + {0xA8FD, 0xA8FD}, {0xA900, 0xA909}, {0xA90A, 0xA925}, + {0xA926, 0xA92D}, {0xA92E, 0xA92F}, {0xA930, 0xA946}, + {0xA947, 0xA951}, {0xA952, 0xA953}, {0xA95F, 0xA95F}, + {0xA980, 0xA982}, {0xA983, 0xA983}, {0xA984, 0xA9B2}, + {0xA9B3, 0xA9B3}, {0xA9B4, 0xA9B5}, {0xA9B6, 0xA9B9}, + {0xA9BA, 0xA9BB}, {0xA9BC, 0xA9BC}, {0xA9BD, 0xA9C0}, + {0xA9C1, 0xA9CD}, {0xA9CF, 0xA9CF}, {0xA9D0, 0xA9D9}, + {0xA9DE, 0xA9DF}, {0xA9E0, 0xA9E4}, {0xA9E5, 0xA9E5}, + {0xA9E6, 0xA9E6}, {0xA9E7, 0xA9EF}, {0xA9F0, 0xA9F9}, + {0xA9FA, 0xA9FE}, {0xAA00, 0xAA28}, {0xAA29, 0xAA2E}, + {0xAA2F, 0xAA30}, {0xAA31, 0xAA32}, {0xAA33, 0xAA34}, + {0xAA35, 0xAA36}, {0xAA40, 0xAA42}, {0xAA43, 0xAA43}, + {0xAA44, 0xAA4B}, {0xAA4C, 0xAA4C}, {0xAA4D, 0xAA4D}, + {0xAA50, 0xAA59}, {0xAA5C, 0xAA5F}, {0xAA60, 0xAA6F}, + {0xAA70, 0xAA70}, {0xAA71, 0xAA76}, {0xAA77, 0xAA79}, + {0xAA7A, 0xAA7A}, {0xAA7B, 0xAA7B}, {0xAA7C, 0xAA7C}, + {0xAA7D, 0xAA7D}, {0xAA7E, 0xAA7F}, {0xAA80, 0xAAAF}, + {0xAAB0, 0xAAB0}, {0xAAB1, 0xAAB1}, {0xAAB2, 0xAAB4}, + {0xAAB5, 0xAAB6}, {0xAAB7, 0xAAB8}, {0xAAB9, 0xAABD}, + {0xAABE, 0xAABF}, {0xAAC0, 0xAAC0}, {0xAAC1, 0xAAC1}, + {0xAAC2, 0xAAC2}, {0xAADB, 0xAADC}, {0xAADD, 0xAADD}, + {0xAADE, 0xAADF}, {0xAAE0, 0xAAEA}, {0xAAEB, 0xAAEB}, + {0xAAEC, 0xAAED}, {0xAAEE, 0xAAEF}, {0xAAF0, 0xAAF1}, + {0xAAF2, 0xAAF2}, {0xAAF3, 0xAAF4}, {0xAAF5, 0xAAF5}, + {0xAAF6, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, + {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, + {0xAB30, 0xAB5A}, {0xAB5B, 0xAB5B}, {0xAB5C, 0xAB5F}, + {0xAB60, 0xAB65}, {0xAB70, 0xABBF}, {0xABC0, 0xABE2}, + {0xABE3, 0xABE4}, {0xABE5, 0xABE5}, {0xABE6, 0xABE7}, + {0xABE8, 0xABE8}, {0xABE9, 0xABEA}, {0xABEB, 0xABEB}, + {0xABEC, 0xABEC}, {0xABED, 0xABED}, {0xABF0, 0xABF9}, + {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDB7F}, + {0xDB80, 0xDBFF}, {0xDC00, 0xDFFF}, {0xFB00, 0xFB06}, + {0xFB13, 0xFB17}, {0xFB1D, 0xFB1D}, {0xFB1E, 0xFB1E}, + {0xFB1F, 0xFB28}, {0xFB29, 0xFB29}, {0xFB2A, 0xFB36}, + {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, + {0xFB43, 0xFB44}, {0xFB46, 0xFB4F}, {0xFB50, 0xFBB1}, + {0xFBB2, 0xFBC1}, {0xFBD3, 0xFD3D}, {0xFD3E, 0xFD3E}, + {0xFD3F, 0xFD3F}, {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, + {0xFDF0, 0xFDFB}, {0xFDFC, 0xFDFC}, {0xFDFD, 0xFDFD}, + {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFC, 0xFFFC}, + {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, + {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, + {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133}, + {0x10137, 0x1013F}, {0x10140, 0x10174}, {0x10175, 0x10178}, + {0x10179, 0x10189}, {0x1018A, 0x1018B}, {0x1018C, 0x1018E}, + {0x10190, 0x1019B}, {0x101A0, 0x101A0}, {0x101D0, 0x101FC}, + {0x101FD, 0x101FD}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, + {0x102E0, 0x102E0}, {0x102E1, 0x102FB}, {0x10300, 0x1031F}, + {0x10320, 0x10323}, {0x10330, 0x10340}, {0x10341, 0x10341}, + {0x10342, 0x10349}, {0x1034A, 0x1034A}, {0x10350, 0x10375}, + {0x10376, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x1039F}, + {0x103A0, 0x103C3}, {0x103C8, 0x103CF}, {0x103D0, 0x103D0}, + {0x103D1, 0x103D5}, {0x10400, 0x1044F}, {0x10450, 0x1047F}, + {0x10480, 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, 0x1083F}, {0x10840, 0x10855}, {0x10857, 0x10857}, + {0x10858, 0x1085F}, {0x10860, 0x10876}, {0x10877, 0x10878}, + {0x10879, 0x1087F}, {0x10880, 0x1089E}, {0x108A7, 0x108AF}, + {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x108FF}, + {0x10900, 0x10915}, {0x10916, 0x1091B}, {0x1091F, 0x1091F}, + {0x10920, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x1099F}, + {0x109A0, 0x109B7}, {0x109BC, 0x109BD}, {0x109BE, 0x109BF}, + {0x109C0, 0x109CF}, {0x109D2, 0x109FF}, {0x10A00, 0x10A00}, + {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, + {0x10A10, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A33}, + {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x10A40, 0x10A47}, + {0x10A50, 0x10A58}, {0x10A60, 0x10A7C}, {0x10A7D, 0x10A7E}, + {0x10A7F, 0x10A7F}, {0x10A80, 0x10A9C}, {0x10A9D, 0x10A9F}, + {0x10AC0, 0x10AC7}, {0x10AC8, 0x10AC8}, {0x10AC9, 0x10AE4}, + {0x10AE5, 0x10AE6}, {0x10AEB, 0x10AEF}, {0x10AF0, 0x10AF6}, + {0x10B00, 0x10B35}, {0x10B39, 0x10B3F}, {0x10B40, 0x10B55}, + {0x10B58, 0x10B5F}, {0x10B60, 0x10B72}, {0x10B78, 0x10B7F}, + {0x10B80, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF}, + {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, + {0x10CFA, 0x10CFF}, {0x10E60, 0x10E7E}, {0x11000, 0x11000}, + {0x11001, 0x11001}, {0x11002, 0x11002}, {0x11003, 0x11037}, + {0x11038, 0x11046}, {0x11047, 0x1104D}, {0x11052, 0x11065}, + {0x11066, 0x1106F}, {0x1107F, 0x1107F}, {0x11080, 0x11081}, + {0x11082, 0x11082}, {0x11083, 0x110AF}, {0x110B0, 0x110B2}, + {0x110B3, 0x110B6}, {0x110B7, 0x110B8}, {0x110B9, 0x110BA}, + {0x110BB, 0x110BC}, {0x110BD, 0x110BD}, {0x110BE, 0x110C1}, + {0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11102}, + {0x11103, 0x11126}, {0x11127, 0x1112B}, {0x1112C, 0x1112C}, + {0x1112D, 0x11134}, {0x11136, 0x1113F}, {0x11140, 0x11143}, + {0x11150, 0x11172}, {0x11173, 0x11173}, {0x11174, 0x11175}, + {0x11176, 0x11176}, {0x11180, 0x11181}, {0x11182, 0x11182}, + {0x11183, 0x111B2}, {0x111B3, 0x111B5}, {0x111B6, 0x111BE}, + {0x111BF, 0x111C0}, {0x111C1, 0x111C4}, {0x111C5, 0x111C9}, + {0x111CA, 0x111CC}, {0x111CD, 0x111CD}, {0x111D0, 0x111D9}, + {0x111DA, 0x111DA}, {0x111DB, 0x111DB}, {0x111DC, 0x111DC}, + {0x111DD, 0x111DF}, {0x111E1, 0x111F4}, {0x11200, 0x11211}, + {0x11213, 0x1122B}, {0x1122C, 0x1122E}, {0x1122F, 0x11231}, + {0x11232, 0x11233}, {0x11234, 0x11234}, {0x11235, 0x11235}, + {0x11236, 0x11237}, {0x11238, 0x1123D}, {0x1123E, 0x1123E}, + {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D}, + {0x1128F, 0x1129D}, {0x1129F, 0x112A8}, {0x112A9, 0x112A9}, + {0x112B0, 0x112DE}, {0x112DF, 0x112DF}, {0x112E0, 0x112E2}, + {0x112E3, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11301}, + {0x11302, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, + {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, + {0x11335, 0x11339}, {0x1133C, 0x1133C}, {0x1133D, 0x1133D}, + {0x1133E, 0x1133F}, {0x11340, 0x11340}, {0x11341, 0x11344}, + {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350}, + {0x11357, 0x11357}, {0x1135D, 0x11361}, {0x11362, 0x11363}, + {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x11434}, + {0x11435, 0x11437}, {0x11438, 0x1143F}, {0x11440, 0x11441}, + {0x11442, 0x11444}, {0x11445, 0x11445}, {0x11446, 0x11446}, + {0x11447, 0x1144A}, {0x1144B, 0x1144F}, {0x11450, 0x11459}, + {0x1145B, 0x1145B}, {0x1145D, 0x1145D}, {0x11480, 0x114AF}, + {0x114B0, 0x114B2}, {0x114B3, 0x114B8}, {0x114B9, 0x114B9}, + {0x114BA, 0x114BA}, {0x114BB, 0x114BE}, {0x114BF, 0x114C0}, + {0x114C1, 0x114C1}, {0x114C2, 0x114C3}, {0x114C4, 0x114C5}, + {0x114C6, 0x114C6}, {0x114C7, 0x114C7}, {0x114D0, 0x114D9}, + {0x11580, 0x115AE}, {0x115AF, 0x115B1}, {0x115B2, 0x115B5}, + {0x115B8, 0x115BB}, {0x115BC, 0x115BD}, {0x115BE, 0x115BE}, + {0x115BF, 0x115C0}, {0x115C1, 0x115D7}, {0x115D8, 0x115DB}, + {0x115DC, 0x115DD}, {0x11600, 0x1162F}, {0x11630, 0x11632}, + {0x11633, 0x1163A}, {0x1163B, 0x1163C}, {0x1163D, 0x1163D}, + {0x1163E, 0x1163E}, {0x1163F, 0x11640}, {0x11641, 0x11643}, + {0x11644, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, + {0x11680, 0x116AA}, {0x116AB, 0x116AB}, {0x116AC, 0x116AC}, + {0x116AD, 0x116AD}, {0x116AE, 0x116AF}, {0x116B0, 0x116B5}, + {0x116B6, 0x116B6}, {0x116B7, 0x116B7}, {0x116C0, 0x116C9}, + {0x11700, 0x11719}, {0x1171D, 0x1171F}, {0x11720, 0x11721}, + {0x11722, 0x11725}, {0x11726, 0x11726}, {0x11727, 0x1172B}, + {0x11730, 0x11739}, {0x1173A, 0x1173B}, {0x1173C, 0x1173E}, + {0x1173F, 0x1173F}, {0x118A0, 0x118DF}, {0x118E0, 0x118E9}, + {0x118EA, 0x118F2}, {0x118FF, 0x118FF}, {0x11AC0, 0x11AF8}, + {0x11C00, 0x11C08}, {0x11C0A, 0x11C2E}, {0x11C2F, 0x11C2F}, + {0x11C30, 0x11C36}, {0x11C38, 0x11C3D}, {0x11C3E, 0x11C3E}, + {0x11C3F, 0x11C3F}, {0x11C40, 0x11C40}, {0x11C41, 0x11C45}, + {0x11C50, 0x11C59}, {0x11C5A, 0x11C6C}, {0x11C70, 0x11C71}, + {0x11C72, 0x11C8F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CA9}, + {0x11CAA, 0x11CB0}, {0x11CB1, 0x11CB1}, {0x11CB2, 0x11CB3}, + {0x11CB4, 0x11CB4}, {0x11CB5, 0x11CB6}, {0x12000, 0x12399}, + {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, + {0x13000, 0x1342E}, {0x14400, 0x14646}, {0x16800, 0x16A38}, + {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16A6F}, + {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF4}, {0x16AF5, 0x16AF5}, + {0x16B00, 0x16B2F}, {0x16B30, 0x16B36}, {0x16B37, 0x16B3B}, + {0x16B3C, 0x16B3F}, {0x16B40, 0x16B43}, {0x16B44, 0x16B44}, + {0x16B45, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, + {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16F00, 0x16F44}, + {0x16F50, 0x16F50}, {0x16F51, 0x16F7E}, {0x16F8F, 0x16F92}, + {0x16F93, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, + {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BC9C}, + {0x1BC9D, 0x1BC9E}, {0x1BC9F, 0x1BC9F}, {0x1BCA0, 0x1BCA3}, + {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D164}, + {0x1D165, 0x1D166}, {0x1D167, 0x1D169}, {0x1D16A, 0x1D16C}, + {0x1D16D, 0x1D172}, {0x1D173, 0x1D17A}, {0x1D17B, 0x1D182}, + {0x1D183, 0x1D184}, {0x1D185, 0x1D18B}, {0x1D18C, 0x1D1A9}, + {0x1D1AA, 0x1D1AD}, {0x1D1AE, 0x1D1E8}, {0x1D200, 0x1D241}, + {0x1D242, 0x1D244}, {0x1D245, 0x1D245}, {0x1D300, 0x1D356}, + {0x1D360, 0x1D371}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, + {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, + {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, + {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, + {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, + {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, + {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D6C0}, + {0x1D6C1, 0x1D6C1}, {0x1D6C2, 0x1D6DA}, {0x1D6DB, 0x1D6DB}, + {0x1D6DC, 0x1D6FA}, {0x1D6FB, 0x1D6FB}, {0x1D6FC, 0x1D714}, + {0x1D715, 0x1D715}, {0x1D716, 0x1D734}, {0x1D735, 0x1D735}, + {0x1D736, 0x1D74E}, {0x1D74F, 0x1D74F}, {0x1D750, 0x1D76E}, + {0x1D76F, 0x1D76F}, {0x1D770, 0x1D788}, {0x1D789, 0x1D789}, + {0x1D78A, 0x1D7A8}, {0x1D7A9, 0x1D7A9}, {0x1D7AA, 0x1D7C2}, + {0x1D7C3, 0x1D7C3}, {0x1D7C4, 0x1D7CB}, {0x1D7CE, 0x1D7FF}, + {0x1D800, 0x1D9FF}, {0x1DA00, 0x1DA36}, {0x1DA37, 0x1DA3A}, + {0x1DA3B, 0x1DA6C}, {0x1DA6D, 0x1DA74}, {0x1DA75, 0x1DA75}, + {0x1DA76, 0x1DA83}, {0x1DA84, 0x1DA84}, {0x1DA85, 0x1DA86}, + {0x1DA87, 0x1DA8B}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, + {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, + {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E800, 0x1E8C4}, + {0x1E8C7, 0x1E8CF}, {0x1E8D0, 0x1E8D6}, {0x1E900, 0x1E943}, + {0x1E944, 0x1E94A}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, + {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, + {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, + {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, + {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, + {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, + {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, + {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, + {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, + {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, + {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, + {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, + {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, + {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, + {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10C}, + {0x1F12E, 0x1F12E}, {0x1F16A, 0x1F16B}, {0x1F1E6, 0x1F1FF}, + {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, + {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, + {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, + {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, + {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, + {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, + {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6E0, 0x1F6EA}, + {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D4}, + {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, + {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0xE0001, 0xE0001}, + {0xE0020, 0xE007F}, +} + +// Condition have flag EastAsianWidth whether the current locale is CJK or not. +type Condition struct { + EastAsianWidth bool +} + +// NewCondition return new instance of Condition which is current locale. +func NewCondition() *Condition { + return &Condition{EastAsianWidth} +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func (c *Condition) RuneWidth(r rune) int { + switch { + case r < 0 || r > 0x10FFFF || + inTables(r, nonprint, combining, notassigned): + return 0 + case (c.EastAsianWidth && IsAmbiguousWidth(r)) || + inTables(r, doublewidth, emoji): + return 2 + default: + return 1 + } +} + +// StringWidth return width as you can see +func (c *Condition) StringWidth(s string) (width int) { + for _, r := range []rune(s) { + width += c.RuneWidth(r) + } + return width +} + +// 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/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/mattn/go-runewidth/runewidth_js.go new file mode 100644 index 0000000..0ce32c5 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_js.go @@ -0,0 +1,8 @@ +// +build js + +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/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go new file mode 100644 index 0000000..c579e9a --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go @@ -0,0 +1,77 @@ +// +build !windows,!js + +package runewidth + +import ( + "os" + "regexp" + "strings" +) + +var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) + +var mblenTable = map[string]int{ + "utf-8": 6, + "utf8": 6, + "jis": 8, + "eucjp": 3, + "euckr": 2, + "euccn": 2, + "sjis": 2, + "cp932": 2, + "cp51932": 2, + "cp936": 2, + "cp949": 2, + "cp950": 2, + "big5": 2, + "gbk": 2, + "gb2312": 2, +} + +func isEastAsian(locale string) bool { + charset := strings.ToLower(locale) + r := reLoc.FindStringSubmatch(locale) + if len(r) == 2 { + charset = strings.ToLower(r[1]) + } + + if strings.HasSuffix(charset, "@cjk_narrow") { + return false + } + + for pos, b := range []byte(charset) { + if b == '@' { + charset = charset[:pos] + break + } + } + max := 1 + if m, ok := mblenTable[charset]; ok { + max = m + } + if max > 1 && (charset[0] != 'u' || + strings.HasPrefix(locale, "ja") || + strings.HasPrefix(locale, "ko") || + strings.HasPrefix(locale, "zh")) { + return true + } + return false +} + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + locale := os.Getenv("LC_CTYPE") + if locale == "" { + locale = os.Getenv("LANG") + } + + // ignore C locale + if locale == "POSIX" || locale == "C" { + return false + } + if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { + return false + } + + return isEastAsian(locale) +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_test.go b/vendor/github.com/mattn/go-runewidth/runewidth_test.go new file mode 100644 index 0000000..3cabbea --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_test.go @@ -0,0 +1,277 @@ +package runewidth + +import ( + "sort" + "testing" +) + +var _ sort.Interface = (*table)(nil) + +func (t table) Len() int { + return len(t) +} + +func (t table) Less(i, j int) bool { + return t[i].first < t[j].first +} + +func (t *table) Swap(i, j int) { + (*t)[i], (*t)[j] = (*t)[j], (*t)[i] +} + +var tables = []table{ + private, + nonprint, + combining, + doublewidth, + ambiguous, + emoji, + notassigned, + neutral, +} + +func TestSorted(t *testing.T) { + for _, tbl := range tables { + if !sort.IsSorted(&tbl) { + t.Errorf("not sorted") + } + } +} + +var runewidthtests = []struct { + in rune + out int + eaout int +}{ + {'世', 2, 2}, + {'界', 2, 2}, + {'セ', 1, 1}, + {'カ', 1, 1}, + {'イ', 1, 1}, + {'☆', 1, 2}, // double width in ambiguous + {'\x00', 0, 0}, + {'\x01', 0, 0}, + {'\u0300', 0, 0}, +} + +func TestRuneWidth(t *testing.T) { + c := NewCondition() + c.EastAsianWidth = false + for _, tt := range runewidthtests { + if out := c.RuneWidth(tt.in); out != tt.out { + t.Errorf("RuneWidth(%q) = %d, want %d", tt.in, out, tt.out) + } + } + c.EastAsianWidth = true + for _, tt := range runewidthtests { + if out := c.RuneWidth(tt.in); out != tt.eaout { + t.Errorf("RuneWidth(%q) = %d, want %d", tt.in, out, tt.eaout) + } + } +} + +var isambiguouswidthtests = []struct { + in rune + out bool +}{ + {'世', false}, + {'■', true}, + {'界', false}, + {'○', true}, + {'㈱', false}, + {'①', true}, + {'②', true}, + {'③', true}, + {'④', true}, + {'⑤', true}, + {'⑥', true}, + {'⑦', true}, + {'⑧', true}, + {'⑨', true}, + {'⑩', true}, + {'⑪', true}, + {'⑫', true}, + {'⑬', true}, + {'⑭', true}, + {'⑮', true}, + {'⑯', true}, + {'⑰', true}, + {'⑱', true}, + {'⑲', true}, + {'⑳', true}, + {'☆', true}, +} + +func TestIsAmbiguousWidth(t *testing.T) { + for _, tt := range isambiguouswidthtests { + if out := IsAmbiguousWidth(tt.in); out != tt.out { + t.Errorf("IsAmbiguousWidth(%q) = %v, want %v", tt.in, out, tt.out) + } + } +} + +var stringwidthtests = []struct { + in string + out int + eaout int +}{ + {"■㈱の世界①", 10, 12}, + {"スター☆", 7, 8}, + {"つのだ☆HIRO", 11, 12}, +} + +func TestStringWidth(t *testing.T) { + c := NewCondition() + c.EastAsianWidth = false + for _, tt := range stringwidthtests { + if out := c.StringWidth(tt.in); out != tt.out { + t.Errorf("StringWidth(%q) = %d, want %d", tt.in, out, tt.out) + } + } + c.EastAsianWidth = true + for _, tt := range stringwidthtests { + if out := c.StringWidth(tt.in); out != tt.eaout { + t.Errorf("StringWidth(%q) = %d, want %d", tt.in, out, tt.eaout) + } + } +} + +func TestStringWidthInvalid(t *testing.T) { + s := "こんにちわ\x00世界" + if out := StringWidth(s); out != 14 { + t.Errorf("StringWidth(%q) = %d, want %d", s, out, 14) + } +} + +func TestTruncateSmaller(t *testing.T) { + s := "あいうえお" + expected := "あいうえお" + + if out := Truncate(s, 10, "..."); out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } +} + +func TestTruncate(t *testing.T) { + s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" + expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..." + out := Truncate(s, 80, "...") + if out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } + width := StringWidth(out) + if width != 79 { + t.Errorf("width of Truncate(%q) should be %d, but %d", s, 79, width) + } +} + +func TestTruncateFit(t *testing.T) { + s := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" + expected := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..." + + out := Truncate(s, 80, "...") + if out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } + width := StringWidth(out) + if width != 80 { + t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width) + } +} + +func TestTruncateJustFit(t *testing.T) { + s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" + expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" + + out := Truncate(s, 80, "...") + if out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } + width := StringWidth(out) + if width != 80 { + t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width) + } +} + +func TestWrap(t *testing.T) { + s := `東京特許許可局局長はよく柿喰う客だ/東京特許許可局局長はよく柿喰う客だ +123456789012345678901234567890 + +END` + expected := `東京特許許可局局長はよく柿喰う +客だ/東京特許許可局局長はよく +柿喰う客だ +123456789012345678901234567890 + +END` + + if out := Wrap(s, 30); out != expected { + t.Errorf("Wrap(%q) = %q, want %q", s, out, expected) + } +} + +func TestTruncateNoNeeded(t *testing.T) { + s := "あいうえおあい" + expected := "あいうえおあい" + + if out := Truncate(s, 80, "..."); out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } +} + +var isneutralwidthtests = []struct { + in rune + out bool +}{ + {'→', false}, + {'┊', false}, + {'┈', false}, + {'~', false}, + {'└', false}, + {'⣀', true}, + {'⣀', true}, +} + +func TestIsNeutralWidth(t *testing.T) { + for _, tt := range isneutralwidthtests { + if out := IsNeutralWidth(tt.in); out != tt.out { + t.Errorf("IsNeutralWidth(%q) = %v, want %v", tt.in, out, tt.out) + } + } +} + +func TestFillLeft(t *testing.T) { + s := "あxいうえお" + expected := " あxいうえお" + + if out := FillLeft(s, 15); out != expected { + t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected) + } +} + +func TestFillLeftFit(t *testing.T) { + s := "あいうえお" + expected := "あいうえお" + + if out := FillLeft(s, 10); out != expected { + t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected) + } +} + +func TestFillRight(t *testing.T) { + s := "あxいうえお" + expected := "あxいうえお " + + if out := FillRight(s, 15); out != expected { + t.Errorf("FillRight(%q) = %q, want %q", s, out, expected) + } +} + +func TestFillRightFit(t *testing.T) { + s := "あいうえお" + expected := "あいうえお" + + if out := FillRight(s, 10); out != expected { + t.Errorf("FillRight(%q) = %q, want %q", s, out, expected) + } +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go new file mode 100644 index 0000000..0258876 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go @@ -0,0 +1,25 @@ +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/vendor/github.com/nsf/termbox-go/AUTHORS b/vendor/github.com/nsf/termbox-go/AUTHORS new file mode 100644 index 0000000..fe26fb0 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/AUTHORS @@ -0,0 +1,4 @@ +# Please keep this file sorted. + +Georg Reinke +nsf diff --git a/vendor/github.com/nsf/termbox-go/LICENSE b/vendor/github.com/nsf/termbox-go/LICENSE new file mode 100644 index 0000000..d9bc068 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2012 termbox-go authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/nsf/termbox-go/README.md b/vendor/github.com/nsf/termbox-go/README.md new file mode 100644 index 0000000..5174435 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/README.md @@ -0,0 +1,32 @@ +## 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. + - [gomatrix](https://github.com/GeertJohan/gomatrix) connects to The Matrix and displays its data streams in your terminal. + - [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/michaeldv/mop) is stock market tracker for hackers. + - [termui](https://github.com/gizak/termui) 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. + +### API reference +[godoc.org/github.com/nsf/termbox-go](http://godoc.org/github.com/nsf/termbox-go) diff --git a/vendor/github.com/nsf/termbox-go/_demos/editbox.go b/vendor/github.com/nsf/termbox-go/_demos/editbox.go new file mode 100644 index 0000000..e429080 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/_demos/editbox.go @@ -0,0 +1,300 @@ +package main + +import ( + "github.com/mattn/go-runewidth" + "github.com/nsf/termbox-go" + "unicode/utf8" +) + +func tbprint(x, y int, fg, bg termbox.Attribute, msg string) { + for _, c := range msg { + termbox.SetCell(x, y, c, fg, bg) + x += runewidth.RuneWidth(c) + } +} + +func fill(x, y, w, h int, cell termbox.Cell) { + for ly := 0; ly < h; ly++ { + for lx := 0; lx < w; lx++ { + termbox.SetCell(x+lx, y+ly, cell.Ch, cell.Fg, cell.Bg) + } + } +} + +func rune_advance_len(r rune, pos int) int { + if r == '\t' { + return tabstop_length - pos%tabstop_length + } + return runewidth.RuneWidth(r) +} + +func voffset_coffset(text []byte, boffset int) (voffset, coffset int) { + text = text[:boffset] + for len(text) > 0 { + r, size := utf8.DecodeRune(text) + text = text[size:] + coffset += 1 + voffset += rune_advance_len(r, voffset) + } + return +} + +func byte_slice_grow(s []byte, desired_cap int) []byte { + if cap(s) < desired_cap { + ns := make([]byte, len(s), desired_cap) + copy(ns, s) + return ns + } + return s +} + +func byte_slice_remove(text []byte, from, to int) []byte { + size := to - from + copy(text[from:], text[to:]) + text = text[:len(text)-size] + return text +} + +func byte_slice_insert(text []byte, offset int, what []byte) []byte { + n := len(text) + len(what) + text = byte_slice_grow(text, n) + text = text[:n] + copy(text[offset+len(what):], text[offset:]) + copy(text[offset:], what) + return text +} + +const preferred_horizontal_threshold = 5 +const tabstop_length = 8 + +type EditBox struct { + text []byte + line_voffset int + cursor_boffset int // cursor offset in bytes + cursor_voffset int // visual cursor offset in termbox cells + cursor_coffset int // cursor offset in unicode code points +} + +// Draws the EditBox in the given location, 'h' is not used at the moment +func (eb *EditBox) Draw(x, y, w, h int) { + eb.AdjustVOffset(w) + + const coldef = termbox.ColorDefault + fill(x, y, w, h, termbox.Cell{Ch: ' '}) + + t := eb.text + lx := 0 + tabstop := 0 + for { + rx := lx - eb.line_voffset + if len(t) == 0 { + break + } + + if lx == tabstop { + tabstop += tabstop_length + } + + if rx >= w { + termbox.SetCell(x+w-1, y, '→', + coldef, coldef) + break + } + + r, size := utf8.DecodeRune(t) + if r == '\t' { + for ; lx < tabstop; lx++ { + rx = lx - eb.line_voffset + if rx >= w { + goto next + } + + if rx >= 0 { + termbox.SetCell(x+rx, y, ' ', coldef, coldef) + } + } + } else { + if rx >= 0 { + termbox.SetCell(x+rx, y, r, coldef, coldef) + } + lx += runewidth.RuneWidth(r) + } + next: + t = t[size:] + } + + if eb.line_voffset != 0 { + termbox.SetCell(x, y, '←', coldef, coldef) + } +} + +// Adjusts line visual offset to a proper value depending on width +func (eb *EditBox) AdjustVOffset(width int) { + ht := preferred_horizontal_threshold + max_h_threshold := (width - 1) / 2 + if ht > max_h_threshold { + ht = max_h_threshold + } + + threshold := width - 1 + if eb.line_voffset != 0 { + threshold = width - ht + } + if eb.cursor_voffset-eb.line_voffset >= threshold { + eb.line_voffset = eb.cursor_voffset + (ht - width + 1) + } + + if eb.line_voffset != 0 && eb.cursor_voffset-eb.line_voffset < ht { + eb.line_voffset = eb.cursor_voffset - ht + if eb.line_voffset < 0 { + eb.line_voffset = 0 + } + } +} + +func (eb *EditBox) MoveCursorTo(boffset int) { + eb.cursor_boffset = boffset + eb.cursor_voffset, eb.cursor_coffset = voffset_coffset(eb.text, boffset) +} + +func (eb *EditBox) RuneUnderCursor() (rune, int) { + return utf8.DecodeRune(eb.text[eb.cursor_boffset:]) +} + +func (eb *EditBox) RuneBeforeCursor() (rune, int) { + return utf8.DecodeLastRune(eb.text[:eb.cursor_boffset]) +} + +func (eb *EditBox) MoveCursorOneRuneBackward() { + if eb.cursor_boffset == 0 { + return + } + _, size := eb.RuneBeforeCursor() + eb.MoveCursorTo(eb.cursor_boffset - size) +} + +func (eb *EditBox) MoveCursorOneRuneForward() { + if eb.cursor_boffset == len(eb.text) { + return + } + _, size := eb.RuneUnderCursor() + eb.MoveCursorTo(eb.cursor_boffset + size) +} + +func (eb *EditBox) MoveCursorToBeginningOfTheLine() { + eb.MoveCursorTo(0) +} + +func (eb *EditBox) MoveCursorToEndOfTheLine() { + eb.MoveCursorTo(len(eb.text)) +} + +func (eb *EditBox) DeleteRuneBackward() { + if eb.cursor_boffset == 0 { + return + } + + eb.MoveCursorOneRuneBackward() + _, size := eb.RuneUnderCursor() + eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset+size) +} + +func (eb *EditBox) DeleteRuneForward() { + if eb.cursor_boffset == len(eb.text) { + return + } + _, size := eb.RuneUnderCursor() + eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset+size) +} + +func (eb *EditBox) DeleteTheRestOfTheLine() { + eb.text = eb.text[:eb.cursor_boffset] +} + +func (eb *EditBox) InsertRune(r rune) { + var buf [utf8.UTFMax]byte + n := utf8.EncodeRune(buf[:], r) + eb.text = byte_slice_insert(eb.text, eb.cursor_boffset, buf[:n]) + eb.MoveCursorOneRuneForward() +} + +// Please, keep in mind that cursor depends on the value of line_voffset, which +// is being set on Draw() call, so.. call this method after Draw() one. +func (eb *EditBox) CursorX() int { + return eb.cursor_voffset - eb.line_voffset +} + +var edit_box EditBox + +const edit_box_width = 30 + +func redraw_all() { + const coldef = termbox.ColorDefault + termbox.Clear(coldef, coldef) + w, h := termbox.Size() + + midy := h / 2 + midx := (w - edit_box_width) / 2 + + // unicode box drawing chars around the edit box + termbox.SetCell(midx-1, midy, '│', coldef, coldef) + termbox.SetCell(midx+edit_box_width, midy, '│', coldef, coldef) + termbox.SetCell(midx-1, midy-1, '┌', coldef, coldef) + termbox.SetCell(midx-1, midy+1, '└', coldef, coldef) + termbox.SetCell(midx+edit_box_width, midy-1, '┐', coldef, coldef) + termbox.SetCell(midx+edit_box_width, midy+1, '┘', coldef, coldef) + fill(midx, midy-1, edit_box_width, 1, termbox.Cell{Ch: '─'}) + fill(midx, midy+1, edit_box_width, 1, termbox.Cell{Ch: '─'}) + + edit_box.Draw(midx, midy, edit_box_width, 1) + termbox.SetCursor(midx+edit_box.CursorX(), midy) + + tbprint(midx+6, midy+3, coldef, coldef, "Press ESC to quit") + termbox.Flush() +} + +func main() { + err := termbox.Init() + if err != nil { + panic(err) + } + defer termbox.Close() + termbox.SetInputMode(termbox.InputEsc) + + redraw_all() +mainloop: + for { + switch ev := termbox.PollEvent(); ev.Type { + case termbox.EventKey: + switch ev.Key { + case termbox.KeyEsc: + break mainloop + case termbox.KeyArrowLeft, termbox.KeyCtrlB: + edit_box.MoveCursorOneRuneBackward() + case termbox.KeyArrowRight, termbox.KeyCtrlF: + edit_box.MoveCursorOneRuneForward() + case termbox.KeyBackspace, termbox.KeyBackspace2: + edit_box.DeleteRuneBackward() + case termbox.KeyDelete, termbox.KeyCtrlD: + edit_box.DeleteRuneForward() + case termbox.KeyTab: + edit_box.InsertRune('\t') + case termbox.KeySpace: + edit_box.InsertRune(' ') + case termbox.KeyCtrlK: + edit_box.DeleteTheRestOfTheLine() + case termbox.KeyHome, termbox.KeyCtrlA: + edit_box.MoveCursorToBeginningOfTheLine() + case termbox.KeyEnd, termbox.KeyCtrlE: + edit_box.MoveCursorToEndOfTheLine() + default: + if ev.Ch != 0 { + edit_box.InsertRune(ev.Ch) + } + } + case termbox.EventError: + panic(ev.Err) + } + redraw_all() + } +} diff --git a/vendor/github.com/nsf/termbox-go/_demos/interrupt.go b/vendor/github.com/nsf/termbox-go/_demos/interrupt.go new file mode 100644 index 0000000..5534521 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/_demos/interrupt.go @@ -0,0 +1,69 @@ +package main + +import ( + "fmt" + "github.com/nsf/termbox-go" + "time" +) + +func tbPrint(x, y int, fg, bg termbox.Attribute, msg string) { + for _, c := range msg { + termbox.SetCell(x, y, c, fg, bg) + x++ + } +} + +func draw(i int) { + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + defer termbox.Flush() + + w, h := termbox.Size() + s := fmt.Sprintf("count = %d", i) + + tbPrint((w/2)-(len(s)/2), h/2, termbox.ColorRed, termbox.ColorDefault, s) +} + +func main() { + err := termbox.Init() + if err != nil { + panic(err) + } + termbox.SetInputMode(termbox.InputEsc) + + go func() { + time.Sleep(5 * time.Second) + termbox.Interrupt() + + // This should never run - the Interrupt(), above, should cause the event + // loop below to exit, which then exits the process. If something goes + // wrong, this panic will trigger and show what happened. + time.Sleep(1 * time.Second) + panic("this should never run") + }() + + var count int + + draw(count) +mainloop: + for { + switch ev := termbox.PollEvent(); ev.Type { + case termbox.EventKey: + if ev.Ch == '+' { + count++ + } else if ev.Ch == '-' { + count-- + } + + case termbox.EventError: + panic(ev.Err) + + case termbox.EventInterrupt: + break mainloop + } + + draw(count) + } + termbox.Close() + + fmt.Println("Finished") +} diff --git a/vendor/github.com/nsf/termbox-go/_demos/keyboard.go b/vendor/github.com/nsf/termbox-go/_demos/keyboard.go new file mode 100644 index 0000000..b6a258e --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/_demos/keyboard.go @@ -0,0 +1,722 @@ +package main + +import "github.com/nsf/termbox-go" +import "fmt" + +type key struct { + x int + y int + ch rune +} + +var K_ESC = []key{{1, 1, 'E'}, {2, 1, 'S'}, {3, 1, 'C'}} +var K_F1 = []key{{6, 1, 'F'}, {7, 1, '1'}} +var K_F2 = []key{{9, 1, 'F'}, {10, 1, '2'}} +var K_F3 = []key{{12, 1, 'F'}, {13, 1, '3'}} +var K_F4 = []key{{15, 1, 'F'}, {16, 1, '4'}} +var K_F5 = []key{{19, 1, 'F'}, {20, 1, '5'}} +var K_F6 = []key{{22, 1, 'F'}, {23, 1, '6'}} +var K_F7 = []key{{25, 1, 'F'}, {26, 1, '7'}} +var K_F8 = []key{{28, 1, 'F'}, {29, 1, '8'}} +var K_F9 = []key{{33, 1, 'F'}, {34, 1, '9'}} +var K_F10 = []key{{36, 1, 'F'}, {37, 1, '1'}, {38, 1, '0'}} +var K_F11 = []key{{40, 1, 'F'}, {41, 1, '1'}, {42, 1, '1'}} +var K_F12 = []key{{44, 1, 'F'}, {45, 1, '1'}, {46, 1, '2'}} +var K_PRN = []key{{50, 1, 'P'}, {51, 1, 'R'}, {52, 1, 'N'}} +var K_SCR = []key{{54, 1, 'S'}, {55, 1, 'C'}, {56, 1, 'R'}} +var K_BRK = []key{{58, 1, 'B'}, {59, 1, 'R'}, {60, 1, 'K'}} +var K_LED1 = []key{{66, 1, '-'}} +var K_LED2 = []key{{70, 1, '-'}} +var K_LED3 = []key{{74, 1, '-'}} +var K_TILDE = []key{{1, 4, '`'}} +var K_TILDE_SHIFT = []key{{1, 4, '~'}} +var K_1 = []key{{4, 4, '1'}} +var K_1_SHIFT = []key{{4, 4, '!'}} +var K_2 = []key{{7, 4, '2'}} +var K_2_SHIFT = []key{{7, 4, '@'}} +var K_3 = []key{{10, 4, '3'}} +var K_3_SHIFT = []key{{10, 4, '#'}} +var K_4 = []key{{13, 4, '4'}} +var K_4_SHIFT = []key{{13, 4, '$'}} +var K_5 = []key{{16, 4, '5'}} +var K_5_SHIFT = []key{{16, 4, '%'}} +var K_6 = []key{{19, 4, '6'}} +var K_6_SHIFT = []key{{19, 4, '^'}} +var K_7 = []key{{22, 4, '7'}} +var K_7_SHIFT = []key{{22, 4, '&'}} +var K_8 = []key{{25, 4, '8'}} +var K_8_SHIFT = []key{{25, 4, '*'}} +var K_9 = []key{{28, 4, '9'}} +var K_9_SHIFT = []key{{28, 4, '('}} +var K_0 = []key{{31, 4, '0'}} +var K_0_SHIFT = []key{{31, 4, ')'}} +var K_MINUS = []key{{34, 4, '-'}} +var K_MINUS_SHIFT = []key{{34, 4, '_'}} +var K_EQUALS = []key{{37, 4, '='}} +var K_EQUALS_SHIFT = []key{{37, 4, '+'}} +var K_BACKSLASH = []key{{40, 4, '\\'}} +var K_BACKSLASH_SHIFT = []key{{40, 4, '|'}} +var K_BACKSPACE = []key{{44, 4, 0x2190}, {45, 4, 0x2500}, {46, 4, 0x2500}} +var K_INS = []key{{50, 4, 'I'}, {51, 4, 'N'}, {52, 4, 'S'}} +var K_HOM = []key{{54, 4, 'H'}, {55, 4, 'O'}, {56, 4, 'M'}} +var K_PGU = []key{{58, 4, 'P'}, {59, 4, 'G'}, {60, 4, 'U'}} +var K_K_NUMLOCK = []key{{65, 4, 'N'}} +var K_K_SLASH = []key{{68, 4, '/'}} +var K_K_STAR = []key{{71, 4, '*'}} +var K_K_MINUS = []key{{74, 4, '-'}} +var K_TAB = []key{{1, 6, 'T'}, {2, 6, 'A'}, {3, 6, 'B'}} +var K_q = []key{{6, 6, 'q'}} +var K_Q = []key{{6, 6, 'Q'}} +var K_w = []key{{9, 6, 'w'}} +var K_W = []key{{9, 6, 'W'}} +var K_e = []key{{12, 6, 'e'}} +var K_E = []key{{12, 6, 'E'}} +var K_r = []key{{15, 6, 'r'}} +var K_R = []key{{15, 6, 'R'}} +var K_t = []key{{18, 6, 't'}} +var K_T = []key{{18, 6, 'T'}} +var K_y = []key{{21, 6, 'y'}} +var K_Y = []key{{21, 6, 'Y'}} +var K_u = []key{{24, 6, 'u'}} +var K_U = []key{{24, 6, 'U'}} +var K_i = []key{{27, 6, 'i'}} +var K_I = []key{{27, 6, 'I'}} +var K_o = []key{{30, 6, 'o'}} +var K_O = []key{{30, 6, 'O'}} +var K_p = []key{{33, 6, 'p'}} +var K_P = []key{{33, 6, 'P'}} +var K_LSQB = []key{{36, 6, '['}} +var K_LCUB = []key{{36, 6, '{'}} +var K_RSQB = []key{{39, 6, ']'}} +var K_RCUB = []key{{39, 6, '}'}} +var K_ENTER = []key{ + {43, 6, 0x2591}, {44, 6, 0x2591}, {45, 6, 0x2591}, {46, 6, 0x2591}, + {43, 7, 0x2591}, {44, 7, 0x2591}, {45, 7, 0x21B5}, {46, 7, 0x2591}, + {41, 8, 0x2591}, {42, 8, 0x2591}, {43, 8, 0x2591}, {44, 8, 0x2591}, + {45, 8, 0x2591}, {46, 8, 0x2591}, +} +var K_DEL = []key{{50, 6, 'D'}, {51, 6, 'E'}, {52, 6, 'L'}} +var K_END = []key{{54, 6, 'E'}, {55, 6, 'N'}, {56, 6, 'D'}} +var K_PGD = []key{{58, 6, 'P'}, {59, 6, 'G'}, {60, 6, 'D'}} +var K_K_7 = []key{{65, 6, '7'}} +var K_K_8 = []key{{68, 6, '8'}} +var K_K_9 = []key{{71, 6, '9'}} +var K_K_PLUS = []key{{74, 6, ' '}, {74, 7, '+'}, {74, 8, ' '}} +var K_CAPS = []key{{1, 8, 'C'}, {2, 8, 'A'}, {3, 8, 'P'}, {4, 8, 'S'}} +var K_a = []key{{7, 8, 'a'}} +var K_A = []key{{7, 8, 'A'}} +var K_s = []key{{10, 8, 's'}} +var K_S = []key{{10, 8, 'S'}} +var K_d = []key{{13, 8, 'd'}} +var K_D = []key{{13, 8, 'D'}} +var K_f = []key{{16, 8, 'f'}} +var K_F = []key{{16, 8, 'F'}} +var K_g = []key{{19, 8, 'g'}} +var K_G = []key{{19, 8, 'G'}} +var K_h = []key{{22, 8, 'h'}} +var K_H = []key{{22, 8, 'H'}} +var K_j = []key{{25, 8, 'j'}} +var K_J = []key{{25, 8, 'J'}} +var K_k = []key{{28, 8, 'k'}} +var K_K = []key{{28, 8, 'K'}} +var K_l = []key{{31, 8, 'l'}} +var K_L = []key{{31, 8, 'L'}} +var K_SEMICOLON = []key{{34, 8, ';'}} +var K_PARENTHESIS = []key{{34, 8, ':'}} +var K_QUOTE = []key{{37, 8, '\''}} +var K_DOUBLEQUOTE = []key{{37, 8, '"'}} +var K_K_4 = []key{{65, 8, '4'}} +var K_K_5 = []key{{68, 8, '5'}} +var K_K_6 = []key{{71, 8, '6'}} +var K_LSHIFT = []key{{1, 10, 'S'}, {2, 10, 'H'}, {3, 10, 'I'}, {4, 10, 'F'}, {5, 10, 'T'}} +var K_z = []key{{9, 10, 'z'}} +var K_Z = []key{{9, 10, 'Z'}} +var K_x = []key{{12, 10, 'x'}} +var K_X = []key{{12, 10, 'X'}} +var K_c = []key{{15, 10, 'c'}} +var K_C = []key{{15, 10, 'C'}} +var K_v = []key{{18, 10, 'v'}} +var K_V = []key{{18, 10, 'V'}} +var K_b = []key{{21, 10, 'b'}} +var K_B = []key{{21, 10, 'B'}} +var K_n = []key{{24, 10, 'n'}} +var K_N = []key{{24, 10, 'N'}} +var K_m = []key{{27, 10, 'm'}} +var K_M = []key{{27, 10, 'M'}} +var K_COMMA = []key{{30, 10, ','}} +var K_LANB = []key{{30, 10, '<'}} +var K_PERIOD = []key{{33, 10, '.'}} +var K_RANB = []key{{33, 10, '>'}} +var K_SLASH = []key{{36, 10, '/'}} +var K_QUESTION = []key{{36, 10, '?'}} +var K_RSHIFT = []key{{42, 10, 'S'}, {43, 10, 'H'}, {44, 10, 'I'}, {45, 10, 'F'}, {46, 10, 'T'}} +var K_ARROW_UP = []key{{54, 10, '('}, {55, 10, 0x2191}, {56, 10, ')'}} +var K_K_1 = []key{{65, 10, '1'}} +var K_K_2 = []key{{68, 10, '2'}} +var K_K_3 = []key{{71, 10, '3'}} +var K_K_ENTER = []key{{74, 10, 0x2591}, {74, 11, 0x2591}, {74, 12, 0x2591}} +var K_LCTRL = []key{{1, 12, 'C'}, {2, 12, 'T'}, {3, 12, 'R'}, {4, 12, 'L'}} +var K_LWIN = []key{{6, 12, 'W'}, {7, 12, 'I'}, {8, 12, 'N'}} +var K_LALT = []key{{10, 12, 'A'}, {11, 12, 'L'}, {12, 12, 'T'}} +var K_SPACE = []key{ + {14, 12, ' '}, {15, 12, ' '}, {16, 12, ' '}, {17, 12, ' '}, {18, 12, ' '}, + {19, 12, 'S'}, {20, 12, 'P'}, {21, 12, 'A'}, {22, 12, 'C'}, {23, 12, 'E'}, + {24, 12, ' '}, {25, 12, ' '}, {26, 12, ' '}, {27, 12, ' '}, {28, 12, ' '}, +} +var K_RALT = []key{{30, 12, 'A'}, {31, 12, 'L'}, {32, 12, 'T'}} +var K_RWIN = []key{{34, 12, 'W'}, {35, 12, 'I'}, {36, 12, 'N'}} +var K_RPROP = []key{{38, 12, 'P'}, {39, 12, 'R'}, {40, 12, 'O'}, {41, 12, 'P'}} +var K_RCTRL = []key{{43, 12, 'C'}, {44, 12, 'T'}, {45, 12, 'R'}, {46, 12, 'L'}} +var K_ARROW_LEFT = []key{{50, 12, '('}, {51, 12, 0x2190}, {52, 12, ')'}} +var K_ARROW_DOWN = []key{{54, 12, '('}, {55, 12, 0x2193}, {56, 12, ')'}} +var K_ARROW_RIGHT = []key{{58, 12, '('}, {59, 12, 0x2192}, {60, 12, ')'}} +var K_K_0 = []key{{65, 12, ' '}, {66, 12, '0'}, {67, 12, ' '}, {68, 12, ' '}} +var K_K_PERIOD = []key{{71, 12, '.'}} + +type combo struct { + keys [][]key +} + +var combos = []combo{ + {[][]key{K_TILDE, K_2, K_SPACE, K_LCTRL, K_RCTRL}}, + {[][]key{K_A, K_LCTRL, K_RCTRL}}, + {[][]key{K_B, K_LCTRL, K_RCTRL}}, + {[][]key{K_C, K_LCTRL, K_RCTRL}}, + {[][]key{K_D, K_LCTRL, K_RCTRL}}, + {[][]key{K_E, K_LCTRL, K_RCTRL}}, + {[][]key{K_F, K_LCTRL, K_RCTRL}}, + {[][]key{K_G, K_LCTRL, K_RCTRL}}, + {[][]key{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL}}, + {[][]key{K_I, K_TAB, K_LCTRL, K_RCTRL}}, + {[][]key{K_J, K_LCTRL, K_RCTRL}}, + {[][]key{K_K, K_LCTRL, K_RCTRL}}, + {[][]key{K_L, K_LCTRL, K_RCTRL}}, + {[][]key{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL}}, + {[][]key{K_N, K_LCTRL, K_RCTRL}}, + {[][]key{K_O, K_LCTRL, K_RCTRL}}, + {[][]key{K_P, K_LCTRL, K_RCTRL}}, + {[][]key{K_Q, K_LCTRL, K_RCTRL}}, + {[][]key{K_R, K_LCTRL, K_RCTRL}}, + {[][]key{K_S, K_LCTRL, K_RCTRL}}, + {[][]key{K_T, K_LCTRL, K_RCTRL}}, + {[][]key{K_U, K_LCTRL, K_RCTRL}}, + {[][]key{K_V, K_LCTRL, K_RCTRL}}, + {[][]key{K_W, K_LCTRL, K_RCTRL}}, + {[][]key{K_X, K_LCTRL, K_RCTRL}}, + {[][]key{K_Y, K_LCTRL, K_RCTRL}}, + {[][]key{K_Z, K_LCTRL, K_RCTRL}}, + {[][]key{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL}}, + {[][]key{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL}}, + {[][]key{K_RSQB, K_5, K_LCTRL, K_RCTRL}}, + {[][]key{K_6, K_LCTRL, K_RCTRL}}, + {[][]key{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL}}, + {[][]key{K_SPACE}}, + {[][]key{K_1_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_DOUBLEQUOTE, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_3_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_4_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_5_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_7_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_QUOTE}}, + {[][]key{K_9_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_0_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_8_SHIFT, K_K_STAR, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_EQUALS_SHIFT, K_K_PLUS, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_COMMA}}, + {[][]key{K_MINUS, K_K_MINUS}}, + {[][]key{K_PERIOD, K_K_PERIOD}}, + {[][]key{K_SLASH, K_K_SLASH}}, + {[][]key{K_0, K_K_0}}, + {[][]key{K_1, K_K_1}}, + {[][]key{K_2, K_K_2}}, + {[][]key{K_3, K_K_3}}, + {[][]key{K_4, K_K_4}}, + {[][]key{K_5, K_K_5}}, + {[][]key{K_6, K_K_6}}, + {[][]key{K_7, K_K_7}}, + {[][]key{K_8, K_K_8}}, + {[][]key{K_9, K_K_9}}, + {[][]key{K_PARENTHESIS, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_SEMICOLON}}, + {[][]key{K_LANB, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_EQUALS}}, + {[][]key{K_RANB, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_QUESTION, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_2_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_A, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_B, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_C, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_D, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_E, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_F, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_G, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_H, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_I, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_J, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_K, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_L, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_M, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_N, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_O, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_P, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_Q, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_R, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_S, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_T, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_U, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_V, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_W, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_X, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_Y, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_Z, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_LSQB}}, + {[][]key{K_BACKSLASH}}, + {[][]key{K_RSQB}}, + {[][]key{K_6_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_MINUS_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_TILDE}}, + {[][]key{K_a}}, + {[][]key{K_b}}, + {[][]key{K_c}}, + {[][]key{K_d}}, + {[][]key{K_e}}, + {[][]key{K_f}}, + {[][]key{K_g}}, + {[][]key{K_h}}, + {[][]key{K_i}}, + {[][]key{K_j}}, + {[][]key{K_k}}, + {[][]key{K_l}}, + {[][]key{K_m}}, + {[][]key{K_n}}, + {[][]key{K_o}}, + {[][]key{K_p}}, + {[][]key{K_q}}, + {[][]key{K_r}}, + {[][]key{K_s}}, + {[][]key{K_t}}, + {[][]key{K_u}}, + {[][]key{K_v}}, + {[][]key{K_w}}, + {[][]key{K_x}}, + {[][]key{K_y}}, + {[][]key{K_z}}, + {[][]key{K_LCUB, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_BACKSLASH_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_RCUB, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_TILDE_SHIFT, K_LSHIFT, K_RSHIFT}}, + {[][]key{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL}}, +} + +var func_combos = []combo{ + {[][]key{K_F1}}, + {[][]key{K_F2}}, + {[][]key{K_F3}}, + {[][]key{K_F4}}, + {[][]key{K_F5}}, + {[][]key{K_F6}}, + {[][]key{K_F7}}, + {[][]key{K_F8}}, + {[][]key{K_F9}}, + {[][]key{K_F10}}, + {[][]key{K_F11}}, + {[][]key{K_F12}}, + {[][]key{K_INS}}, + {[][]key{K_DEL}}, + {[][]key{K_HOM}}, + {[][]key{K_END}}, + {[][]key{K_PGU}}, + {[][]key{K_PGD}}, + {[][]key{K_ARROW_UP}}, + {[][]key{K_ARROW_DOWN}}, + {[][]key{K_ARROW_LEFT}}, + {[][]key{K_ARROW_RIGHT}}, +} + +func print_tb(x, y int, fg, bg termbox.Attribute, msg string) { + for _, c := range msg { + termbox.SetCell(x, y, c, fg, bg) + x++ + } +} + +func printf_tb(x, y int, fg, bg termbox.Attribute, format string, args ...interface{}) { + s := fmt.Sprintf(format, args...) + print_tb(x, y, fg, bg, s) +} + +func draw_key(k []key, fg, bg termbox.Attribute) { + for _, k := range k { + termbox.SetCell(k.x+2, k.y+4, k.ch, fg, bg) + } +} + +func draw_keyboard() { + termbox.SetCell(0, 0, 0x250C, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(79, 0, 0x2510, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(0, 23, 0x2514, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(79, 23, 0x2518, termbox.ColorWhite, termbox.ColorBlack) + + for i := 1; i < 79; i++ { + termbox.SetCell(i, 0, 0x2500, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(i, 23, 0x2500, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(i, 17, 0x2500, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(i, 4, 0x2500, termbox.ColorWhite, termbox.ColorBlack) + } + for i := 1; i < 23; i++ { + termbox.SetCell(0, i, 0x2502, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(79, i, 0x2502, termbox.ColorWhite, termbox.ColorBlack) + } + termbox.SetCell(0, 17, 0x251C, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(79, 17, 0x2524, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(0, 4, 0x251C, termbox.ColorWhite, termbox.ColorBlack) + termbox.SetCell(79, 4, 0x2524, termbox.ColorWhite, termbox.ColorBlack) + for i := 5; i < 17; i++ { + termbox.SetCell(1, i, 0x2588, termbox.ColorYellow, termbox.ColorYellow) + termbox.SetCell(78, i, 0x2588, termbox.ColorYellow, termbox.ColorYellow) + } + + draw_key(K_ESC, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F1, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F2, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F3, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F4, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F5, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F6, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F7, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F8, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F9, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F10, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F11, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_F12, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_PRN, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_SCR, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_BRK, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_LED1, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_LED2, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_LED3, termbox.ColorWhite, termbox.ColorBlue) + + draw_key(K_TILDE, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_1, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_2, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_3, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_4, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_5, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_6, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_7, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_8, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_9, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_0, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_MINUS, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_EQUALS, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_BACKSLASH, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_BACKSPACE, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_INS, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_HOM, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_PGU, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_NUMLOCK, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_SLASH, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_STAR, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_MINUS, termbox.ColorWhite, termbox.ColorBlue) + + draw_key(K_TAB, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_q, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_w, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_e, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_r, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_t, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_y, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_u, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_i, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_o, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_p, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_LSQB, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_RSQB, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_ENTER, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_DEL, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_END, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_PGD, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_7, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_8, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_9, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_PLUS, termbox.ColorWhite, termbox.ColorBlue) + + draw_key(K_CAPS, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_a, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_s, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_d, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_f, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_g, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_h, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_j, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_k, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_l, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_SEMICOLON, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_QUOTE, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_4, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_5, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_6, termbox.ColorWhite, termbox.ColorBlue) + + draw_key(K_LSHIFT, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_z, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_x, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_c, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_v, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_b, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_n, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_m, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_COMMA, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_PERIOD, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_SLASH, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_RSHIFT, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_ARROW_UP, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_1, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_2, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_3, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_ENTER, termbox.ColorWhite, termbox.ColorBlue) + + draw_key(K_LCTRL, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_LWIN, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_LALT, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_SPACE, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_RCTRL, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_RPROP, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_RWIN, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_RALT, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_ARROW_LEFT, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_ARROW_DOWN, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_ARROW_RIGHT, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_0, termbox.ColorWhite, termbox.ColorBlue) + draw_key(K_K_PERIOD, termbox.ColorWhite, termbox.ColorBlue) + + printf_tb(33, 1, termbox.ColorMagenta|termbox.AttrBold, termbox.ColorBlack, "Keyboard demo!") + printf_tb(21, 2, termbox.ColorMagenta, termbox.ColorBlack, "(press CTRL+X and then CTRL+Q to exit)") + printf_tb(15, 3, termbox.ColorMagenta, termbox.ColorBlack, "(press CTRL+X and then CTRL+C to change input mode)") + + inputmode := termbox.SetInputMode(termbox.InputCurrent) + inputmode_str := "" + switch { + case inputmode&termbox.InputEsc != 0: + inputmode_str = "termbox.InputEsc" + case inputmode&termbox.InputAlt != 0: + inputmode_str = "termbox.InputAlt" + } + + if inputmode&termbox.InputMouse != 0 { + inputmode_str += " | termbox.InputMouse" + } + printf_tb(3, 18, termbox.ColorWhite, termbox.ColorBlack, "Input mode: %s", inputmode_str) +} + +var fcmap = []string{ + "CTRL+2, CTRL+~", + "CTRL+A", + "CTRL+B", + "CTRL+C", + "CTRL+D", + "CTRL+E", + "CTRL+F", + "CTRL+G", + "CTRL+H, BACKSPACE", + "CTRL+I, TAB", + "CTRL+J", + "CTRL+K", + "CTRL+L", + "CTRL+M, ENTER", + "CTRL+N", + "CTRL+O", + "CTRL+P", + "CTRL+Q", + "CTRL+R", + "CTRL+S", + "CTRL+T", + "CTRL+U", + "CTRL+V", + "CTRL+W", + "CTRL+X", + "CTRL+Y", + "CTRL+Z", + "CTRL+3, ESC, CTRL+[", + "CTRL+4, CTRL+\\", + "CTRL+5, CTRL+]", + "CTRL+6", + "CTRL+7, CTRL+/, CTRL+_", + "SPACE", +} + +var fkmap = []string{ + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "INSERT", + "DELETE", + "HOME", + "END", + "PGUP", + "PGDN", + "ARROW UP", + "ARROW DOWN", + "ARROW LEFT", + "ARROW RIGHT", +} + +func funckeymap(k termbox.Key) string { + if k == termbox.KeyCtrl8 { + return "CTRL+8, BACKSPACE 2" /* 0x7F */ + } else if k >= termbox.KeyArrowRight && k <= 0xFFFF { + return fkmap[0xFFFF-k] + } else if k <= termbox.KeySpace { + return fcmap[k] + } + return "UNKNOWN" +} + +func pretty_print_press(ev *termbox.Event) { + printf_tb(3, 19, termbox.ColorWhite, termbox.ColorBlack, "Key: ") + printf_tb(8, 19, termbox.ColorYellow, termbox.ColorBlack, "decimal: %d", ev.Key) + printf_tb(8, 20, termbox.ColorGreen, termbox.ColorBlack, "hex: 0x%X", ev.Key) + printf_tb(8, 21, termbox.ColorCyan, termbox.ColorBlack, "octal: 0%o", ev.Key) + printf_tb(8, 22, termbox.ColorRed, termbox.ColorBlack, "string: %s", funckeymap(ev.Key)) + + printf_tb(54, 19, termbox.ColorWhite, termbox.ColorBlack, "Char: ") + printf_tb(60, 19, termbox.ColorYellow, termbox.ColorBlack, "decimal: %d", ev.Ch) + printf_tb(60, 20, termbox.ColorGreen, termbox.ColorBlack, "hex: 0x%X", ev.Ch) + printf_tb(60, 21, termbox.ColorCyan, termbox.ColorBlack, "octal: 0%o", ev.Ch) + printf_tb(60, 22, termbox.ColorRed, termbox.ColorBlack, "string: %s", string(ev.Ch)) + + modifier := "none" + if ev.Mod != 0 { + modifier = "termbox.ModAlt" + } + printf_tb(54, 18, termbox.ColorWhite, termbox.ColorBlack, "Modifier: %s", modifier) +} + +func pretty_print_resize(ev *termbox.Event) { + printf_tb(3, 19, termbox.ColorWhite, termbox.ColorBlack, "Resize event: %d x %d", ev.Width, ev.Height) +} + +var counter = 0 + +func pretty_print_mouse(ev *termbox.Event) { + printf_tb(3, 19, termbox.ColorWhite, termbox.ColorBlack, "Mouse event: %d x %d", ev.MouseX, ev.MouseY) + button := "" + switch ev.Key { + case termbox.MouseLeft: + button = "MouseLeft: %d" + case termbox.MouseMiddle: + button = "MouseMiddle: %d" + case termbox.MouseRight: + button = "MouseRight: %d" + case termbox.MouseWheelUp: + button = "MouseWheelUp: %d" + case termbox.MouseWheelDown: + button = "MouseWheelDown: %d" + case termbox.MouseRelease: + button = "MouseRelease: %d" + } + if ev.Mod&termbox.ModMotion != 0 { + button += "*" + } + counter++ + printf_tb(43, 19, termbox.ColorWhite, termbox.ColorBlack, "Key: ") + printf_tb(48, 19, termbox.ColorYellow, termbox.ColorBlack, button, counter) +} + +func dispatch_press(ev *termbox.Event) { + if ev.Mod&termbox.ModAlt != 0 { + draw_key(K_LALT, termbox.ColorWhite, termbox.ColorRed) + draw_key(K_RALT, termbox.ColorWhite, termbox.ColorRed) + } + + var k *combo + if ev.Key >= termbox.KeyArrowRight { + k = &func_combos[0xFFFF-ev.Key] + } else if ev.Ch < 128 { + if ev.Ch == 0 && ev.Key < 128 { + k = &combos[ev.Key] + } else { + k = &combos[ev.Ch] + } + } + if k == nil { + return + } + + keys := k.keys + for _, k := range keys { + draw_key(k, termbox.ColorWhite, termbox.ColorRed) + } +} + +func main() { + err := termbox.Init() + if err != nil { + panic(err) + } + defer termbox.Close() + + termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse) + + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + draw_keyboard() + termbox.Flush() + inputmode := 0 + ctrlxpressed := false +loop: + for { + switch ev := termbox.PollEvent(); ev.Type { + case termbox.EventKey: + if ev.Key == termbox.KeyCtrlS && ctrlxpressed { + termbox.Sync() + } + if ev.Key == termbox.KeyCtrlQ && ctrlxpressed { + break loop + } + if ev.Key == termbox.KeyCtrlC && ctrlxpressed { + chmap := []termbox.InputMode{ + termbox.InputEsc | termbox.InputMouse, + termbox.InputAlt | termbox.InputMouse, + termbox.InputEsc, + termbox.InputAlt, + } + inputmode++ + if inputmode >= len(chmap) { + inputmode = 0 + } + termbox.SetInputMode(chmap[inputmode]) + } + if ev.Key == termbox.KeyCtrlX { + ctrlxpressed = true + } else { + ctrlxpressed = false + } + + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + draw_keyboard() + dispatch_press(&ev) + pretty_print_press(&ev) + termbox.Flush() + case termbox.EventResize: + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + draw_keyboard() + pretty_print_resize(&ev) + termbox.Flush() + case termbox.EventMouse: + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + draw_keyboard() + pretty_print_mouse(&ev) + termbox.Flush() + case termbox.EventError: + panic(ev.Err) + } + } +} diff --git a/vendor/github.com/nsf/termbox-go/_demos/output.go b/vendor/github.com/nsf/termbox-go/_demos/output.go new file mode 100644 index 0000000..2b9479b --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/_demos/output.go @@ -0,0 +1,228 @@ +package main + +import "github.com/mattn/go-runewidth" +import "github.com/nsf/termbox-go" + +const chars = "nnnnnnnnnbbbbbbbbbuuuuuuuuuBBBBBBBBB" + +var output_mode = termbox.OutputNormal + +func next_char(current int) int { + current++ + if current >= len(chars) { + return 0 + } + return current +} + +func print_combinations_table(sx, sy int, attrs []termbox.Attribute) { + var bg termbox.Attribute + current_char := 0 + y := sy + + all_attrs := []termbox.Attribute{ + 0, + termbox.AttrBold, + termbox.AttrUnderline, + termbox.AttrBold | termbox.AttrUnderline, + } + + draw_line := func() { + x := sx + for _, a := range all_attrs { + for c := termbox.ColorDefault; c <= termbox.ColorWhite; c++ { + fg := a | c + termbox.SetCell(x, y, rune(chars[current_char]), fg, bg) + current_char = next_char(current_char) + x++ + } + } + } + + for _, a := range attrs { + for c := termbox.ColorDefault; c <= termbox.ColorWhite; c++ { + bg = a | c + draw_line() + y++ + } + } +} + +func print_wide(x, y int, s string) { + red := false + for _, r := range s { + c := termbox.ColorDefault + if red { + c = termbox.ColorRed + } + termbox.SetCell(x, y, r, termbox.ColorDefault, c) + w := runewidth.RuneWidth(r) + if w == 0 || (w == 2 && runewidth.IsAmbiguousWidth(r)) { + w = 1 + } + x += w + + red = !red + } +} + +const hello_world = "こんにちは世界" + +func draw_all() { + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + + switch output_mode { + + case termbox.OutputNormal: + print_combinations_table(1, 1, []termbox.Attribute{ + 0, + termbox.AttrBold, + }) + print_combinations_table(2+len(chars), 1, []termbox.Attribute{ + termbox.AttrReverse, + }) + print_wide(2+len(chars), 11, hello_world) + + case termbox.OutputGrayscale: + for y := 0; y < 26; y++ { + for x := 0; x < 26; x++ { + termbox.SetCell(x, y, 'n', + termbox.Attribute(x+1), + termbox.Attribute(y+1)) + termbox.SetCell(x+27, y, 'b', + termbox.Attribute(x+1)|termbox.AttrBold, + termbox.Attribute(26-y)) + termbox.SetCell(x+54, y, 'u', + termbox.Attribute(x+1)|termbox.AttrUnderline, + termbox.Attribute(y+1)) + } + termbox.SetCell(82, y, 'd', + termbox.Attribute(y+1), + termbox.ColorDefault) + termbox.SetCell(83, y, 'd', + termbox.ColorDefault, + termbox.Attribute(26-y)) + } + + case termbox.Output216: + for r := 0; r < 6; r++ { + for g := 0; g < 6; g++ { + for b := 0; b < 6; b++ { + y := r + x := g + 6*b + c1 := termbox.Attribute(1 + r*36 + g*6 + b) + bg := termbox.Attribute(1 + g*36 + b*6 + r) + c2 := termbox.Attribute(1 + b*36 + r*6 + g) + bc1 := c1 | termbox.AttrBold + uc1 := c1 | termbox.AttrUnderline + bc2 := c2 | termbox.AttrBold + uc2 := c2 | termbox.AttrUnderline + termbox.SetCell(x, y, 'n', c1, bg) + termbox.SetCell(x, y+6, 'b', bc1, bg) + termbox.SetCell(x, y+12, 'u', uc1, bg) + termbox.SetCell(x, y+18, 'B', bc1|uc1, bg) + termbox.SetCell(x+37, y, 'n', c2, bg) + termbox.SetCell(x+37, y+6, 'b', bc2, bg) + termbox.SetCell(x+37, y+12, 'u', uc2, bg) + termbox.SetCell(x+37, y+18, 'B', bc2|uc2, bg) + } + c1 := termbox.Attribute(1 + g*6 + r*36) + c2 := termbox.Attribute(6 + g*6 + r*36) + termbox.SetCell(74+g, r, 'd', c1, termbox.ColorDefault) + termbox.SetCell(74+g, r+6, 'd', c2, termbox.ColorDefault) + termbox.SetCell(74+g, r+12, 'd', termbox.ColorDefault, c1) + termbox.SetCell(74+g, r+18, 'd', termbox.ColorDefault, c2) + } + } + + case termbox.Output256: + for y := 0; y < 4; y++ { + for x := 0; x < 8; x++ { + for z := 0; z < 8; z++ { + bg := termbox.Attribute(1 + y*64 + x*8 + z) + c1 := termbox.Attribute(256 - y*64 - x*8 - z) + c2 := termbox.Attribute(1 + y*64 + z*8 + x) + c3 := termbox.Attribute(256 - y*64 - z*8 - x) + c4 := termbox.Attribute(1 + y*64 + x*4 + z*4) + bold := c2 | termbox.AttrBold + under := c3 | termbox.AttrUnderline + both := c1 | termbox.AttrBold | termbox.AttrUnderline + termbox.SetCell(z+8*x, y, ' ', 0, bg) + termbox.SetCell(z+8*x, y+5, 'n', c4, bg) + termbox.SetCell(z+8*x, y+10, 'b', bold, bg) + termbox.SetCell(z+8*x, y+15, 'u', under, bg) + termbox.SetCell(z+8*x, y+20, 'B', both, bg) + } + } + } + for x := 0; x < 12; x++ { + for y := 0; y < 2; y++ { + c1 := termbox.Attribute(233 + y*12 + x) + termbox.SetCell(66+x, y, 'd', c1, termbox.ColorDefault) + termbox.SetCell(66+x, 2+y, 'd', termbox.ColorDefault, c1) + } + } + for x := 0; x < 6; x++ { + for y := 0; y < 6; y++ { + c1 := termbox.Attribute(17 + x*6 + y*36) + c2 := termbox.Attribute(17 + 5 + x*6 + y*36) + termbox.SetCell(66+x, 6+y, 'd', c1, termbox.ColorDefault) + termbox.SetCell(66+x, 12+y, 'd', c2, termbox.ColorDefault) + termbox.SetCell(72+x, 6+y, 'd', termbox.ColorDefault, c1) + termbox.SetCell(72+x, 12+y, 'd', termbox.ColorDefault, c2) + } + } + + } + + termbox.Flush() +} + +var available_modes = []termbox.OutputMode{ + termbox.OutputNormal, + termbox.OutputGrayscale, + termbox.Output216, + termbox.Output256, +} + +var output_mode_index = 0 + +func switch_output_mode(direction int) { + output_mode_index += direction + if output_mode_index < 0 { + output_mode_index = len(available_modes) - 1 + } else if output_mode_index >= len(available_modes) { + output_mode_index = 0 + } + output_mode = termbox.SetOutputMode(available_modes[output_mode_index]) + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + termbox.Sync() +} + +func main() { + err := termbox.Init() + if err != nil { + panic(err) + } + defer termbox.Close() + + draw_all() +loop: + for { + switch ev := termbox.PollEvent(); ev.Type { + case termbox.EventKey: + switch ev.Key { + case termbox.KeyEsc: + break loop + case termbox.KeyArrowUp, termbox.KeyArrowRight: + switch_output_mode(1) + draw_all() + case termbox.KeyArrowDown, termbox.KeyArrowLeft: + switch_output_mode(-1) + draw_all() + } + case termbox.EventResize: + draw_all() + } + } +} diff --git a/vendor/github.com/nsf/termbox-go/_demos/paint.go b/vendor/github.com/nsf/termbox-go/_demos/paint.go new file mode 100644 index 0000000..fbafd18 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/_demos/paint.go @@ -0,0 +1,105 @@ +package main + +import ( + "github.com/nsf/termbox-go" +) + +var curCol = 0 +var curRune = 0 +var backbuf []termbox.Cell +var bbw, bbh int + +var runes = []rune{' ', '░', '▒', '▓', '█'} +var colors = []termbox.Attribute{ + termbox.ColorBlack, + termbox.ColorRed, + termbox.ColorGreen, + termbox.ColorYellow, + termbox.ColorBlue, + termbox.ColorMagenta, + termbox.ColorCyan, + termbox.ColorWhite, +} + +type attrFunc func(int) (rune, termbox.Attribute, termbox.Attribute) + +func updateAndDrawButtons(current *int, x, y int, mx, my int, n int, attrf attrFunc) { + lx, ly := x, y + for i := 0; i < n; i++ { + if lx <= mx && mx <= lx+3 && ly <= my && my <= ly+1 { + *current = i + } + r, fg, bg := attrf(i) + termbox.SetCell(lx+0, ly+0, r, fg, bg) + termbox.SetCell(lx+1, ly+0, r, fg, bg) + termbox.SetCell(lx+2, ly+0, r, fg, bg) + termbox.SetCell(lx+3, ly+0, r, fg, bg) + termbox.SetCell(lx+0, ly+1, r, fg, bg) + termbox.SetCell(lx+1, ly+1, r, fg, bg) + termbox.SetCell(lx+2, ly+1, r, fg, bg) + termbox.SetCell(lx+3, ly+1, r, fg, bg) + lx += 4 + } + lx, ly = x, y + for i := 0; i < n; i++ { + if *current == i { + fg := termbox.ColorRed | termbox.AttrBold + bg := termbox.ColorDefault + termbox.SetCell(lx+0, ly+2, '^', fg, bg) + termbox.SetCell(lx+1, ly+2, '^', fg, bg) + termbox.SetCell(lx+2, ly+2, '^', fg, bg) + termbox.SetCell(lx+3, ly+2, '^', fg, bg) + } + lx += 4 + } +} + +func update_and_redraw_all(mx, my int) { + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + if mx != -1 && my != -1 { + backbuf[bbw*my+mx] = termbox.Cell{Ch: runes[curRune], Fg: colors[curCol]} + } + copy(termbox.CellBuffer(), backbuf) + _, h := termbox.Size() + updateAndDrawButtons(&curRune, 0, 0, mx, my, len(runes), func(i int) (rune, termbox.Attribute, termbox.Attribute) { + return runes[i], termbox.ColorDefault, termbox.ColorDefault + }) + updateAndDrawButtons(&curCol, 0, h-3, mx, my, len(colors), func(i int) (rune, termbox.Attribute, termbox.Attribute) { + return ' ', termbox.ColorDefault, colors[i] + }) + termbox.Flush() +} + +func reallocBackBuffer(w, h int) { + bbw, bbh = w, h + backbuf = make([]termbox.Cell, w*h) +} + +func main() { + err := termbox.Init() + if err != nil { + panic(err) + } + defer termbox.Close() + termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse) + reallocBackBuffer(termbox.Size()) + update_and_redraw_all(-1, -1) + +mainloop: + for { + mx, my := -1, -1 + switch ev := termbox.PollEvent(); ev.Type { + case termbox.EventKey: + if ev.Key == termbox.KeyEsc { + break mainloop + } + case termbox.EventMouse: + if ev.Key == termbox.MouseLeft { + mx, my = ev.MouseX, ev.MouseY + } + case termbox.EventResize: + reallocBackBuffer(ev.Width, ev.Height) + } + update_and_redraw_all(mx, my) + } +} diff --git a/vendor/github.com/nsf/termbox-go/_demos/random_output.go b/vendor/github.com/nsf/termbox-go/_demos/random_output.go new file mode 100644 index 0000000..efcf0b7 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/_demos/random_output.go @@ -0,0 +1,46 @@ +package main + +import "github.com/nsf/termbox-go" +import "math/rand" +import "time" + +func draw() { + w, h := termbox.Size() + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + termbox.SetCell(x, y, ' ', termbox.ColorDefault, + termbox.Attribute(rand.Int()%8)+1) + } + } + termbox.Flush() +} + +func main() { + err := termbox.Init() + if err != nil { + panic(err) + } + defer termbox.Close() + + event_queue := make(chan termbox.Event) + go func() { + for { + event_queue <- termbox.PollEvent() + } + }() + + draw() +loop: + for { + select { + case ev := <-event_queue: + if ev.Type == termbox.EventKey && ev.Key == termbox.KeyEsc { + break loop + } + default: + draw() + time.Sleep(10 * time.Millisecond) + } + } +} diff --git a/vendor/github.com/nsf/termbox-go/_demos/raw_input.go b/vendor/github.com/nsf/termbox-go/_demos/raw_input.go new file mode 100644 index 0000000..97a4897 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/_demos/raw_input.go @@ -0,0 +1,109 @@ +package main + +import ( + "fmt" + "github.com/nsf/termbox-go" + "strings" +) + +func tbprint(x, y int, fg, bg termbox.Attribute, msg string) { + for _, c := range msg { + termbox.SetCell(x, y, c, fg, bg) + x++ + } +} + +var current string +var curev termbox.Event + +func mouse_button_str(k termbox.Key) string { + switch k { + case termbox.MouseLeft: + return "MouseLeft" + case termbox.MouseMiddle: + return "MouseMiddle" + case termbox.MouseRight: + return "MouseRight" + case termbox.MouseRelease: + return "MouseRelease" + case termbox.MouseWheelUp: + return "MouseWheelUp" + case termbox.MouseWheelDown: + return "MouseWheelDown" + } + return "Key" +} + +func mod_str(m termbox.Modifier) string { + var out []string + if m&termbox.ModAlt != 0 { + out = append(out, "ModAlt") + } + if m&termbox.ModMotion != 0 { + out = append(out, "ModMotion") + } + return strings.Join(out, " | ") +} + +func redraw_all() { + const coldef = termbox.ColorDefault + termbox.Clear(coldef, coldef) + tbprint(0, 0, termbox.ColorMagenta, coldef, "Press 'q' to quit") + tbprint(0, 1, coldef, coldef, current) + switch curev.Type { + case termbox.EventKey: + tbprint(0, 2, coldef, coldef, + fmt.Sprintf("EventKey: k: %d, c: %c, mod: %s", curev.Key, curev.Ch, mod_str(curev.Mod))) + case termbox.EventMouse: + tbprint(0, 2, coldef, coldef, + fmt.Sprintf("EventMouse: x: %d, y: %d, b: %s, mod: %s", + curev.MouseX, curev.MouseY, mouse_button_str(curev.Key), mod_str(curev.Mod))) + case termbox.EventNone: + tbprint(0, 2, coldef, coldef, "EventNone") + } + tbprint(0, 3, coldef, coldef, fmt.Sprintf("%d", curev.N)) + termbox.Flush() +} + +func main() { + err := termbox.Init() + if err != nil { + panic(err) + } + defer termbox.Close() + termbox.SetInputMode(termbox.InputAlt | termbox.InputMouse) + redraw_all() + + data := make([]byte, 0, 64) +mainloop: + for { + if cap(data)-len(data) < 32 { + newdata := make([]byte, len(data), len(data)+32) + copy(newdata, data) + data = newdata + } + beg := len(data) + d := data[beg : beg+32] + switch ev := termbox.PollRawEvent(d); ev.Type { + case termbox.EventRaw: + data = data[:beg+ev.N] + current = fmt.Sprintf("%q", data) + if current == `"q"` { + break mainloop + } + + for { + ev := termbox.ParseEvent(data) + if ev.N == 0 { + break + } + curev = ev + copy(data, data[curev.N:]) + data = data[:len(data)-curev.N] + } + case termbox.EventError: + panic(ev.Err) + } + redraw_all() + } +} diff --git a/vendor/github.com/nsf/termbox-go/api.go b/vendor/github.com/nsf/termbox-go/api.go new file mode 100644 index 0000000..b242ddc --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/api.go @@ -0,0 +1,457 @@ +// +build !windows + +package termbox + +import "github.com/mattn/go-runewidth" +import "fmt" +import "os" +import "os/signal" +import "syscall" +import "runtime" + +// 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 + + out, err = os.OpenFile("/dev/tty", syscall.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} + ok := extract_event(data, &event) + if !ok { + 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 { + var event Event + + // try to extract event from input buffer, return on success + event.Type = EventKey + ok := extract_event(inbuf, &event) + if event.N != 0 { + copy(inbuf, inbuf[event.N:]) + inbuf = inbuf[:len(inbuf)-event.N] + } + if ok { + 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 + ok := extract_event(inbuf, &event) + if event.N != 0 { + copy(inbuf, inbuf[event.N:]) + inbuf = inbuf[:len(inbuf)-event.N] + } + if ok { + 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 dont 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 dont 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/vendor/github.com/nsf/termbox-go/api_common.go b/vendor/github.com/nsf/termbox-go/api_common.go new file mode 100644 index 0000000..9f23661 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/api_common.go @@ -0,0 +1,187 @@ +// termbox is a library for creating cross-platform text-based interfaces +package termbox + +// public API, common OS agnostic part + +type ( + InputMode int + OutputMode int + EventType uint8 + Modifier uint8 + Key uint16 + Attribute uint16 +) + +// This type represents a termbox event. The 'Mod', 'Key' and 'Ch' fields are +// valid if 'Type' is EventKey. The 'Width' and 'Height' fields are valid if +// 'Type' is EventResize. The 'Err' field is valid if 'Type' is EventError. +type Event struct { + Type EventType // one of Event* constants + Mod Modifier // one of Mod* constants or 0 + Key Key // one of Key* constants, invalid if 'Ch' is not 0 + Ch rune // a unicode character + Width int // width of the screen + Height int // height of the screen + Err error // error in case if input failed + MouseX int // x coord of mouse + MouseY int // y coord of mouse + N int // number of bytes written when getting a raw event +} + +// A cell, single conceptual entity on the screen. The screen is basically a 2d +// array of cells. 'Ch' is a unicode character, 'Fg' and 'Bg' are foreground +// and background attributes respectively. +type Cell struct { + Ch rune + Fg Attribute + Bg Attribute +} + +// To know if termbox has been initialized or not +var ( + IsInit bool = false +) + +// Key constants, see Event.Key field. +const ( + KeyF1 Key = 0xFFFF - iota + KeyF2 + KeyF3 + KeyF4 + KeyF5 + KeyF6 + KeyF7 + KeyF8 + KeyF9 + KeyF10 + KeyF11 + KeyF12 + KeyInsert + KeyDelete + KeyHome + KeyEnd + KeyPgup + KeyPgdn + KeyArrowUp + KeyArrowDown + KeyArrowLeft + KeyArrowRight + key_min // see terminfo + MouseLeft + MouseMiddle + MouseRight + MouseRelease + MouseWheelUp + MouseWheelDown +) + +const ( + KeyCtrlTilde Key = 0x00 + KeyCtrl2 Key = 0x00 + KeyCtrlSpace Key = 0x00 + KeyCtrlA Key = 0x01 + KeyCtrlB Key = 0x02 + KeyCtrlC Key = 0x03 + KeyCtrlD Key = 0x04 + KeyCtrlE Key = 0x05 + KeyCtrlF Key = 0x06 + KeyCtrlG Key = 0x07 + KeyBackspace Key = 0x08 + KeyCtrlH Key = 0x08 + KeyTab Key = 0x09 + KeyCtrlI Key = 0x09 + KeyCtrlJ Key = 0x0A + KeyCtrlK Key = 0x0B + KeyCtrlL Key = 0x0C + KeyEnter Key = 0x0D + KeyCtrlM Key = 0x0D + KeyCtrlN Key = 0x0E + KeyCtrlO Key = 0x0F + KeyCtrlP Key = 0x10 + KeyCtrlQ Key = 0x11 + KeyCtrlR Key = 0x12 + KeyCtrlS Key = 0x13 + KeyCtrlT Key = 0x14 + KeyCtrlU Key = 0x15 + KeyCtrlV Key = 0x16 + KeyCtrlW Key = 0x17 + KeyCtrlX Key = 0x18 + KeyCtrlY Key = 0x19 + KeyCtrlZ Key = 0x1A + KeyEsc Key = 0x1B + KeyCtrlLsqBracket Key = 0x1B + KeyCtrl3 Key = 0x1B + KeyCtrl4 Key = 0x1C + KeyCtrlBackslash Key = 0x1C + KeyCtrl5 Key = 0x1D + KeyCtrlRsqBracket Key = 0x1D + KeyCtrl6 Key = 0x1E + KeyCtrl7 Key = 0x1F + KeyCtrlSlash Key = 0x1F + KeyCtrlUnderscore Key = 0x1F + KeySpace Key = 0x20 + KeyBackspace2 Key = 0x7F + KeyCtrl8 Key = 0x7F +) + +// Alt modifier constant, see Event.Mod field and SetInputMode function. +const ( + ModAlt Modifier = 1 << iota + ModMotion +) + +// Cell colors, you can combine a color with multiple attributes using bitwise +// OR ('|'). +const ( + ColorDefault Attribute = iota + ColorBlack + ColorRed + ColorGreen + ColorYellow + ColorBlue + ColorMagenta + ColorCyan + ColorWhite +) + +// Cell attributes, it is possible to use multiple attributes by combining them +// using bitwise OR ('|'). Although, colors cannot be combined. But you can +// combine attributes and a single color. +// +// It's worth mentioning that some platforms don't support certain attibutes. +// 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/vendor/github.com/nsf/termbox-go/api_windows.go b/vendor/github.com/nsf/termbox-go/api_windows.go new file mode 100644 index 0000000..7def30a --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/api_windows.go @@ -0,0 +1,239 @@ +package termbox + +import ( + "syscall" +) + +// public API + +// Initializes termbox library. This function should be called before any other functions. +// After successful initialization, the library must be finalized using 'Close' function. +// +// Example usage: +// err := termbox.Init() +// if err != nil { +// panic(err) +// } +// defer termbox.Close() +func Init() error { + var err error + + interrupt, err = create_event() + if err != nil { + return err + } + + in, err = syscall.Open("CONIN$", syscall.O_RDWR, 0) + if err != nil { + return err + } + out, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0) + if err != nil { + return err + } + + err = get_console_mode(in, &orig_mode) + if err != nil { + return err + } + + err = set_console_mode(in, enable_window_input) + if err != nil { + return err + } + + orig_size = 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 = 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_cursor_info(out, &orig_cursor_info) + set_console_cursor_position(out, coord{}) + set_console_screen_buffer_size(out, orig_size) + set_console_mode(in, orig_mode) + syscall.Close(in) + syscall.Close(out) + syscall.Close(interrupt) + IsInit = false +} + +// Interrupt an in-progress call to PollEvent by causing it to return +// EventInterrupt. Note that this function will block until the PollEvent +// function has successfully been interrupted. +func Interrupt() { + interrupt_comm <- struct{}{} +} + +// Synchronizes the internal back buffer with the terminal. +func Flush() error { + update_size_maybe() + prepare_diff_messages() + for _, diff := range diffbuf { + r := small_rect{ + left: 0, + top: diff.pos, + right: term_size.x - 1, + bottom: diff.pos + diff.lines - 1, + } + write_console_output(out, diff.chars, r) + } + if !is_cursor_hidden(cursor_x, cursor_y) { + move_cursor(cursor_x, cursor_y) + } + return nil +} + +// Sets the position of the cursor. See also HideCursor(). +func SetCursor(x, y int) { + if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) { + show_cursor(true) + } + + if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) { + show_cursor(false) + } + + cursor_x, cursor_y = x, y + if !is_cursor_hidden(cursor_x, cursor_y) { + move_cursor(cursor_x, cursor_y) + } +} + +// The shortcut for SetCursor(-1, -1). +func HideCursor() { + SetCursor(cursor_hidden, cursor_hidden) +} + +// Changes cell's parameters in the internal back buffer at the specified +// position. +func SetCell(x, y int, ch rune, fg, bg Attribute) { + if x < 0 || x >= back_buffer.width { + return + } + if y < 0 || y >= back_buffer.height { + return + } + + back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg} +} + +// Returns a slice into the termbox's back buffer. You can get its dimensions +// using 'Size' function. The slice remains valid as long as no 'Clear' or +// 'Flush' function calls were made after call to this function. +func CellBuffer() []Cell { + return back_buffer.cells +} + +// Wait for an event and return it. This is a blocking function call. +func PollEvent() Event { + select { + case ev := <-input_comm: + return ev + case <-interrupt_comm: + return Event{Type: EventInterrupt} + } +} + +// Returns the size of the internal back buffer (which is mostly the same as +// console's window size in characters). But it doesn't always match the size +// of the console window, after the console size has changed, the internal back +// buffer will get in sync only after Clear or Flush function calls. +func Size() (int, int) { + return int(term_size.x), int(term_size.y) +} + +// Clears the internal back buffer. +func Clear(fg, bg Attribute) error { + foreground, background = fg, bg + update_size_maybe() + back_buffer.clear() + return nil +} + +// Sets termbox input mode. Termbox has two input modes: +// +// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match +// any known sequence. ESC means KeyEsc. This is the default input mode. +// +// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match +// any known sequence. ESC enables ModAlt modifier for the next keyboard event. +// +// Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will +// enable mouse button press/release and drag events. +// +// If 'mode' is InputCurrent, returns the current input mode. See also Input* +// constants. +func SetInputMode(mode InputMode) InputMode { + if mode == InputCurrent { + return input_mode + } + if mode&InputMouse != 0 { + err := set_console_mode(in, enable_window_input|enable_mouse_input|enable_extended_flags) + if err != nil { + panic(err) + } + } else { + err := set_console_mode(in, enable_window_input) + if err != nil { + panic(err) + } + } + + input_mode = mode + return input_mode +} + +// Sets the termbox output mode. +// +// Windows console does not support extra colour modes, +// so this will always set and return OutputNormal. +func SetOutputMode(mode OutputMode) OutputMode { + return OutputNormal +} + +// Sync comes handy when something causes desync between termbox's understanding +// of a terminal buffer and the reality. Such as a third party process. Sync +// forces a complete resync between the termbox and a terminal, it may not be +// visually pretty though. At the moment on Windows it does nothing. +func Sync() error { + return nil +} diff --git a/vendor/github.com/nsf/termbox-go/collect_terminfo.py b/vendor/github.com/nsf/termbox-go/collect_terminfo.py new file mode 100644 index 0000000..5e50975 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/collect_terminfo.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +import sys, os, subprocess + +def escaped(s): + return repr(s)[1:-1] + +def tput(term, name): + try: + return subprocess.check_output(['tput', '-T%s' % term, name]).decode() + except subprocess.CalledProcessError as e: + return e.output.decode() + + +def w(s): + if s == None: + return + sys.stdout.write(s) + +terminals = { + 'xterm' : 'xterm', + 'rxvt-256color' : 'rxvt_256color', + 'rxvt-unicode' : 'rxvt_unicode', + 'linux' : 'linux', + 'Eterm' : 'eterm', + 'screen' : 'screen' +} + +keys = [ + "F1", "kf1", + "F2", "kf2", + "F3", "kf3", + "F4", "kf4", + "F5", "kf5", + "F6", "kf6", + "F7", "kf7", + "F8", "kf8", + "F9", "kf9", + "F10", "kf10", + "F11", "kf11", + "F12", "kf12", + "INSERT", "kich1", + "DELETE", "kdch1", + "HOME", "khome", + "END", "kend", + "PGUP", "kpp", + "PGDN", "knp", + "KEY_UP", "kcuu1", + "KEY_DOWN", "kcud1", + "KEY_LEFT", "kcub1", + "KEY_RIGHT", "kcuf1" +] + +funcs = [ + "T_ENTER_CA", "smcup", + "T_EXIT_CA", "rmcup", + "T_SHOW_CURSOR", "cnorm", + "T_HIDE_CURSOR", "civis", + "T_CLEAR_SCREEN", "clear", + "T_SGR0", "sgr0", + "T_UNDERLINE", "smul", + "T_BOLD", "bold", + "T_BLINK", "blink", + "T_REVERSE", "rev", + "T_ENTER_KEYPAD", "smkx", + "T_EXIT_KEYPAD", "rmkx" +] + +def iter_pairs(iterable): + iterable = iter(iterable) + while True: + yield (next(iterable), next(iterable)) + +def do_term(term, nick): + w("// %s\n" % term) + w("var %s_keys = []string{\n\t" % nick) + for k, v in iter_pairs(keys): + w('"') + w(escaped(tput(term, v))) + w('",') + w("\n}\n") + w("var %s_funcs = []string{\n\t" % nick) + for k,v in iter_pairs(funcs): + w('"') + if v == "sgr": + w("\\033[3%d;4%dm") + elif v == "cup": + w("\\033[%d;%dH") + else: + w(escaped(tput(term, v))) + w('", ') + w("\n}\n\n") + +def do_terms(d): + w("var terms = []struct {\n") + w("\tname string\n") + w("\tkeys []string\n") + w("\tfuncs []string\n") + w("}{\n") + for k, v in d.items(): + w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v)) + w("}\n\n") + +w("// +build !windows\n\npackage termbox\n\n") + +for k,v in terminals.items(): + do_term(k, v) + +do_terms(terminals) + diff --git a/vendor/github.com/nsf/termbox-go/syscalls.go b/vendor/github.com/nsf/termbox-go/syscalls.go new file mode 100644 index 0000000..4f52bb9 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/syscalls.go @@ -0,0 +1,39 @@ +// +build ignore + +package termbox + +/* +#include +#include +*/ +import "C" + +type syscall_Termios C.struct_termios + +const ( + syscall_IGNBRK = C.IGNBRK + syscall_BRKINT = C.BRKINT + syscall_PARMRK = C.PARMRK + syscall_ISTRIP = C.ISTRIP + syscall_INLCR = C.INLCR + syscall_IGNCR = C.IGNCR + syscall_ICRNL = C.ICRNL + syscall_IXON = C.IXON + syscall_OPOST = C.OPOST + syscall_ECHO = C.ECHO + syscall_ECHONL = C.ECHONL + syscall_ICANON = C.ICANON + syscall_ISIG = C.ISIG + syscall_IEXTEN = C.IEXTEN + syscall_CSIZE = C.CSIZE + syscall_PARENB = C.PARENB + syscall_CS8 = C.CS8 + syscall_VMIN = C.VMIN + syscall_VTIME = C.VTIME + + // on darwin change these to (on *bsd too?): + // C.TIOCGETA + // C.TIOCSETA + syscall_TCGETS = C.TCGETS + syscall_TCSETS = C.TCSETS +) diff --git a/vendor/github.com/nsf/termbox-go/syscalls_darwin.go b/vendor/github.com/nsf/termbox-go/syscalls_darwin.go new file mode 100644 index 0000000..25b78f7 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/syscalls_darwin.go @@ -0,0 +1,41 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +// +build !amd64 + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go b/vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go new file mode 100644 index 0000000..11f25be --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go @@ -0,0 +1,40 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint64 + Oflag uint64 + Cflag uint64 + Lflag uint64 + Cc [20]uint8 + Pad_cgo_0 [4]byte + Ispeed uint64 + Ospeed uint64 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x40487413 + syscall_TCSETS = 0x80487414 +) diff --git a/vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go b/vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go new file mode 100644 index 0000000..e03624e --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go @@ -0,0 +1,39 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/nsf/termbox-go/syscalls_freebsd.go b/vendor/github.com/nsf/termbox-go/syscalls_freebsd.go new file mode 100644 index 0000000..e03624e --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/syscalls_freebsd.go @@ -0,0 +1,39 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/nsf/termbox-go/syscalls_linux.go b/vendor/github.com/nsf/termbox-go/syscalls_linux.go new file mode 100644 index 0000000..b88960d --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/syscalls_linux.go @@ -0,0 +1,33 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +import "syscall" + +type syscall_Termios syscall.Termios + +const ( + syscall_IGNBRK = syscall.IGNBRK + syscall_BRKINT = syscall.BRKINT + syscall_PARMRK = syscall.PARMRK + syscall_ISTRIP = syscall.ISTRIP + syscall_INLCR = syscall.INLCR + syscall_IGNCR = syscall.IGNCR + syscall_ICRNL = syscall.ICRNL + syscall_IXON = syscall.IXON + syscall_OPOST = syscall.OPOST + syscall_ECHO = syscall.ECHO + syscall_ECHONL = syscall.ECHONL + syscall_ICANON = syscall.ICANON + syscall_ISIG = syscall.ISIG + syscall_IEXTEN = syscall.IEXTEN + syscall_CSIZE = syscall.CSIZE + syscall_PARENB = syscall.PARENB + syscall_CS8 = syscall.CS8 + syscall_VMIN = syscall.VMIN + syscall_VTIME = syscall.VTIME + + syscall_TCGETS = syscall.TCGETS + syscall_TCSETS = syscall.TCSETS +) diff --git a/vendor/github.com/nsf/termbox-go/syscalls_netbsd.go b/vendor/github.com/nsf/termbox-go/syscalls_netbsd.go new file mode 100644 index 0000000..49a3355 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/syscalls_netbsd.go @@ -0,0 +1,39 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed int32 + Ospeed int32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/nsf/termbox-go/syscalls_openbsd.go b/vendor/github.com/nsf/termbox-go/syscalls_openbsd.go new file mode 100644 index 0000000..49a3355 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/syscalls_openbsd.go @@ -0,0 +1,39 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed int32 + Ospeed int32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/nsf/termbox-go/syscalls_windows.go b/vendor/github.com/nsf/termbox-go/syscalls_windows.go new file mode 100644 index 0000000..472d002 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/syscalls_windows.go @@ -0,0 +1,61 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs -- -DUNICODE syscalls.go + +package termbox + +const ( + foreground_blue = 0x1 + foreground_green = 0x2 + foreground_red = 0x4 + foreground_intensity = 0x8 + background_blue = 0x10 + background_green = 0x20 + background_red = 0x40 + background_intensity = 0x80 + std_input_handle = -0xa + std_output_handle = -0xb + key_event = 0x1 + mouse_event = 0x2 + window_buffer_size_event = 0x4 + enable_window_input = 0x8 + enable_mouse_input = 0x10 + enable_extended_flags = 0x80 + + vk_f1 = 0x70 + vk_f2 = 0x71 + vk_f3 = 0x72 + vk_f4 = 0x73 + vk_f5 = 0x74 + vk_f6 = 0x75 + vk_f7 = 0x76 + vk_f8 = 0x77 + vk_f9 = 0x78 + vk_f10 = 0x79 + vk_f11 = 0x7a + vk_f12 = 0x7b + vk_insert = 0x2d + vk_delete = 0x2e + vk_home = 0x24 + vk_end = 0x23 + vk_pgup = 0x21 + vk_pgdn = 0x22 + vk_arrow_up = 0x26 + vk_arrow_down = 0x28 + vk_arrow_left = 0x25 + vk_arrow_right = 0x27 + vk_backspace = 0x8 + vk_tab = 0x9 + vk_enter = 0xd + vk_esc = 0x1b + vk_space = 0x20 + + left_alt_pressed = 0x2 + left_ctrl_pressed = 0x8 + right_alt_pressed = 0x1 + right_ctrl_pressed = 0x4 + shift_pressed = 0x10 + + generic_read = 0x80000000 + generic_write = 0x40000000 + console_textmode_buffer = 0x1 +) diff --git a/vendor/github.com/nsf/termbox-go/termbox.go b/vendor/github.com/nsf/termbox-go/termbox.go new file mode 100644 index 0000000..c2d86c6 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/termbox.go @@ -0,0 +1,511 @@ +// +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 +} + +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 seqences + 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) bool { + if len(inbuf) == 0 { + event.N = 0 + return false + } + + if inbuf[0] == '\033' { + // possible escape sequence + if n, ok := parse_escape_sequence(event, inbuf); n != 0 { + event.N = n + return ok + } + + // 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 true + case input_mode&InputAlt != 0: + // if we're in alt mode, set Alt modifier to event and redo parsing + event.Mod = ModAlt + ok := extract_event(inbuf[1:], event) + if ok { + event.N++ + } else { + event.N = 0 + } + return ok + 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 true + } + + // 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 true + } + + return false +} + +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/vendor/github.com/nsf/termbox-go/termbox_common.go b/vendor/github.com/nsf/termbox-go/termbox_common.go new file mode 100644 index 0000000..c3355cc --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/termbox_common.go @@ -0,0 +1,59 @@ +package termbox + +// private API, common OS agnostic part + +type cellbuf struct { + width int + height int + cells []Cell +} + +func (this *cellbuf) init(width, height int) { + this.width = width + this.height = height + this.cells = make([]Cell, width*height) +} + +func (this *cellbuf) resize(width, height int) { + if this.width == width && this.height == height { + return + } + + oldw := this.width + oldh := this.height + oldcells := this.cells + + this.init(width, height) + this.clear() + + minw, minh := oldw, oldh + + if width < minw { + minw = width + } + if height < minh { + minh = height + } + + for i := 0; i < minh; i++ { + srco, dsto := i*oldw, i*width + src := oldcells[srco : srco+minw] + dst := this.cells[dsto : dsto+minw] + copy(dst, src) + } +} + +func (this *cellbuf) clear() { + for i := range this.cells { + c := &this.cells[i] + c.Ch = ' ' + c.Fg = foreground + c.Bg = background + } +} + +const cursor_hidden = -1 + +func is_cursor_hidden(x, y int) bool { + return x == cursor_hidden || y == cursor_hidden +} diff --git a/vendor/github.com/nsf/termbox-go/termbox_windows.go b/vendor/github.com/nsf/termbox-go/termbox_windows.go new file mode 100644 index 0000000..f7dad7b --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/termbox_windows.go @@ -0,0 +1,856 @@ +package termbox + +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 + } +) + +const ( + mouse_lmb = 0x1 + mouse_rmb = 0x2 + mouse_mmb = 0x4 | 0x8 | 0x10 +) + +func (this coord) uintptr() uintptr { + return uintptr(*(*int32)(unsafe.Pointer(&this))) +} + +var kernel32 = syscall.NewLazyDLL("kernel32.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_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") +) + +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 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) { + 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) { + 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 +} + +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_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} +) + +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 { + err := get_console_screen_buffer_info(out, &tmp_info) + if err != nil { + panic(err) + } + return tmp_info.size +} + +func get_win_size(out syscall.Handle) coord { + err := get_console_screen_buffer_info(out, &tmp_info) + if err != nil { + panic(err) + } + return coord{ + x: tmp_info.window.right - tmp_info.window.left + 1, + y: tmp_info.window.bottom - tmp_info.window.top + 1, + } +} + +func update_size_maybe() { + size := get_term_size(out) + if size.x != term_size.x || size.y != term_size.y { + term_size = size + back_buffer.resize(int(size.x), int(size.y)) + front_buffer.resize(int(size.x), int(size.y)) + front_buffer.clear() + clear() + + area := int(size.x) * int(size.y) + if cap(charbuf) < area { + charbuf = make([]char_info, 0, area) + } + } +} + +var color_table_bg = []word{ + 0, // default (black) + 0, // black + background_red, + background_green, + background_red | background_green, // yellow + background_blue, + background_red | background_blue, // magenta + background_green | background_blue, // cyan + background_red | background_blue | background_green, // white +} + +var color_table_fg = []word{ + foreground_red | foreground_blue | foreground_green, // default (white) + 0, + foreground_red, + foreground_green, + foreground_red | foreground_green, // yellow + foreground_blue, + foreground_red | foreground_blue, // magenta + foreground_green | foreground_blue, // cyan + foreground_red | foreground_blue | foreground_green, // white +} + +const ( + replacement_char = '\uFFFD' + max_rune = '\U0010FFFF' + surr1 = 0xd800 + surr2 = 0xdc00 + surr3 = 0xe000 + surr_self = 0x10000 +) + +func append_diff_line(y int) int { + n := 0 + for x := 0; x < front_buffer.width; { + cell_offset := y*front_buffer.width + x + back := &back_buffer.cells[cell_offset] + front := &front_buffer.cells[cell_offset] + attr, char := cell_to_char_info(*back) + charbuf = append(charbuf, char_info{attr: attr, char: char[0]}) + *front = *back + n++ + w := runewidth.RuneWidth(back.Ch) + if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) { + w = 1 + } + x += w + // If not CJK, fill trailing space with whitespace + if !is_cjk && w == 2 { + charbuf = append(charbuf, char_info{attr: attr, char: ' '}) + } + } + return n +} + +// compares 'back_buffer' with 'front_buffer' and prepares all changes in the form of +// 'diff_msg's in the 'diff_buf' +func prepare_diff_messages() { + // clear buffers + diffbuf = diffbuf[:0] + charbuf = charbuf[:0] + + var diff diff_msg + gbeg := 0 + for y := 0; y < front_buffer.height; y++ { + same := true + line_offset := y * front_buffer.width + for x := 0; x < front_buffer.width; x++ { + cell_offset := line_offset + x + back := &back_buffer.cells[cell_offset] + front := &front_buffer.cells[cell_offset] + if *back != *front { + same = false + break + } + } + if same && diff.lines > 0 { + diffbuf = append(diffbuf, diff) + diff = diff_msg{} + } + if !same { + beg := len(charbuf) + end := beg + append_diff_line(y) + if diff.lines == 0 { + diff.pos = short(y) + gbeg = beg + } + diff.lines++ + diff.chars = charbuf[gbeg:end] + } + } + if diff.lines > 0 { + diffbuf = append(diffbuf, diff) + diff = diff_msg{} + } +} + +func get_ct(table []word, idx int) word { + idx = idx & 0x0F + if idx >= len(table) { + idx = len(table) - 1 + } + return table[idx] +} + +func cell_to_char_info(c Cell) (attr word, wc [2]wchar) { + attr = get_ct(color_table_fg, int(c.Fg)) | get_ct(color_table_bg, int(c.Bg)) + if c.Fg&AttrReverse|c.Bg&AttrReverse != 0 { + attr = (attr&0xF0)>>4 | (attr&0x0F)<<4 + } + if c.Fg&AttrBold != 0 { + attr |= foreground_intensity + } + if c.Bg&AttrBold != 0 { + attr |= background_intensity + } + + r0, r1 := utf16.EncodeRune(c.Ch) + if r0 == 0xFFFD { + wc[0] = wchar(c.Ch) + wc[1] = ' ' + } else { + wc[0] = wchar(r0) + wc[1] = wchar(r1) + } + return +} + +func move_cursor(x, y int) { + err := set_console_cursor_position(out, coord{short(x), short(y)}) + if err != nil { + panic(err) + } +} + +func show_cursor(visible bool) { + var v int32 + if visible { + v = 1 + } + + var info console_cursor_info + info.size = 100 + info.visible = v + err := set_console_cursor_info(out, &info) + if err != nil { + panic(err) + } +} + +func clear() { + var err error + attr, char := cell_to_char_info(Cell{ + ' ', + foreground, + background, + }) + + area := int(term_size.x) * int(term_size.y) + err = fill_console_output_attribute(out, attr, area) + if err != nil { + panic(err) + } + err = fill_console_output_character(out, char[0], area) + if err != nil { + panic(err) + } + if !is_cursor_hidden(cursor_x, cursor_y) { + move_cursor(cursor_x, cursor_y) + } +} + +func key_event_record_to_event(r *key_event_record) (Event, bool) { + if r.key_down == 0 { + return Event{}, false + } + + e := Event{Type: EventKey} + if input_mode&InputAlt != 0 { + if alt_mode_esc { + e.Mod = ModAlt + alt_mode_esc = false + } + if r.control_key_state&(left_alt_pressed|right_alt_pressed) != 0 { + e.Mod = ModAlt + } + } + + ctrlpressed := r.control_key_state&(left_ctrl_pressed|right_ctrl_pressed) != 0 + + if r.virtual_key_code >= vk_f1 && r.virtual_key_code <= vk_f12 { + switch r.virtual_key_code { + case vk_f1: + e.Key = KeyF1 + case vk_f2: + e.Key = KeyF2 + case vk_f3: + e.Key = KeyF3 + case vk_f4: + e.Key = KeyF4 + case vk_f5: + e.Key = KeyF5 + case vk_f6: + e.Key = KeyF6 + case vk_f7: + e.Key = KeyF7 + case vk_f8: + e.Key = KeyF8 + case vk_f9: + e.Key = KeyF9 + case vk_f10: + e.Key = KeyF10 + case vk_f11: + e.Key = KeyF11 + case vk_f12: + e.Key = KeyF12 + default: + panic("unreachable") + } + + return e, true + } + + if r.virtual_key_code <= vk_delete { + switch r.virtual_key_code { + case vk_insert: + e.Key = KeyInsert + case vk_delete: + e.Key = KeyDelete + case vk_home: + e.Key = KeyHome + case vk_end: + e.Key = KeyEnd + case vk_pgup: + e.Key = KeyPgup + case vk_pgdn: + e.Key = KeyPgdn + case vk_arrow_up: + e.Key = KeyArrowUp + case vk_arrow_down: + e.Key = KeyArrowDown + case vk_arrow_left: + e.Key = KeyArrowLeft + case vk_arrow_right: + e.Key = KeyArrowRight + case vk_backspace: + if ctrlpressed { + e.Key = KeyBackspace2 + } else { + e.Key = KeyBackspace + } + case vk_tab: + e.Key = KeyTab + case vk_enter: + e.Key = KeyEnter + case vk_esc: + switch { + case input_mode&InputEsc != 0: + e.Key = KeyEsc + case input_mode&InputAlt != 0: + alt_mode_esc = true + return Event{}, false + } + case vk_space: + if ctrlpressed { + // manual return here, because KeyCtrlSpace is zero + e.Key = KeyCtrlSpace + return e, true + } else { + e.Key = KeySpace + } + } + + if e.Key != 0 { + return e, true + } + } + + if ctrlpressed { + if Key(r.unicode_char) >= KeyCtrlA && Key(r.unicode_char) <= KeyCtrlRsqBracket { + e.Key = Key(r.unicode_char) + if input_mode&InputAlt != 0 && e.Key == KeyEsc { + alt_mode_esc = true + return Event{}, false + } + return e, true + } + switch r.virtual_key_code { + case 192, 50: + // manual return here, because KeyCtrl2 is zero + e.Key = KeyCtrl2 + return e, true + case 51: + if input_mode&InputAlt != 0 { + alt_mode_esc = true + return Event{}, false + } + e.Key = KeyCtrl3 + case 52: + e.Key = KeyCtrl4 + case 53: + e.Key = KeyCtrl5 + case 54: + e.Key = KeyCtrl6 + case 189, 191, 55: + e.Key = KeyCtrl7 + case 8, 56: + e.Key = KeyCtrl8 + } + + if e.Key != 0 { + return e, true + } + } + + if r.unicode_char != 0 { + e.Ch = rune(r.unicode_char) + return e, true + } + + return Event{}, false +} + +func input_event_producer() { + var r input_record + var err error + var last_button Key + var last_button_pressed Key + var last_state = dword(0) + var last_x, last_y = -1, -1 + handles := []syscall.Handle{in, interrupt} + for { + err = wait_for_multiple_objects(handles) + if err != nil { + input_comm <- Event{Type: EventError, Err: err} + } + + select { + case <-cancel_comm: + cancel_done_comm <- true + return + default: + } + + err = read_console_input(in, &r) + if err != nil { + input_comm <- Event{Type: EventError, Err: err} + } + + switch r.event_type { + case key_event: + kr := (*key_event_record)(unsafe.Pointer(&r.event)) + ev, ok := key_event_record_to_event(kr) + if ok { + for i := 0; i < int(kr.repeat_count); i++ { + input_comm <- ev + } + } + case window_buffer_size_event: + sr := *(*window_buffer_size_record)(unsafe.Pointer(&r.event)) + input_comm <- Event{ + Type: EventResize, + Width: int(sr.size.x), + Height: int(sr.size.y), + } + case mouse_event: + mr := *(*mouse_event_record)(unsafe.Pointer(&r.event)) + ev := Event{Type: EventMouse} + switch mr.event_flags { + case 0, 2: + // single or double click + cur_state := mr.button_state + switch { + case last_state&mouse_lmb == 0 && cur_state&mouse_lmb != 0: + last_button = MouseLeft + last_button_pressed = last_button + case last_state&mouse_rmb == 0 && cur_state&mouse_rmb != 0: + last_button = MouseRight + last_button_pressed = last_button + case last_state&mouse_mmb == 0 && cur_state&mouse_mmb != 0: + last_button = MouseMiddle + last_button_pressed = last_button + case last_state&mouse_lmb != 0 && cur_state&mouse_lmb == 0: + last_button = MouseRelease + case last_state&mouse_rmb != 0 && cur_state&mouse_rmb == 0: + last_button = MouseRelease + case last_state&mouse_mmb != 0 && cur_state&mouse_mmb == 0: + last_button = MouseRelease + default: + last_state = cur_state + continue + } + last_state = cur_state + ev.Key = last_button + last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y) + ev.MouseX = last_x + ev.MouseY = last_y + case 1: + // mouse motion + x, y := int(mr.mouse_pos.x), int(mr.mouse_pos.y) + if last_state != 0 && (last_x != x || last_y != y) { + ev.Key = last_button_pressed + ev.Mod = ModMotion + ev.MouseX = x + ev.MouseY = y + last_x, last_y = x, y + } else { + ev.Type = EventNone + } + case 4: + // mouse wheel + n := int16(mr.button_state >> 16) + if n > 0 { + ev.Key = MouseWheelUp + } else { + ev.Key = MouseWheelDown + } + last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y) + ev.MouseX = last_x + ev.MouseY = last_y + default: + ev.Type = EventNone + } + if ev.Type != EventNone { + input_comm <- ev + } + } + } +} diff --git a/vendor/github.com/nsf/termbox-go/terminfo.go b/vendor/github.com/nsf/termbox-go/terminfo.go new file mode 100644 index 0000000..35dbd70 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/terminfo.go @@ -0,0 +1,221 @@ +// +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 + } + } + } + + // 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 + } + + 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] + 2*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/vendor/github.com/nsf/termbox-go/terminfo_builtin.go b/vendor/github.com/nsf/termbox-go/terminfo_builtin.go new file mode 100644 index 0000000..a948660 --- /dev/null +++ b/vendor/github.com/nsf/termbox-go/terminfo_builtin.go @@ -0,0 +1,64 @@ +// +build !windows + +package termbox + +// Eterm +var eterm_keys = []string{ + "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", +} +var eterm_funcs = []string{ + "\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "", +} + +// screen +var screen_keys = []string{ + "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC", +} +var screen_funcs = []string{ + "\x1b[?1049h", "\x1b[?1049l", "\x1b[34h\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave, +} + +// xterm +var xterm_keys = []string{ + "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1bOH", "\x1bOF", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC", +} +var xterm_funcs = []string{ + "\x1b[?1049h", "\x1b[?1049l", "\x1b[?12l\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b(B\x1b[m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave, +} + +// rxvt-unicode +var rxvt_unicode_keys = []string{ + "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", +} +var rxvt_unicode_funcs = []string{ + "\x1b[?1049h", "\x1b[r\x1b[?1049l", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x1b(B", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave, +} + +// linux +var linux_keys = []string{ + "\x1b[[A", "\x1b[[B", "\x1b[[C", "\x1b[[D", "\x1b[[E", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", +} +var linux_funcs = []string{ + "", "", "\x1b[?25h\x1b[?0c", "\x1b[?25l\x1b[?1c", "\x1b[H\x1b[J", "\x1b[0;10m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "", +} + +// rxvt-256color +var rxvt_256color_keys = []string{ + "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", +} +var rxvt_256color_funcs = []string{ + "\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave, +} + +var terms = []struct { + name string + keys []string + funcs []string +}{ + {"Eterm", eterm_keys, eterm_funcs}, + {"screen", screen_keys, screen_funcs}, + {"xterm", xterm_keys, xterm_funcs}, + {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs}, + {"linux", linux_keys, linux_funcs}, + {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs}, +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/.travis.yml b/vendor/gopkg.in/inconshreveable/log15.v2/.travis.yml new file mode 100644 index 0000000..56678f3 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/.travis.yml @@ -0,0 +1,9 @@ +language: go + +go: + - 1.0 + - 1.1 + - 1.2 + - 1.3 + - release + - tip diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/CONTRIBUTORS b/vendor/gopkg.in/inconshreveable/log15.v2/CONTRIBUTORS new file mode 100644 index 0000000..a086671 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/CONTRIBUTORS @@ -0,0 +1,11 @@ +Contributors to log15: + +- Aaron L +- Alan Shreve +- Chris Hines +- Ciaran Downey +- Dmitry Chestnykh +- Evan Shaw +- Péter Szilágyi +- Trevor Gattis +- Vincent Vanackere diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/LICENSE b/vendor/gopkg.in/inconshreveable/log15.v2/LICENSE new file mode 100644 index 0000000..5f0d1fb --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/LICENSE @@ -0,0 +1,13 @@ +Copyright 2014 Alan Shreve + +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/vendor/gopkg.in/inconshreveable/log15.v2/README.md b/vendor/gopkg.in/inconshreveable/log15.v2/README.md new file mode 100644 index 0000000..49313ff --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/README.md @@ -0,0 +1,60 @@ +![obligatory xkcd](http://imgs.xkcd.com/comics/standards.png) + +# log15 [![godoc reference](https://godoc.org/gopkg.in/inconshreveable/log15.v2?status.png)](https://godoc.org/gopkg.in/inconshreveable/log15.v2) + +Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package. + +## Features +- A simple, easy-to-understand API +- Promotes structured logging by encouraging use of key/value pairs +- Child loggers which inherit and add their own private context +- Lazy evaluation of expensive operations +- Simple Handler interface allowing for construction of flexible, custom logging configurations with a tiny API. +- Color terminal support +- Built-in support for logging to files, streams, syslog, and the network +- Support for forking records to multiple handlers, buffering records for output, failing over from failed handler writes, + more + +## Versioning +The API of the master branch of log15 should always be considered unstable. Using a stable version +of the log15 package is supported by gopkg.in. Include your dependency like so: + +```go +import log "gopkg.in/inconshreveable/log15.v2" +``` + +## Examples + +```go +// all loggers can have key/value context +srvlog := log.New("module", "app/server") + +// all log messages can have key/value context +srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate) + +// child loggers with inherited context +connlog := srvlog.New("raddr", c.RemoteAddr()) +connlog.Info("connection open") + +// lazy evaluation +connlog.Debug("ping remote", "latency", log.Lazy(pingRemote)) + +// flexible configuration +srvlog.SetHandler(log.MultiHandler( + log.StreamHandler(os.Stderr, log.LogfmtFormat()), + log.LvlFilterHandler( + log.LvlError, + log.Must.FileHandler("errors.json", log.JsonHandler()))) +``` + +## FAQ + +### The varargs style is brittle and error prone! Can I have type safety please? +Yes. Use `log.Ctx`: + +```go +srvlog := log.New(log.Ctx{"module": "app/server"}) +srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate}) +``` + +## License +Apache diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/RELEASING.md b/vendor/gopkg.in/inconshreveable/log15.v2/RELEASING.md new file mode 100644 index 0000000..589a4dc --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/RELEASING.md @@ -0,0 +1,19 @@ +# log15's release strategy + +log15 uses gopkg.in to manage versioning releases so that consumers who don't vendor dependencies can rely upon a stable API. + +## Master + +Master is considered to have no API stability guarantee, so merging new code that passes tests into master is always okay. + +## Releasing a new API-compatible version + +The process to release a new API-compatible version is described below. For the purposes of this example, we'll assume you're trying to release a new version of v2 + +1. `git checkout v2` +1. `git merge master` +1. Audit the code for any imports of sub-packages. Modify any import references from `github.com/inconshrevealbe/log15/` -> `gopkg.in/inconshreveable/log15.v2/` +1. `git commit` +1. `git tag`, find the latest tag of the style v2.X. +1. `git tag v2.X+1` If the last version was v2.6, you would run `git tag v2.7` +1. `git push --tags git@github.com:inconshreveable/log15.git v2` diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/bench_test.go b/vendor/gopkg.in/inconshreveable/log15.v2/bench_test.go new file mode 100644 index 0000000..e692e61 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/bench_test.go @@ -0,0 +1,129 @@ +package log15 + +import ( + "bytes" + "testing" + "time" +) + +func BenchmarkStreamNoCtx(b *testing.B) { + lg := New() + + buf := bytes.Buffer{} + lg.SetHandler(StreamHandler(&buf, LogfmtFormat())) + + for i := 0; i < b.N; i++ { + lg.Info("test message") + buf.Reset() + } +} + +func BenchmarkDiscard(b *testing.B) { + lg := New() + lg.SetHandler(DiscardHandler()) + + for i := 0; i < b.N; i++ { + lg.Info("test message") + } +} + +func BenchmarkCallerFileHandler(b *testing.B) { + lg := New() + lg.SetHandler(CallerFileHandler(DiscardHandler())) + + for i := 0; i < b.N; i++ { + lg.Info("test message") + } +} + +func BenchmarkCallerFuncHandler(b *testing.B) { + lg := New() + lg.SetHandler(CallerFuncHandler(DiscardHandler())) + + for i := 0; i < b.N; i++ { + lg.Info("test message") + } +} + +func BenchmarkLogfmtNoCtx(b *testing.B) { + r := Record{ + Time: time.Now(), + Lvl: LvlInfo, + Msg: "test message", + Ctx: []interface{}{}, + } + + logfmt := LogfmtFormat() + for i := 0; i < b.N; i++ { + logfmt.Format(&r) + } +} + +func BenchmarkJsonNoCtx(b *testing.B) { + r := Record{ + Time: time.Now(), + Lvl: LvlInfo, + Msg: "test message", + Ctx: []interface{}{}, + } + + jsonfmt := JsonFormat() + for i := 0; i < b.N; i++ { + jsonfmt.Format(&r) + } +} + +func BenchmarkMultiLevelFilter(b *testing.B) { + handler := MultiHandler( + LvlFilterHandler(LvlDebug, DiscardHandler()), + LvlFilterHandler(LvlError, DiscardHandler()), + ) + + lg := New() + lg.SetHandler(handler) + for i := 0; i < b.N; i++ { + lg.Info("test message") + } +} + +func BenchmarkDescendant1(b *testing.B) { + lg := New() + lg.SetHandler(DiscardHandler()) + lg = lg.New() + for i := 0; i < b.N; i++ { + lg.Info("test message") + } +} + +func BenchmarkDescendant2(b *testing.B) { + lg := New() + lg.SetHandler(DiscardHandler()) + for i := 0; i < 2; i++ { + lg = lg.New() + } + for i := 0; i < b.N; i++ { + lg.Info("test message") + } +} + +func BenchmarkDescendant4(b *testing.B) { + lg := New() + lg.SetHandler(DiscardHandler()) + for i := 0; i < 4; i++ { + lg = lg.New() + } + for i := 0; i < b.N; i++ { + lg.Info("test message") + } +} + +func BenchmarkDescendant8(b *testing.B) { + lg := New() + lg.SetHandler(DiscardHandler()) + for i := 0; i < 8; i++ { + lg = lg.New() + } + for i := 0; i < b.N; i++ { + lg.Info("test message") + } +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/doc.go b/vendor/gopkg.in/inconshreveable/log15.v2/doc.go new file mode 100644 index 0000000..64826d7 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/doc.go @@ -0,0 +1,333 @@ +/* +Package log15 provides an opinionated, simple toolkit for best-practice logging that is +both human and machine readable. It is modeled after the standard library's io and net/http +packages. + +This package enforces you to only log key/value pairs. Keys must be strings. Values may be +any type that you like. The default output format is logfmt, but you may also choose to use +JSON instead if that suits you. Here's how you log: + + log.Info("page accessed", "path", r.URL.Path, "user_id", user.id) + +This will output a line that looks like: + + lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9 + +Getting Started + +To get started, you'll want to import the library: + + import log "gopkg.in/inconshreveable/log15.v2" + + +Now you're ready to start logging: + + func main() { + log.Info("Program starting", "args", os.Args()) + } + + +Convention + +Because recording a human-meaningful message is common and good practice, the first argument to every +logging method is the value to the *implicit* key 'msg'. + +Additionally, the level you choose for a message will be automatically added with the key 'lvl', and so +will the current timestamp with key 't'. + +You may supply any additional context as a set of key/value pairs to the logging function. log15 allows +you to favor terseness, ordering, and speed over safety. This is a reasonable tradeoff for +logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate +in the variadic argument list: + + log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val) + +If you really do favor your type-safety, you may choose to pass a log.Ctx instead: + + log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val}) + + +Context loggers + +Frequently, you want to add context to a logger so that you can track actions associated with it. An http +request is a good example. You can easily create new loggers that have context that is automatically included +with each log line: + + requestlogger := log.New("path", r.URL.Path) + + // later + requestlogger.Debug("db txn commit", "duration", txnTimer.Finish()) + +This will output a log line that includes the path context that is attached to the logger: + + lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12 + + +Handlers + +The Handler interface defines where log lines are printed to and how they are formated. Handler is a +single interface that is inspired by net/http's handler interface: + + type Handler interface { + Log(r *Record) + } + + +Handlers can filter records, format them, or dispatch to multiple other Handlers. +This package implements a number of Handlers for common logging patterns that are +easily composed to create flexible, custom logging structures. + +Here's an example handler that prints logfmt output to Stdout: + + handler := log.StreamHandler(os.Stdout, log.LogfmtFormat()) + +Here's an example handler that defers to two other handlers. One handler only prints records +from the rpc package in logfmt to standard out. The other prints records at Error level +or above in JSON formatted output to the file /var/log/service.json + + handler := log.MultiHandler( + log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JsonFormat())), + log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler()) + ) + +Logging File Names and Line Numbers + +This package implements three Handlers that add debugging information to the +context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's +an example that adds the source file and line number of each logging call to +the context. + + h := log.CallerFileHandler(log.StdoutHandler()) + log.Root().SetHandler(h) + ... + log.Error("open file", "err", err) + +This will output a line that looks like: + + lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42 + +Here's an example that logs the call stack rather than just the call site. + + h := log.CallerStackHandler("%+v", log.StdoutHandler()) + log.Root().SetHandler(h) + ... + log.Error("open file", "err", err) + +This will output a line that looks like: + + lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]" + +The "%+v" format instructs the handler to include the path of the source file +relative to the compile time GOPATH. The log15/stack package documents the +full list of formatting verbs and modifiers available. + +Custom Handlers + +The Handler interface is so simple that it's also trivial to write your own. Let's create an +example handler which tries to write to one handler, but if that fails it falls back to +writing to another handler and includes the error that it encountered when trying to write +to the primary. This might be useful when trying to log over a network socket, but if that +fails you want to log those records to a file on disk. + + type BackupHandler struct { + Primary Handler + Secondary Handler + } + + func (h *BackupHandler) Log (r *Record) error { + err := h.Primary.Log(r) + if err != nil { + r.Ctx = append(ctx, "primary_err", err) + return h.Secondary.Log(r) + } + return nil + } + +This pattern is so useful that a generic version that handles an arbitrary number of Handlers +is included as part of this library called FailoverHandler. + +Logging Expensive Operations + +Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay +the price of computing them if you haven't turned up your logging level to a high level of detail. + +This package provides a simple type to annotate a logging operation that you want to be evaluated +lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler +filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example: + + func factorRSAKey() (factors []int) { + // return the factors of a very large number + } + + log.Debug("factors", log.Lazy{factorRSAKey}) + +If this message is not logged for any reason (like logging at the Error level), then +factorRSAKey is never evaluated. + +Dynamic context values + +The same log.Lazy mechanism can be used to attach context to a logger which you want to be +evaluated when the message is logged, but not when the logger is created. For example, let's imagine +a game where you have Player objects: + + type Player struct { + name string + alive bool + log.Logger + } + +You always want to log a player's name and whether they're alive or dead, so when you create the player +object, you might do: + + p := &Player{name: name, alive: true} + p.Logger = log.New("name", p.name, "alive", p.alive) + +Only now, even after a player has died, the logger will still report they are alive because the logging +context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation +of whether the player is alive or not to each log message, so that the log records will reflect the player's +current state no matter when the log message is written: + + p := &Player{name: name, alive: true} + isAlive := func() bool { return p.alive } + player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive}) + +Terminal Format + +If log15 detects that stdout is a terminal, it will configure the default +handler for it (which is log.StdoutHandler) to use TerminalFormat. This format +logs records nicely for your terminal, including color-coded output based +on log level. + +Error Handling + +Becasuse log15 allows you to step around the type system, there are a few ways you can specify +invalid arguments to the logging functions. You could, for example, wrap something that is not +a zero-argument function with log.Lazy or pass a context key that is not a string. Since logging libraries +are typically the mechanism by which errors are reported, it would be onerous for the logging functions +to return errors. Instead, log15 handles errors by making these guarantees to you: + +- Any log record containing an error will still be printed with the error explained to you as part of the log record. + +- Any log record containing an error will include the context key LOG15_ERROR, enabling you to easily +(and if you like, automatically) detect if any of your logging calls are passing bad values. + +Understanding this, you might wonder why the Handler interface can return an error value in its Log method. Handlers +are encouraged to return errors only if they fail to write their log records out to an external source like if the +syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures +like the FailoverHandler. + +Library Use + +log15 is intended to be useful for library authors as a way to provide configurable logging to +users of their library. Best practice for use in a library is to always disable all output for your logger +by default and to provide a public Logger instance that consumers of your library can configure. Like so: + + package yourlib + + import "gopkg.in/inconshreveable/log15.v2" + + var Log = log.New() + + func init() { + Log.SetHandler(log.DiscardHandler()) + } + +Users of your library may then enable it if they like: + + import "gopkg.in/inconshreveable/log15.v2" + import "example.com/yourlib" + + func main() { + handler := // custom handler setup + yourlib.Log.SetHandler(handler) + } + +Best practices attaching logger context + +The ability to attach context to a logger is a powerful one. Where should you do it and why? +I favor embedding a Logger directly into any persistent object in my application and adding +unique, tracing context keys to it. For instance, imagine I am writing a web browser: + + type Tab struct { + url string + render *RenderingContext + // ... + + Logger + } + + func NewTab(url string) *Tab { + return &Tab { + // ... + url: url, + + Logger: log.New("url", url), + } + } + +When a new tab is created, I assign a logger to it with the url of +the tab as context so it can easily be traced through the logs. +Now, whenever we perform any operation with the tab, we'll log with its +embedded logger and it will include the tab title automatically: + + tab.Debug("moved position", "idx", tab.idx) + +There's only one problem. What if the tab url changes? We could +use log.Lazy to make sure the current url is always written, but that +would mean that we couldn't trace a tab's full lifetime through our +logs after the user navigate to a new URL. + +Instead, think about what values to attach to your loggers the +same way you think about what to use as a key in a SQL database schema. +If it's possible to use a natural key that is unique for the lifetime of the +object, do so. But otherwise, log15's ext package has a handy RandId +function to let you generate what you might call "surrogate keys" +They're just random hex identifiers to use for tracing. Back to our +Tab example, we would prefer to set up our Logger like so: + + import logext "gopkg.in/inconshreveable/log15.v2/ext" + + t := &Tab { + // ... + url: url, + } + + t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl}) + return t + +Now we'll have a unique traceable identifier even across loading new urls, but +we'll still be able to see the tab's current url in the log messages. + +Must + +For all Handler functions which can return an error, there is a version of that +function which will return no error but panics on failure. They are all available +on the Must object. For example: + + log.Must.FileHandler("/path", log.JsonFormat) + log.Must.NetHandler("tcp", ":1234", log.JsonFormat) + +Inspiration and Credit + +All of the following excellent projects inspired the design of this library: + +code.google.com/p/log4go + +github.com/op/go-logging + +github.com/technoweenie/grohl + +github.com/Sirupsen/logrus + +github.com/kr/logfmt + +github.com/spacemonkeygo/spacelog + +golang's stdlib, notably io and net/http + +The Name + +https://xkcd.com/927/ + +*/ +package log15 diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/ext/ext_test.go b/vendor/gopkg.in/inconshreveable/log15.v2/ext/ext_test.go new file mode 100644 index 0000000..c7f3538 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/ext/ext_test.go @@ -0,0 +1,109 @@ +package ext + +import ( + "errors" + log "gopkg.in/inconshreveable/log15.v2" + "math" + "testing" +) + +func testHandler() (log.Handler, *log.Record) { + rec := new(log.Record) + return log.FuncHandler(func(r *log.Record) error { + *rec = *r + return nil + }), rec +} + +func TestHotSwapHandler(t *testing.T) { + t.Parallel() + + h1, r1 := testHandler() + + l := log.New() + h := HotSwapHandler(h1) + l.SetHandler(h) + + l.Info("to h1") + if r1.Msg != "to h1" { + t.Fatalf("didn't get expected message to h1") + } + + h2, r2 := testHandler() + h.Swap(h2) + l.Info("to h2") + if r2.Msg != "to h2" { + t.Fatalf("didn't get expected message to h2") + } +} + +func TestSpeculativeHandler(t *testing.T) { + t.Parallel() + + // test with an even multiple of the buffer size, less than full buffer size + // and not a multiple of the buffer size + for _, count := range []int{10000, 50, 432} { + recs := make(chan *log.Record) + done := make(chan int) + spec := SpeculativeHandler(100, log.ChannelHandler(recs)) + + go func() { + defer close(done) + expectedCount := int(math.Min(float64(count), float64(100))) + expectedIdx := count - expectedCount + for r := range recs { + if r.Ctx[1] != expectedIdx { + t.Errorf("Bad ctx 'i', got %d expected %d", r.Ctx[1], expectedIdx) + return + } + expectedIdx++ + expectedCount-- + + if expectedCount == 0 { + // got everything we expected + break + } + } + + select { + case <-recs: + t.Errorf("got an extra record we shouldn't have!") + default: + } + }() + + lg := log.New() + lg.SetHandler(spec) + for i := 0; i < count; i++ { + lg.Debug("test speculative", "i", i) + } + + go spec.Flush() + + // wait for the go routine to finish + <-done + } +} + +func TestErrorHandler(t *testing.T) { + t.Parallel() + + h, r := testHandler() + lg := log.New() + lg.SetHandler(EscalateErrHandler( + log.LvlFilterHandler(log.LvlError, h))) + + lg.Debug("some function result", "err", nil) + if r.Msg != "" { + t.Fatalf("Expected debug level message to be filtered") + } + + lg.Debug("some function result", "err", errors.New("failed operation")) + if r.Msg != "some function result" { + t.Fatalf("Expected debug level message to be escalated and pass lvlfilter") + } + + if r.Lvl != log.LvlError { + t.Fatalf("Expected debug level message to be escalated to LvlError") + } +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/ext/handler.go b/vendor/gopkg.in/inconshreveable/log15.v2/ext/handler.go new file mode 100644 index 0000000..8a1eee0 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/ext/handler.go @@ -0,0 +1,130 @@ +package ext + +import ( + "os" + "sync" + "sync/atomic" + "unsafe" + + log "gopkg.in/inconshreveable/log15.v2" +) + +// EscalateErrHandler wraps another handler and passes all records through +// unchanged except if the logged context contains a non-nil error +// value in its context. In that case, the record's level is raised +// to LvlError unless it was already more serious (LvlCrit). +// +// This allows you to log the result of all functions for debugging +// and still capture error conditions when in production with a single +// log line. As an example, the following the log record will be written +// out only if there was an error writing a value to redis: +// +// logger := logext.EscalateErrHandler( +// log.LvlFilterHandler(log.LvlInfo, log.StdoutHandler)) +// +// reply, err := redisConn.Do("SET", "foo", "bar") +// logger.Debug("Wrote value to redis", "reply", reply, "err", err) +// if err != nil { +// return err +// } +// +func EscalateErrHandler(h log.Handler) log.Handler { + return log.FuncHandler(func(r *log.Record) error { + if r.Lvl > log.LvlError { + for i := 1; i < len(r.Ctx); i++ { + if v, ok := r.Ctx[i].(error); ok && v != nil { + r.Lvl = log.LvlError + break + } + } + } + return h.Log(r) + }) +} + +// SpeculativeHandler is a handler for speculative logging. It +// keeps a ring buffer of the given size full of the last events +// logged into it. When Flush is called, all buffered log records +// are written to the wrapped handler. This is extremely for +// continuosly capturing debug level output, but only flushing those +// log records if an exceptional condition is encountered. +func SpeculativeHandler(size int, h log.Handler) *Speculative { + return &Speculative{ + handler: h, + recs: make([]*log.Record, size), + } +} + +type Speculative struct { + mu sync.Mutex + idx int + recs []*log.Record + handler log.Handler + full bool +} + +func (h *Speculative) Log(r *log.Record) error { + h.mu.Lock() + defer h.mu.Unlock() + h.recs[h.idx] = r + h.idx = (h.idx + 1) % len(h.recs) + h.full = h.full || h.idx == 0 + return nil +} + +func (h *Speculative) Flush() { + recs := make([]*log.Record, 0) + func() { + h.mu.Lock() + defer h.mu.Unlock() + if h.full { + recs = append(recs, h.recs[h.idx:]...) + } + recs = append(recs, h.recs[:h.idx]...) + + // reset state + h.full = false + h.idx = 0 + }() + + // don't hold the lock while we flush to the wrapped handler + for _, r := range recs { + h.handler.Log(r) + } +} + +// HotSwapHandler wraps another handler that may swapped out +// dynamically at runtime in a thread-safe fashion. +// HotSwapHandler is the same functionality +// used to implement the SetHandler method for the default +// implementation of Logger. +func HotSwapHandler(h log.Handler) *HotSwap { + hs := new(HotSwap) + hs.Swap(h) + return hs +} + +type HotSwap struct { + handler unsafe.Pointer +} + +func (h *HotSwap) Log(r *log.Record) error { + return (*(*log.Handler)(atomic.LoadPointer(&h.handler))).Log(r) +} + +func (h *HotSwap) Swap(newHandler log.Handler) { + atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler)) +} + +// FatalHandler makes critical errors exit the program +// immediately, much like the log.Fatal* methods from the +// standard log package +func FatalHandler(h log.Handler) log.Handler { + return log.FuncHandler(func(r *log.Record) error { + err := h.Log(r) + if r.Lvl == log.LvlCrit { + os.Exit(1) + } + return err + }) +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/ext/id.go b/vendor/gopkg.in/inconshreveable/log15.v2/ext/id.go new file mode 100644 index 0000000..0bfb155 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/ext/id.go @@ -0,0 +1,47 @@ +package ext + +import ( + "fmt" + "math/rand" + "sync" + "time" +) + +var r = rand.New(&lockedSource{src: rand.NewSource(time.Now().Unix())}) + +// RandId creates a random identifier of the requested length. +// Useful for assigning mostly-unique identifiers for logging +// and identification that are unlikely to collide because of +// short lifespan or low set cardinality +func RandId(idlen int) string { + b := make([]byte, idlen) + var randVal uint32 + for i := 0; i < idlen; i++ { + byteIdx := i % 4 + if byteIdx == 0 { + randVal = r.Uint32() + } + b[i] = byte((randVal >> (8 * uint(byteIdx))) & 0xFF) + } + return fmt.Sprintf("%x", b) +} + +// lockedSource is a wrapper to allow a rand.Source to be used +// concurrently (same type as the one used internally in math/rand). +type lockedSource struct { + lk sync.Mutex + src rand.Source +} + +func (r *lockedSource) Int63() (n int64) { + r.lk.Lock() + n = r.src.Int63() + r.lk.Unlock() + return +} + +func (r *lockedSource) Seed(seed int64) { + r.lk.Lock() + r.src.Seed(seed) + r.lk.Unlock() +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/format.go b/vendor/gopkg.in/inconshreveable/log15.v2/format.go new file mode 100644 index 0000000..76d260e --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/format.go @@ -0,0 +1,257 @@ +package log15 + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strconv" + "strings" + "time" +) + +const ( + timeFormat = "2006-01-02T15:04:05-0700" + termTimeFormat = "01-02|15:04:05" + floatFormat = 'f' + termMsgJust = 40 +) + +type Format interface { + Format(r *Record) []byte +} + +// FormatFunc returns a new Format object which uses +// the given function to perform record formatting. +func FormatFunc(f func(*Record) []byte) Format { + return formatFunc(f) +} + +type formatFunc func(*Record) []byte + +func (f formatFunc) Format(r *Record) []byte { + return f(r) +} + +// TerminalFormat formats log records optimized for human readability on +// a terminal with color-coded level output and terser human friendly timestamp. +// This format should only be used for interactive programs or while developing. +// +// [TIME] [LEVEL] MESAGE key=value key=value ... +// +// Example: +// +// [May 16 20:58:45] [DBUG] remove route ns=haproxy addr=127.0.0.1:50002 +// +func TerminalFormat() Format { + return FormatFunc(func(r *Record) []byte { + var color = 0 + switch r.Lvl { + case LvlCrit: + color = 35 + case LvlError: + color = 31 + case LvlWarn: + color = 33 + case LvlInfo: + color = 32 + case LvlDebug: + color = 36 + } + + b := &bytes.Buffer{} + lvl := strings.ToUpper(r.Lvl.String()) + if color > 0 { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg) + } else { + fmt.Fprintf(b, "[%s] [%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg) + } + + // try to justify the log output for short messages + if len(r.Ctx) > 0 && len(r.Msg) < termMsgJust { + b.Write(bytes.Repeat([]byte{' '}, termMsgJust-len(r.Msg))) + } + + // print the keys logfmt style + logfmt(b, r.Ctx, color) + return b.Bytes() + }) +} + +// LogfmtFormat prints records in logfmt format, an easy machine-parseable but human-readable +// format for key/value pairs. +// +// For more details see: http://godoc.org/github.com/kr/logfmt +// +func LogfmtFormat() Format { + return FormatFunc(func(r *Record) []byte { + common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg} + buf := &bytes.Buffer{} + logfmt(buf, append(common, r.Ctx...), 0) + return buf.Bytes() + }) +} + +func logfmt(buf *bytes.Buffer, ctx []interface{}, color int) { + for i := 0; i < len(ctx); i += 2 { + if i != 0 { + buf.WriteByte(' ') + } + + k, ok := ctx[i].(string) + v := formatLogfmtValue(ctx[i+1]) + if !ok { + k, v = errorKey, formatLogfmtValue(k) + } + + // XXX: we should probably check that all of your key bytes aren't invalid + if color > 0 { + fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=%s", color, k, v) + } else { + fmt.Fprintf(buf, "%s=%s", k, v) + } + } + + buf.WriteByte('\n') +} + +// JsonFormat formats log records as JSON objects separated by newlines. +// It is the equivalent of JsonFormatEx(false, true). +func JsonFormat() Format { + return JsonFormatEx(false, true) +} + +// JsonFormatEx formats log records as JSON objects. If pretty is true, +// records will be pretty-printed. If lineSeparated is true, records +// will be logged with a new line between each record. +func JsonFormatEx(pretty, lineSeparated bool) Format { + jsonMarshal := json.Marshal + if pretty { + jsonMarshal = func(v interface{}) ([]byte, error) { + return json.MarshalIndent(v, "", " ") + } + } + + return FormatFunc(func(r *Record) []byte { + props := make(map[string]interface{}) + + props[r.KeyNames.Time] = r.Time + props[r.KeyNames.Lvl] = r.Lvl + props[r.KeyNames.Msg] = r.Msg + + for i := 0; i < len(r.Ctx); i += 2 { + k, ok := r.Ctx[i].(string) + if !ok { + props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i]) + } + props[k] = formatJsonValue(r.Ctx[i+1]) + } + + b, err := jsonMarshal(props) + if err != nil { + b, _ = jsonMarshal(map[string]string{ + errorKey: err.Error(), + }) + return b + } + + if lineSeparated { + b = append(b, '\n') + } + + return b + }) +} + +func formatShared(value interface{}) (result interface{}) { + defer func() { + if err := recover(); err != nil { + if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() { + result = "nil" + } else { + panic(err) + } + } + }() + + switch v := value.(type) { + case time.Time: + return v.Format(timeFormat) + + case error: + return v.Error() + + case fmt.Stringer: + return v.String() + + default: + return v + } +} + +func formatJsonValue(value interface{}) interface{} { + value = formatShared(value) + switch value.(type) { + case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string: + return value + default: + return fmt.Sprintf("%+v", value) + } +} + +// formatValue formats a value for serialization +func formatLogfmtValue(value interface{}) string { + if value == nil { + return "nil" + } + + value = formatShared(value) + switch v := value.(type) { + case bool: + return strconv.FormatBool(v) + case float32: + return strconv.FormatFloat(float64(v), floatFormat, 3, 64) + case float64: + return strconv.FormatFloat(v, floatFormat, 3, 64) + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return fmt.Sprintf("%d", value) + case string: + return escapeString(v) + default: + return escapeString(fmt.Sprintf("%+v", value)) + } +} + +func escapeString(s string) string { + needQuotes := false + e := bytes.Buffer{} + e.WriteByte('"') + for _, r := range s { + if r <= ' ' || r == '=' || r == '"' { + needQuotes = true + } + + switch r { + case '\\', '"': + e.WriteByte('\\') + e.WriteByte(byte(r)) + case '\n': + e.WriteByte('\\') + e.WriteByte('n') + case '\r': + e.WriteByte('\\') + e.WriteByte('r') + case '\t': + e.WriteByte('\\') + e.WriteByte('t') + default: + e.WriteRune(r) + } + } + e.WriteByte('"') + start, stop := 0, e.Len() + if !needQuotes { + start, stop = 1, stop-1 + } + return string(e.Bytes()[start:stop]) +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/handler.go b/vendor/gopkg.in/inconshreveable/log15.v2/handler.go new file mode 100644 index 0000000..37ecf94 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/handler.go @@ -0,0 +1,371 @@ +package log15 + +import ( + "bytes" + "fmt" + "io" + "net" + "os" + "reflect" + "sync" + + "gopkg.in/inconshreveable/log15.v2/stack" +) + +// A Logger prints its log records by writing to a Handler. +// The Handler interface defines where and how log records are written. +// Handlers are composable, providing you great flexibility in combining +// them to achieve the logging structure that suits your applications. +type Handler interface { + Log(r *Record) error +} + +// FuncHandler returns a Handler that logs records with the given +// function. +func FuncHandler(fn func(r *Record) error) Handler { + return funcHandler(fn) +} + +type funcHandler func(r *Record) error + +func (h funcHandler) Log(r *Record) error { + return h(r) +} + +// StreamHandler writes log records to an io.Writer +// with the given format. StreamHandler can be used +// to easily begin writing log records to other +// outputs. +// +// StreamHandler wraps itself with LazyHandler and SyncHandler +// to evaluate Lazy objects and perform safe concurrent writes. +func StreamHandler(wr io.Writer, fmtr Format) Handler { + h := FuncHandler(func(r *Record) error { + _, err := wr.Write(fmtr.Format(r)) + return err + }) + return LazyHandler(SyncHandler(h)) +} + +// SyncHandler can be wrapped around a handler to guarantee that +// only a single Log operation can proceed at a time. It's necessary +// for thread-safe concurrent writes. +func SyncHandler(h Handler) Handler { + var mu sync.Mutex + return FuncHandler(func(r *Record) error { + defer mu.Unlock() + mu.Lock() + return h.Log(r) + }) +} + +// FileHandler returns a handler which writes log records to the give file +// using the given format. If the path +// already exists, FileHandler will append to the given file. If it does not, +// FileHandler will create the file with mode 0644. +func FileHandler(path string, fmtr Format) (Handler, error) { + f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return nil, err + } + return closingHandler{f, StreamHandler(f, fmtr)}, nil +} + +// NetHandler opens a socket to the given address and writes records +// over the connection. +func NetHandler(network, addr string, fmtr Format) (Handler, error) { + conn, err := net.Dial(network, addr) + if err != nil { + return nil, err + } + + return closingHandler{conn, StreamHandler(conn, fmtr)}, nil +} + +// XXX: closingHandler is essentially unused at the moment +// it's meant for a future time when the Handler interface supports +// a possible Close() operation +type closingHandler struct { + io.WriteCloser + Handler +} + +func (h *closingHandler) Close() error { + return h.WriteCloser.Close() +} + +// CallerFileHandler returns a Handler that adds the line number and file of +// the calling function to the context with key "caller". +func CallerFileHandler(h Handler) Handler { + return FuncHandler(func(r *Record) error { + call := stack.Call(r.CallPC[0]) + r.Ctx = append(r.Ctx, "caller", fmt.Sprint(call)) + return h.Log(r) + }) +} + +// CallerFuncHandler returns a Handler that adds the calling function name to +// the context with key "fn". +func CallerFuncHandler(h Handler) Handler { + return FuncHandler(func(r *Record) error { + call := stack.Call(r.CallPC[0]) + r.Ctx = append(r.Ctx, "fn", fmt.Sprintf("%+n", call)) + return h.Log(r) + }) +} + +// CallerStackHandler returns a Handler that adds a stack trace to the context +// with key "stack". The stack trace is formated as a space separated list of +// call sites inside matching []'s. The most recent call site is listed first. +// Each call site is formatted according to format. See the documentation of +// log15/stack.Call.Format for the list of supported formats. +func CallerStackHandler(format string, h Handler) Handler { + return FuncHandler(func(r *Record) error { + s := stack.Callers(). + TrimBelow(stack.Call(r.CallPC[0])). + TrimRuntime() + if len(s) > 0 { + buf := &bytes.Buffer{} + buf.WriteByte('[') + for i, pc := range s { + if i > 0 { + buf.WriteByte(' ') + } + fmt.Fprintf(buf, format, pc) + } + buf.WriteByte(']') + r.Ctx = append(r.Ctx, "stack", buf.String()) + } + return h.Log(r) + }) +} + +// FilterHandler returns a Handler that only writes records to the +// wrapped Handler if the given function evaluates true. For example, +// to only log records where the 'err' key is not nil: +// +// logger.SetHandler(FilterHandler(func(r *Record) bool { +// for i := 0; i < len(r.Ctx); i += 2 { +// if r.Ctx[i] == "err" { +// return r.Ctx[i+1] != nil +// } +// } +// return false +// }, h)) +// +func FilterHandler(fn func(r *Record) bool, h Handler) Handler { + return FuncHandler(func(r *Record) error { + if fn(r) { + return h.Log(r) + } + return nil + }) +} + +// MatchFilterHandler returns a Handler that only writes records +// to the wrapped Handler if the given key in the logged +// context matches the value. For example, to only log records +// from your ui package: +// +// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) +// +func MatchFilterHandler(key string, value interface{}, h Handler) Handler { + return FilterHandler(func(r *Record) (pass bool) { + switch key { + case r.KeyNames.Lvl: + return r.Lvl == value + case r.KeyNames.Time: + return r.Time == value + case r.KeyNames.Msg: + return r.Msg == value + } + + for i := 0; i < len(r.Ctx); i += 2 { + if r.Ctx[i] == key { + return r.Ctx[i+1] == value + } + } + return false + }, h) +} + +// LvlFilterHandler returns a Handler that only writes +// records which are less than the given verbosity +// level to the wrapped Handler. For example, to only +// log Error/Crit records: +// +// log.LvlFilterHandler(log.Error, log.StdoutHandler) +// +func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { + return FilterHandler(func(r *Record) (pass bool) { + return r.Lvl <= maxLvl + }, h) +} + +// A MultiHandler dispatches any write to each of its handlers. +// This is useful for writing different types of log information +// to different locations. For example, to log to a file and +// standard error: +// +// log.MultiHandler( +// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), +// log.StderrHandler) +// +func MultiHandler(hs ...Handler) Handler { + return FuncHandler(func(r *Record) error { + for _, h := range hs { + // what to do about failures? + h.Log(r) + } + return nil + }) +} + +// A FailoverHandler writes all log records to the first handler +// specified, but will failover and write to the second handler if +// the first handler has failed, and so on for all handlers specified. +// For example you might want to log to a network socket, but failover +// to writing to a file if the network fails, and then to +// standard out if the file write fails: +// +// log.FailoverHandler( +// log.Must.NetHandler("tcp", ":9090", log.JsonFormat()), +// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), +// log.StdoutHandler) +// +// All writes that do not go to the first handler will add context with keys of +// the form "failover_err_{idx}" which explain the error encountered while +// trying to write to the handlers before them in the list. +func FailoverHandler(hs ...Handler) Handler { + return FuncHandler(func(r *Record) error { + var err error + for i, h := range hs { + err = h.Log(r) + if err == nil { + return nil + } else { + r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) + } + } + + return err + }) +} + +// ChannelHandler writes all records to the given channel. +// It blocks if the channel is full. Useful for async processing +// of log messages, it's used by BufferedHandler. +func ChannelHandler(recs chan<- *Record) Handler { + return FuncHandler(func(r *Record) error { + recs <- r + return nil + }) +} + +// BufferedHandler writes all records to a buffered +// channel of the given size which flushes into the wrapped +// handler whenever it is available for writing. Since these +// writes happen asynchronously, all writes to a BufferedHandler +// never return an error and any errors from the wrapped handler are ignored. +func BufferedHandler(bufSize int, h Handler) Handler { + recs := make(chan *Record, bufSize) + go func() { + for m := range recs { + _ = h.Log(m) + } + }() + return ChannelHandler(recs) +} + +// LazyHandler writes all values to the wrapped handler after evaluating +// any lazy functions in the record's context. It is already wrapped +// around StreamHandler and SyslogHandler in this library, you'll only need +// it if you write your own Handler. +func LazyHandler(h Handler) Handler { + return FuncHandler(func(r *Record) error { + // go through the values (odd indices) and reassign + // the values of any lazy fn to the result of its execution + hadErr := false + for i := 1; i < len(r.Ctx); i += 2 { + lz, ok := r.Ctx[i].(Lazy) + if ok { + v, err := evaluateLazy(lz) + if err != nil { + hadErr = true + r.Ctx[i] = err + } else { + if cs, ok := v.(stack.Trace); ok { + v = cs.TrimBelow(stack.Call(r.CallPC[0])). + TrimRuntime() + } + r.Ctx[i] = v + } + } + } + + if hadErr { + r.Ctx = append(r.Ctx, errorKey, "bad lazy") + } + + return h.Log(r) + }) +} + +func evaluateLazy(lz Lazy) (interface{}, error) { + t := reflect.TypeOf(lz.Fn) + + if t.Kind() != reflect.Func { + return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) + } + + if t.NumIn() > 0 { + return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) + } + + if t.NumOut() == 0 { + return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) + } + + value := reflect.ValueOf(lz.Fn) + results := value.Call([]reflect.Value{}) + if len(results) == 1 { + return results[0].Interface(), nil + } else { + values := make([]interface{}, len(results)) + for i, v := range results { + values[i] = v.Interface() + } + return values, nil + } +} + +// DiscardHandler reports success for all writes but does nothing. +// It is useful for dynamically disabling logging at runtime via +// a Logger's SetHandler method. +func DiscardHandler() Handler { + return FuncHandler(func(r *Record) error { + return nil + }) +} + +// The Must object provides the following Handler creation functions +// which instead of returning an error parameter only return a Handler +// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler +var Must muster + +func must(h Handler, err error) Handler { + if err != nil { + panic(err) + } + return h +} + +type muster struct{} + +func (m muster) FileHandler(path string, fmtr Format) Handler { + return must(FileHandler(path, fmtr)) +} + +func (m muster) NetHandler(network, addr string, fmtr Format) Handler { + return must(NetHandler(network, addr, fmtr)) +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/handler_appengine.go b/vendor/gopkg.in/inconshreveable/log15.v2/handler_appengine.go new file mode 100644 index 0000000..f5e34d2 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/handler_appengine.go @@ -0,0 +1,26 @@ +// +build appengine + +package log15 + +import "sync" + +// swapHandler wraps another handler that may be swapped out +// dynamically at runtime in a thread-safe fashion. +type swapHandler struct { + handler interface{} + lock sync.RWMutex +} + +func (h *swapHandler) Log(r *Record) error { + h.lock.RLock() + defer h.lock.RUnlock() + + return h.handler.(Handler).Log(r) +} + +func (h *swapHandler) Swap(newHandler Handler) { + h.lock.Lock() + defer h.lock.Unlock() + + h.handler = newHandler +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/handler_other.go b/vendor/gopkg.in/inconshreveable/log15.v2/handler_other.go new file mode 100644 index 0000000..4da9674 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/handler_other.go @@ -0,0 +1,22 @@ +// +build !appengine + +package log15 + +import ( + "sync/atomic" + "unsafe" +) + +// swapHandler wraps another handler that may be swapped out +// dynamically at runtime in a thread-safe fashion. +type swapHandler struct { + handler unsafe.Pointer +} + +func (h *swapHandler) Log(r *Record) error { + return (*(*Handler)(atomic.LoadPointer(&h.handler))).Log(r) +} + +func (h *swapHandler) Swap(newHandler Handler) { + atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler)) +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/log15_test.go b/vendor/gopkg.in/inconshreveable/log15.v2/log15_test.go new file mode 100644 index 0000000..fc61f5e --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/log15_test.go @@ -0,0 +1,566 @@ +package log15 + +import ( + "bufio" + "bytes" + "encoding/json" + "errors" + "fmt" + "net" + "regexp" + "runtime" + "sync" + "testing" + "time" +) + +func testHandler() (Handler, *Record) { + rec := new(Record) + return FuncHandler(func(r *Record) error { + *rec = *r + return nil + }), rec +} + +func testLogger() (Logger, Handler, *Record) { + l := New() + h, r := testHandler() + l.SetHandler(LazyHandler(h)) + return l, h, r +} + +func TestLazy(t *testing.T) { + t.Parallel() + + x := 1 + lazy := func() int { + return x + } + + l, _, r := testLogger() + l.Info("", "x", Lazy{lazy}) + if r.Ctx[1] != 1 { + t.Fatalf("Lazy function not evaluated, got %v, expected %d", r.Ctx[1], 1) + } + + x = 2 + l.Info("", "x", Lazy{lazy}) + if r.Ctx[1] != 2 { + t.Fatalf("Lazy function not evaluated, got %v, expected %d", r.Ctx[1], 1) + } +} + +func TestInvalidLazy(t *testing.T) { + t.Parallel() + + l, _, r := testLogger() + validate := func() { + if len(r.Ctx) < 4 { + t.Fatalf("Invalid lazy, got %d args, expecting at least 4", len(r.Ctx)) + } + + if r.Ctx[2] != errorKey { + t.Fatalf("Invalid lazy, got key %s expecting %s", r.Ctx[2], errorKey) + } + } + + l.Info("", "x", Lazy{1}) + validate() + + l.Info("", "x", Lazy{func(x int) int { return x }}) + validate() + + l.Info("", "x", Lazy{func() {}}) + validate() +} + +func TestCtx(t *testing.T) { + t.Parallel() + + l, _, r := testLogger() + l.Info("", Ctx{"x": 1, "y": "foo", "tester": t}) + if len(r.Ctx) != 6 { + t.Fatalf("Expecting Ctx tansformed into %d ctx args, got %d: %v", 6, len(r.Ctx), r.Ctx) + } +} + +func testFormatter(f Format) (Logger, *bytes.Buffer) { + l := New() + var buf bytes.Buffer + l.SetHandler(StreamHandler(&buf, f)) + return l, &buf +} + +func TestJson(t *testing.T) { + t.Parallel() + + l, buf := testFormatter(JsonFormat()) + l.Error("some message", "x", 1, "y", 3.2) + + var v map[string]interface{} + decoder := json.NewDecoder(buf) + if err := decoder.Decode(&v); err != nil { + t.Fatalf("Error decoding JSON: %v", v) + } + + validate := func(key string, expected interface{}) { + if v[key] != expected { + t.Fatalf("Got %v expected %v for %v", v[key], expected, key) + } + } + + validate("msg", "some message") + validate("x", float64(1)) // all numbers are floats in JSON land + validate("y", 3.2) +} + +type testtype struct { + name string +} + +func (tt testtype) String() string { + return tt.name +} + +func TestLogfmt(t *testing.T) { + t.Parallel() + + var nilVal *testtype + + l, buf := testFormatter(LogfmtFormat()) + l.Error("some message", "x", 1, "y", 3.2, "equals", "=", "quote", "\"", "nil", nilVal) + + // skip timestamp in comparison + got := buf.Bytes()[27:buf.Len()] + expected := []byte(`lvl=eror msg="some message" x=1 y=3.200 equals="=" quote="\"" nil=nil` + "\n") + if !bytes.Equal(got, expected) { + t.Fatalf("Got %s, expected %s", got, expected) + } +} + +func TestMultiHandler(t *testing.T) { + t.Parallel() + + h1, r1 := testHandler() + h2, r2 := testHandler() + l := New() + l.SetHandler(MultiHandler(h1, h2)) + l.Debug("clone") + + if r1.Msg != "clone" { + t.Fatalf("wrong value for h1.Msg. Got %s expected %s", r1.Msg, "clone") + } + + if r2.Msg != "clone" { + t.Fatalf("wrong value for h2.Msg. Got %s expected %s", r2.Msg, "clone") + } + +} + +type waitHandler struct { + ch chan Record +} + +func (h *waitHandler) Log(r *Record) error { + h.ch <- *r + return nil +} + +func TestBufferedHandler(t *testing.T) { + t.Parallel() + + ch := make(chan Record) + l := New() + l.SetHandler(BufferedHandler(0, &waitHandler{ch})) + + l.Debug("buffer") + if r := <-ch; r.Msg != "buffer" { + t.Fatalf("wrong value for r.Msg. Got %s expected %s", r.Msg, "") + } +} + +func TestLogContext(t *testing.T) { + t.Parallel() + + l, _, r := testLogger() + l = l.New("foo", "bar") + l.Crit("baz") + + if len(r.Ctx) != 2 { + t.Fatalf("Expected logger context in record context. Got length %d, expected %d", len(r.Ctx), 2) + } + + if r.Ctx[0] != "foo" { + t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], "foo") + } + + if r.Ctx[1] != "bar" { + t.Fatalf("Wrong context value, got %s expected %s", r.Ctx[1], "bar") + } +} + +func TestMapCtx(t *testing.T) { + t.Parallel() + + l, _, r := testLogger() + l.Crit("test", Ctx{"foo": "bar"}) + + if len(r.Ctx) != 2 { + t.Fatalf("Wrong context length, got %d, expected %d", len(r.Ctx), 2) + } + + if r.Ctx[0] != "foo" { + t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], "foo") + } + + if r.Ctx[1] != "bar" { + t.Fatalf("Wrong context value, got %s expected %s", r.Ctx[1], "bar") + } +} + +func TestLvlFilterHandler(t *testing.T) { + t.Parallel() + + l := New() + h, r := testHandler() + l.SetHandler(LvlFilterHandler(LvlWarn, h)) + l.Info("info'd") + + if r.Msg != "" { + t.Fatalf("Expected zero record, but got record with msg: %v", r.Msg) + } + + l.Warn("warned") + if r.Msg != "warned" { + t.Fatalf("Got record msg %s expected %s", r.Msg, "warned") + } + + l.Warn("error'd") + if r.Msg != "error'd" { + t.Fatalf("Got record msg %s expected %s", r.Msg, "error'd") + } +} + +func TestNetHandler(t *testing.T) { + t.Parallel() + + l, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Failed to listen: %v", l) + } + + errs := make(chan error) + go func() { + c, err := l.Accept() + if err != nil { + t.Errorf("Failed to accept conneciton: %v", err) + return + } + + rd := bufio.NewReader(c) + s, err := rd.ReadString('\n') + if err != nil { + t.Errorf("Failed to read string: %v", err) + } + + got := s[27:] + expected := "lvl=info msg=test x=1\n" + if got != expected { + t.Errorf("Got log line %s, expected %s", got, expected) + } + + errs <- nil + }() + + lg := New() + h, err := NetHandler("tcp", l.Addr().String(), LogfmtFormat()) + if err != nil { + t.Fatal(err) + } + lg.SetHandler(h) + lg.Info("test", "x", 1) + + select { + case <-time.After(time.Second): + t.Fatalf("Test timed out!") + case <-errs: + // ok + } +} + +func TestMatchFilterHandler(t *testing.T) { + t.Parallel() + + l, h, r := testLogger() + l.SetHandler(MatchFilterHandler("err", nil, h)) + + l.Crit("test", "foo", "bar") + if r.Msg != "" { + t.Fatalf("expected filter handler to discard msg") + } + + l.Crit("test2", "err", "bad fd") + if r.Msg != "" { + t.Fatalf("expected filter handler to discard msg") + } + + l.Crit("test3", "err", nil) + if r.Msg != "test3" { + t.Fatalf("expected filter handler to allow msg") + } +} + +func TestMatchFilterBuiltin(t *testing.T) { + t.Parallel() + + l, h, r := testLogger() + l.SetHandler(MatchFilterHandler("lvl", LvlError, h)) + l.Info("does not pass") + + if r.Msg != "" { + t.Fatalf("got info level record that should not have matched") + } + + l.Error("error!") + if r.Msg != "error!" { + t.Fatalf("did not get error level record that should have matched") + } + + r.Msg = "" + l.SetHandler(MatchFilterHandler("msg", "matching message", h)) + l.Info("doesn't match") + if r.Msg != "" { + t.Fatalf("got record with wrong message matched") + } + + l.Debug("matching message") + if r.Msg != "matching message" { + t.Fatalf("did not get record which matches") + } +} + +type failingWriter struct { + fail bool +} + +func (w *failingWriter) Write(buf []byte) (int, error) { + if w.fail { + return 0, errors.New("fail") + } else { + return len(buf), nil + } +} + +func TestFailoverHandler(t *testing.T) { + t.Parallel() + + l := New() + h, r := testHandler() + w := &failingWriter{false} + + l.SetHandler(FailoverHandler( + StreamHandler(w, JsonFormat()), + h)) + + l.Debug("test ok") + if r.Msg != "" { + t.Fatalf("expected no failover") + } + + w.fail = true + l.Debug("test failover", "x", 1) + if r.Msg != "test failover" { + t.Fatalf("expected failover") + } + + if len(r.Ctx) != 4 { + t.Fatalf("expected additional failover ctx") + } + + got := r.Ctx[2] + expected := "failover_err_0" + if got != expected { + t.Fatalf("expected failover ctx. got: %s, expected %s", got, expected) + } +} + +// https://github.com/inconshreveable/log15/issues/16 +func TestIndependentSetHandler(t *testing.T) { + t.Parallel() + + parent, _, r := testLogger() + child := parent.New() + child.SetHandler(DiscardHandler()) + parent.Info("test") + if r.Msg != "test" { + t.Fatalf("parent handler affected by child") + } +} + +// https://github.com/inconshreveable/log15/issues/16 +func TestInheritHandler(t *testing.T) { + t.Parallel() + + parent, _, r := testLogger() + child := parent.New() + parent.SetHandler(DiscardHandler()) + child.Info("test") + if r.Msg == "test" { + t.Fatalf("child handler affected not affected by parent") + } +} + +func TestCallerFileHandler(t *testing.T) { + t.Parallel() + + l := New() + h, r := testHandler() + l.SetHandler(CallerFileHandler(h)) + + l.Info("baz") + _, _, line, _ := runtime.Caller(0) + + if len(r.Ctx) != 2 { + t.Fatalf("Expected caller in record context. Got length %d, expected %d", len(r.Ctx), 2) + } + + const key = "caller" + + if r.Ctx[0] != key { + t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], key) + } + + s, ok := r.Ctx[1].(string) + if !ok { + t.Fatalf("Wrong context value type, got %T expected string", r.Ctx[1]) + } + + exp := fmt.Sprint("log15_test.go:", line-1) + if s != exp { + t.Fatalf("Wrong context value, got %s expected string matching %s", s, exp) + } +} + +func TestCallerFuncHandler(t *testing.T) { + t.Parallel() + + l := New() + h, r := testHandler() + l.SetHandler(CallerFuncHandler(h)) + + l.Info("baz") + + if len(r.Ctx) != 2 { + t.Fatalf("Expected caller in record context. Got length %d, expected %d", len(r.Ctx), 2) + } + + const key = "fn" + + if r.Ctx[0] != key { + t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], key) + } + + const regex = ".*\\.TestCallerFuncHandler" + + s, ok := r.Ctx[1].(string) + if !ok { + t.Fatalf("Wrong context value type, got %T expected string", r.Ctx[1]) + } + + match, err := regexp.MatchString(regex, s) + if err != nil { + t.Fatalf("Error matching %s to regex %s: %v", s, regex, err) + } + + if !match { + t.Fatalf("Wrong context value, got %s expected string matching %s", s, regex) + } +} + +// https://github.com/inconshreveable/log15/issues/27 +func TestCallerStackHandler(t *testing.T) { + t.Parallel() + + l := New() + h, r := testHandler() + l.SetHandler(CallerStackHandler("%#v", h)) + + lines := []int{} + + func() { + l.Info("baz") + _, _, line, _ := runtime.Caller(0) + lines = append(lines, line-1) + }() + _, file, line, _ := runtime.Caller(0) + lines = append(lines, line-1) + + if len(r.Ctx) != 2 { + t.Fatalf("Expected stack in record context. Got length %d, expected %d", len(r.Ctx), 2) + } + + const key = "stack" + + if r.Ctx[0] != key { + t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], key) + } + + s, ok := r.Ctx[1].(string) + if !ok { + t.Fatalf("Wrong context value type, got %T expected string", r.Ctx[1]) + } + + exp := "[" + for i, line := range lines { + if i > 0 { + exp += " " + } + exp += fmt.Sprint(file, ":", line) + } + exp += "]" + + if s != exp { + t.Fatalf("Wrong context value, got %s expected string matching %s", s, exp) + } +} + +// tests that when logging concurrently to the same logger +// from multiple goroutines that the calls are handled independently +// this test tries to trigger a previous bug where concurrent calls could +// corrupt each other's context values +// +// this test runs N concurrent goroutines each logging a fixed number of +// records and a handler that buckets them based on the index passed in the context. +// if the logger is not concurrent-safe then the values in the buckets will not all be the same +// +// https://github.com/inconshreveable/log15/pull/30 +func TestConcurrent(t *testing.T) { + root := New() + // this was the first value that triggered + // go to allocate extra capacity in the logger's context slice which + // was necessary to trigger the bug + const ctxLen = 34 + l := root.New(make([]interface{}, ctxLen)...) + const goroutines = 8 + var res [goroutines]int + l.SetHandler(SyncHandler(FuncHandler(func(r *Record) error { + res[r.Ctx[ctxLen+1].(int)]++ + return nil + }))) + var wg sync.WaitGroup + wg.Add(goroutines) + for i := 0; i < goroutines; i++ { + go func(idx int) { + defer wg.Done() + for j := 0; j < 10000; j++ { + l.Info("test message", "goroutine_idx", idx) + } + }(i) + } + wg.Wait() + for _, val := range res[:] { + if val != 10000 { + t.Fatalf("Wrong number of messages for context: %+v", res) + } + } +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/logger.go b/vendor/gopkg.in/inconshreveable/log15.v2/logger.go new file mode 100644 index 0000000..dcd7cf8 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/logger.go @@ -0,0 +1,201 @@ +package log15 + +import ( + "fmt" + "runtime" + "time" +) + +const timeKey = "t" +const lvlKey = "lvl" +const msgKey = "msg" +const errorKey = "LOG15_ERROR" + +type Lvl int + +const ( + LvlCrit Lvl = iota + LvlError + LvlWarn + LvlInfo + LvlDebug +) + +// Returns the name of a Lvl +func (l Lvl) String() string { + switch l { + case LvlDebug: + return "dbug" + case LvlInfo: + return "info" + case LvlWarn: + return "warn" + case LvlError: + return "eror" + case LvlCrit: + return "crit" + default: + panic("bad level") + } +} + +// Returns the appropriate Lvl from a string name. +// Useful for parsing command line args and configuration files. +func LvlFromString(lvlString string) (Lvl, error) { + switch lvlString { + case "debug", "dbug": + return LvlDebug, nil + case "info": + return LvlInfo, nil + case "warn": + return LvlWarn, nil + case "error", "eror": + return LvlError, nil + case "crit": + return LvlCrit, nil + default: + return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString) + } +} + +// A Record is what a Logger asks its handler to write +type Record struct { + Time time.Time + Lvl Lvl + Msg string + Ctx []interface{} + CallPC [1]uintptr + KeyNames RecordKeyNames +} + +type RecordKeyNames struct { + Time string + Msg string + Lvl string +} + +// A Logger writes key/value pairs to a Handler +type Logger interface { + // New returns a new Logger that has this logger's context plus the given context + New(ctx ...interface{}) Logger + + // SetHandler updates the logger to write records to the specified handler. + SetHandler(h Handler) + + // Log a message at the given level with context key/value pairs + Debug(msg string, ctx ...interface{}) + Info(msg string, ctx ...interface{}) + Warn(msg string, ctx ...interface{}) + Error(msg string, ctx ...interface{}) + Crit(msg string, ctx ...interface{}) +} + +type logger struct { + ctx []interface{} + h *swapHandler +} + +func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) { + r := Record{ + Time: time.Now(), + Lvl: lvl, + Msg: msg, + Ctx: newContext(l.ctx, ctx), + KeyNames: RecordKeyNames{ + Time: timeKey, + Msg: msgKey, + Lvl: lvlKey, + }, + } + runtime.Callers(3, r.CallPC[:]) + l.h.Log(&r) +} + +func (l *logger) New(ctx ...interface{}) Logger { + child := &logger{newContext(l.ctx, ctx), new(swapHandler)} + child.SetHandler(l.h) + return child +} + +func newContext(prefix []interface{}, suffix []interface{}) []interface{} { + normalizedSuffix := normalize(suffix) + newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix)) + n := copy(newCtx, prefix) + copy(newCtx[n:], normalizedSuffix) + return newCtx +} + +func (l *logger) Debug(msg string, ctx ...interface{}) { + l.write(msg, LvlDebug, ctx) +} + +func (l *logger) Info(msg string, ctx ...interface{}) { + l.write(msg, LvlInfo, ctx) +} + +func (l *logger) Warn(msg string, ctx ...interface{}) { + l.write(msg, LvlWarn, ctx) +} + +func (l *logger) Error(msg string, ctx ...interface{}) { + l.write(msg, LvlError, ctx) +} + +func (l *logger) Crit(msg string, ctx ...interface{}) { + l.write(msg, LvlCrit, ctx) +} + +func (l *logger) SetHandler(h Handler) { + l.h.Swap(h) +} + +func normalize(ctx []interface{}) []interface{} { + // if the caller passed a Ctx object, then expand it + if len(ctx) == 1 { + if ctxMap, ok := ctx[0].(Ctx); ok { + ctx = ctxMap.toArray() + } + } + + // ctx needs to be even because it's a series of key/value pairs + // no one wants to check for errors on logging functions, + // so instead of erroring on bad input, we'll just make sure + // that things are the right length and users can fix bugs + // when they see the output looks wrong + if len(ctx)%2 != 0 { + ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil") + } + + return ctx +} + +// Lazy allows you to defer calculation of a logged value that is expensive +// to compute until it is certain that it must be evaluated with the given filters. +// +// Lazy may also be used in conjunction with a Logger's New() function +// to generate a child logger which always reports the current value of changing +// state. +// +// You may wrap any function which takes no arguments to Lazy. It may return any +// number of values of any type. +type Lazy struct { + Fn interface{} +} + +// Ctx is a map of key/value pairs to pass as context to a log function +// Use this only if you really need greater safety around the arguments you pass +// to the logging functions. +type Ctx map[string]interface{} + +func (c Ctx) toArray() []interface{} { + arr := make([]interface{}, len(c)*2) + + i := 0 + for k, v := range c { + arr[i] = k + arr[i+1] = v + i += 2 + } + + return arr +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/root.go b/vendor/gopkg.in/inconshreveable/log15.v2/root.go new file mode 100644 index 0000000..f07616a --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/root.go @@ -0,0 +1,67 @@ +package log15 + +import ( + "os" + + "github.com/mattn/go-colorable" + "gopkg.in/inconshreveable/log15.v2/term" +) + +var ( + root *logger + StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat()) + StderrHandler = StreamHandler(os.Stderr, LogfmtFormat()) +) + +func init() { + if term.IsTty(os.Stdout.Fd()) { + StdoutHandler = StreamHandler(colorable.NewColorableStdout(), TerminalFormat()) + } + + if term.IsTty(os.Stderr.Fd()) { + StderrHandler = StreamHandler(colorable.NewColorableStderr(), TerminalFormat()) + } + + root = &logger{[]interface{}{}, new(swapHandler)} + root.SetHandler(StdoutHandler) +} + +// New returns a new logger with the given context. +// New is a convenient alias for Root().New +func New(ctx ...interface{}) Logger { + return root.New(ctx...) +} + +// Root returns the root logger +func Root() Logger { + return root +} + +// The following functions bypass the exported logger methods (logger.Debug, +// etc.) to keep the call depth the same for all paths to logger.write so +// runtime.Caller(2) always refers to the call site in client code. + +// Debug is a convenient alias for Root().Debug +func Debug(msg string, ctx ...interface{}) { + root.write(msg, LvlDebug, ctx) +} + +// Info is a convenient alias for Root().Info +func Info(msg string, ctx ...interface{}) { + root.write(msg, LvlInfo, ctx) +} + +// Warn is a convenient alias for Root().Warn +func Warn(msg string, ctx ...interface{}) { + root.write(msg, LvlWarn, ctx) +} + +// Error is a convenient alias for Root().Error +func Error(msg string, ctx ...interface{}) { + root.write(msg, LvlError, ctx) +} + +// Crit is a convenient alias for Root().Crit +func Crit(msg string, ctx ...interface{}) { + root.write(msg, LvlCrit, ctx) +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack.go b/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack.go new file mode 100644 index 0000000..ae3021c --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack.go @@ -0,0 +1,225 @@ +// Package stack implements utilities to capture, manipulate, and format call +// stacks. +package stack + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +// Call records a single function invocation from a goroutine stack. It is a +// wrapper for the program counter values returned by runtime.Caller and +// runtime.Callers and consumed by runtime.FuncForPC. +type Call uintptr + +// Format implements fmt.Formatter with support for the following verbs. +// +// %s source file +// %d line number +// %n function name +// %v equivalent to %s:%d +// +// It accepts the '+' and '#' flags for most of the verbs as follows. +// +// %+s path of source file relative to the compile time GOPATH +// %#s full path of source file +// %+n import path qualified function name +// %+v equivalent to %+s:%d +// %#v equivalent to %#s:%d +func (pc Call) Format(s fmt.State, c rune) { + // BUG(ChrisHines): Subtracting one from pc is a work around for + // https://code.google.com/p/go/issues/detail?id=7690. The idea for this + // work around comes from rsc's initial patch at + // https://codereview.appspot.com/84100043/#ps20001, but as noted in the + // issue discussion, it is not a complete fix since it doesn't handle some + // cases involving signals. Just the same, it handles all of the other + // cases I have tested. + pcFix := uintptr(pc) - 1 + fn := runtime.FuncForPC(pcFix) + if fn == nil { + fmt.Fprintf(s, "%%!%c(NOFUNC)", c) + return + } + + switch c { + case 's', 'v': + file, line := fn.FileLine(pcFix) + switch { + case s.Flag('#'): + // done + case s.Flag('+'): + // Here we want to get the source file path relative to the + // compile time GOPATH. As of Go 1.3.x there is no direct way to + // know the compiled GOPATH at runtime, but we can infer the + // number of path segments in the GOPATH. We note that fn.Name() + // returns the function name qualified by the import path, which + // does not include the GOPATH. Thus we can trim segments from the + // beginning of the file path until the number of path separators + // remaining is one more than the number of path separators in the + // function name. For example, given: + // + // GOPATH /home/user + // file /home/user/src/pkg/sub/file.go + // fn.Name() pkg/sub.Type.Method + // + // We want to produce: + // + // pkg/sub/file.go + // + // From this we can easily see that fn.Name() has one less path + // separator than our desired output. + const sep = "/" + impCnt := strings.Count(fn.Name(), sep) + 1 + pathCnt := strings.Count(file, sep) + for pathCnt > impCnt { + i := strings.Index(file, sep) + if i == -1 { + break + } + file = file[i+len(sep):] + pathCnt-- + } + default: + const sep = "/" + if i := strings.LastIndex(file, sep); i != -1 { + file = file[i+len(sep):] + } + } + fmt.Fprint(s, file) + if c == 'v' { + fmt.Fprint(s, ":", line) + } + + case 'd': + _, line := fn.FileLine(pcFix) + fmt.Fprint(s, line) + + case 'n': + name := fn.Name() + if !s.Flag('+') { + const pathSep = "/" + if i := strings.LastIndex(name, pathSep); i != -1 { + name = name[i+len(pathSep):] + } + const pkgSep = "." + if i := strings.Index(name, pkgSep); i != -1 { + name = name[i+len(pkgSep):] + } + } + fmt.Fprint(s, name) + } +} + +// Callers returns a Trace for the current goroutine with element 0 +// identifying the calling function. +func Callers() Trace { + pcs := poolBuf() + pcs = pcs[:cap(pcs)] + n := runtime.Callers(2, pcs) + cs := make([]Call, n) + for i, pc := range pcs[:n] { + cs[i] = Call(pc) + } + putPoolBuf(pcs) + return cs +} + +// name returns the import path qualified name of the function containing the +// call. +func (pc Call) name() string { + pcFix := uintptr(pc) - 1 // work around for go issue #7690 + fn := runtime.FuncForPC(pcFix) + if fn == nil { + return "???" + } + return fn.Name() +} + +func (pc Call) file() string { + pcFix := uintptr(pc) - 1 // work around for go issue #7690 + fn := runtime.FuncForPC(pcFix) + if fn == nil { + return "???" + } + file, _ := fn.FileLine(pcFix) + return file +} + +// Trace records a sequence of function invocations from a goroutine stack. +type Trace []Call + +// Format implements fmt.Formatter by printing the Trace as square brackes ([, +// ]) surrounding a space separated list of Calls each formatted with the +// supplied verb and options. +func (pcs Trace) Format(s fmt.State, c rune) { + s.Write([]byte("[")) + for i, pc := range pcs { + if i > 0 { + s.Write([]byte(" ")) + } + pc.Format(s, c) + } + s.Write([]byte("]")) +} + +// TrimBelow returns a slice of the Trace with all entries below pc removed. +func (pcs Trace) TrimBelow(pc Call) Trace { + for len(pcs) > 0 && pcs[0] != pc { + pcs = pcs[1:] + } + return pcs +} + +// TrimAbove returns a slice of the Trace with all entries above pc removed. +func (pcs Trace) TrimAbove(pc Call) Trace { + for len(pcs) > 0 && pcs[len(pcs)-1] != pc { + pcs = pcs[:len(pcs)-1] + } + return pcs +} + +// TrimBelowName returns a slice of the Trace with all entries below the +// lowest with function name name removed. +func (pcs Trace) TrimBelowName(name string) Trace { + for len(pcs) > 0 && pcs[0].name() != name { + pcs = pcs[1:] + } + return pcs +} + +// TrimAboveName returns a slice of the Trace with all entries above the +// highest with function name name removed. +func (pcs Trace) TrimAboveName(name string) Trace { + for len(pcs) > 0 && pcs[len(pcs)-1].name() != name { + pcs = pcs[:len(pcs)-1] + } + return pcs +} + +var goroot string + +func init() { + goroot = filepath.ToSlash(runtime.GOROOT()) + if runtime.GOOS == "windows" { + goroot = strings.ToLower(goroot) + } +} + +func inGoroot(path string) bool { + if runtime.GOOS == "windows" { + path = strings.ToLower(path) + } + return strings.HasPrefix(path, goroot) +} + +// TrimRuntime returns a slice of the Trace with the topmost entries from the +// go runtime removed. It considers any calls originating from files under +// GOROOT as part of the runtime. +func (pcs Trace) TrimRuntime() Trace { + for len(pcs) > 0 && inGoroot(pcs[len(pcs)-1].file()) { + pcs = pcs[:len(pcs)-1] + } + return pcs +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack_pool.go b/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack_pool.go new file mode 100644 index 0000000..34f2ca9 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack_pool.go @@ -0,0 +1,19 @@ +// +build go1.3 + +package stack + +import ( + "sync" +) + +var pcStackPool = sync.Pool{ + New: func() interface{} { return make([]uintptr, 1000) }, +} + +func poolBuf() []uintptr { + return pcStackPool.Get().([]uintptr) +} + +func putPoolBuf(p []uintptr) { + pcStackPool.Put(p) +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack_pool_chan.go b/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack_pool_chan.go new file mode 100644 index 0000000..a9d6c15 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack_pool_chan.go @@ -0,0 +1,27 @@ +// +build !go1.3 appengine + +package stack + +const ( + stackPoolSize = 64 +) + +var ( + pcStackPool = make(chan []uintptr, stackPoolSize) +) + +func poolBuf() []uintptr { + select { + case p := <-pcStackPool: + return p + default: + return make([]uintptr, 1000) + } +} + +func putPoolBuf(p []uintptr) { + select { + case pcStackPool <- p: + default: + } +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack_test.go b/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack_test.go new file mode 100644 index 0000000..14e97fa --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/stack/stack_test.go @@ -0,0 +1,231 @@ +package stack_test + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "runtime" + "testing" + + "gopkg.in/inconshreveable/log15.v2/stack" +) + +type testType struct{} + +func (tt testType) testMethod() (pc uintptr, file string, line int, ok bool) { + return runtime.Caller(0) +} + +func TestCallFormat(t *testing.T) { + t.Parallel() + + pc, file, line, ok := runtime.Caller(0) + if !ok { + t.Fatal("runtime.Caller(0) failed") + } + + gopathSrc := filepath.Join(os.Getenv("GOPATH"), "src") + relFile, err := filepath.Rel(gopathSrc, file) + if err != nil { + t.Fatalf("failed to determine path relative to GOPATH: %v", err) + } + relFile = filepath.ToSlash(relFile) + + pc2, file2, line2, ok2 := testType{}.testMethod() + if !ok2 { + t.Fatal("runtime.Caller(0) failed") + } + relFile2, err := filepath.Rel(gopathSrc, file) + if err != nil { + t.Fatalf("failed to determine path relative to GOPATH: %v", err) + } + relFile2 = filepath.ToSlash(relFile2) + + data := []struct { + pc uintptr + desc string + fmt string + out string + }{ + {0, "error", "%s", "%!s(NOFUNC)"}, + + {pc, "func", "%s", path.Base(file)}, + {pc, "func", "%+s", relFile}, + {pc, "func", "%#s", file}, + {pc, "func", "%d", fmt.Sprint(line)}, + {pc, "func", "%n", "TestCallFormat"}, + {pc, "func", "%+n", runtime.FuncForPC(pc).Name()}, + {pc, "func", "%v", fmt.Sprint(path.Base(file), ":", line)}, + {pc, "func", "%+v", fmt.Sprint(relFile, ":", line)}, + {pc, "func", "%#v", fmt.Sprint(file, ":", line)}, + {pc, "func", "%v|%[1]n()", fmt.Sprint(path.Base(file), ":", line, "|", "TestCallFormat()")}, + + {pc2, "meth", "%s", path.Base(file2)}, + {pc2, "meth", "%+s", relFile2}, + {pc2, "meth", "%#s", file2}, + {pc2, "meth", "%d", fmt.Sprint(line2)}, + {pc2, "meth", "%n", "testType.testMethod"}, + {pc2, "meth", "%+n", runtime.FuncForPC(pc2).Name()}, + {pc2, "meth", "%v", fmt.Sprint(path.Base(file2), ":", line2)}, + {pc2, "meth", "%+v", fmt.Sprint(relFile2, ":", line2)}, + {pc2, "meth", "%#v", fmt.Sprint(file2, ":", line2)}, + {pc2, "meth", "%v|%[1]n()", fmt.Sprint(path.Base(file2), ":", line2, "|", "testType.testMethod()")}, + } + + for _, d := range data { + got := fmt.Sprintf(d.fmt, stack.Call(d.pc)) + if got != d.out { + t.Errorf("fmt.Sprintf(%q, Call(%s)) = %s, want %s", d.fmt, d.desc, got, d.out) + } + } +} + +func BenchmarkCallVFmt(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("runtime.Caller(0) failed") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Fprint(ioutil.Discard, stack.Call(pc)) + } +} + +func BenchmarkCallPlusVFmt(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("runtime.Caller(0) failed") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Fprintf(ioutil.Discard, "%+v", stack.Call(pc)) + } +} + +func BenchmarkCallSharpVFmt(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("runtime.Caller(0) failed") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Fprintf(ioutil.Discard, "%#v", stack.Call(pc)) + } +} + +func BenchmarkCallSFmt(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("runtime.Caller(0) failed") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Fprintf(ioutil.Discard, "%s", stack.Call(pc)) + } +} + +func BenchmarkCallPlusSFmt(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("runtime.Caller(0) failed") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Fprintf(ioutil.Discard, "%+s", stack.Call(pc)) + } +} + +func BenchmarkCallSharpSFmt(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("runtime.Caller(0) failed") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Fprintf(ioutil.Discard, "%#s", stack.Call(pc)) + } +} + +func BenchmarkCallDFmt(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("runtime.Caller(0) failed") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Fprintf(ioutil.Discard, "%d", stack.Call(pc)) + } +} + +func BenchmarkCallNFmt(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("runtime.Caller(0) failed") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Fprintf(ioutil.Discard, "%n", stack.Call(pc)) + } +} + +func BenchmarkCallPlusNFmt(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("runtime.Caller(0) failed") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Fprintf(ioutil.Discard, "%+n", stack.Call(pc)) + } +} + +func BenchmarkCallers(b *testing.B) { + for i := 0; i < b.N; i++ { + stack.Callers() + } +} + +func deepStack(depth int, b *testing.B) stack.Trace { + if depth > 0 { + return deepStack(depth-1, b) + } + b.StartTimer() + s := stack.Callers() + b.StopTimer() + return s +} + +func BenchmarkCallers10(b *testing.B) { + b.StopTimer() + + for i := 0; i < b.N; i++ { + deepStack(10, b) + } +} + +func BenchmarkCallers50(b *testing.B) { + b.StopTimer() + + for i := 0; i < b.N; i++ { + deepStack(50, b) + } +} + +func BenchmarkCallers100(b *testing.B) { + b.StopTimer() + + for i := 0; i < b.N; i++ { + deepStack(100, b) + } +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/syslog.go b/vendor/gopkg.in/inconshreveable/log15.v2/syslog.go new file mode 100644 index 0000000..36c12b1 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/syslog.go @@ -0,0 +1,55 @@ +// +build !windows,!plan9 + +package log15 + +import ( + "log/syslog" + "strings" +) + +// SyslogHandler opens a connection to the system syslog daemon by calling +// syslog.New and writes all records to it. +func SyslogHandler(tag string, fmtr Format) (Handler, error) { + wr, err := syslog.New(syslog.LOG_INFO, tag) + return sharedSyslog(fmtr, wr, err) +} + +// SyslogHandler opens a connection to a log daemon over the network and writes +// all log records to it. +func SyslogNetHandler(net, addr string, tag string, fmtr Format) (Handler, error) { + wr, err := syslog.Dial(net, addr, syslog.LOG_INFO, tag) + return sharedSyslog(fmtr, wr, err) +} + +func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) { + if err != nil { + return nil, err + } + h := FuncHandler(func(r *Record) error { + var syslogFn = sysWr.Info + switch r.Lvl { + case LvlCrit: + syslogFn = sysWr.Crit + case LvlError: + syslogFn = sysWr.Err + case LvlWarn: + syslogFn = sysWr.Warning + case LvlInfo: + syslogFn = sysWr.Info + case LvlDebug: + syslogFn = sysWr.Debug + } + + s := strings.TrimSpace(string(fmtr.Format(r))) + return syslogFn(s) + }) + return LazyHandler(&closingHandler{sysWr, h}), nil +} + +func (m muster) SyslogHandler(tag string, fmtr Format) Handler { + return must(SyslogHandler(tag, fmtr)) +} + +func (m muster) SyslogNetHandler(net, addr string, tag string, fmtr Format) Handler { + return must(SyslogNetHandler(net, addr, tag, fmtr)) +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/term/LICENSE b/vendor/gopkg.in/inconshreveable/log15.v2/term/LICENSE new file mode 100644 index 0000000..f090cb4 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/term/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +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/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_appengine.go b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_appengine.go new file mode 100644 index 0000000..c1b5d2a --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_appengine.go @@ -0,0 +1,13 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +package term + +// IsTty always returns false on AppEngine. +func IsTty(fd uintptr) bool { + return false +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_darwin.go b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_darwin.go new file mode 100644 index 0000000..b05de4c --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_darwin.go @@ -0,0 +1,12 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package term + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_freebsd.go b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_freebsd.go new file mode 100644 index 0000000..cfaceab --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_freebsd.go @@ -0,0 +1,18 @@ +package term + +import ( + "syscall" +) + +const ioctlReadTermios = syscall.TIOCGETA + +// Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_linux.go b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_linux.go new file mode 100644 index 0000000..5290468 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_linux.go @@ -0,0 +1,14 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine + +package term + +import "syscall" + +const ioctlReadTermios = syscall.TCGETS + +type Termios syscall.Termios diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_notwindows.go b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_notwindows.go new file mode 100644 index 0000000..87df7d5 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_notwindows.go @@ -0,0 +1,20 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,!appengine darwin freebsd openbsd + +package term + +import ( + "syscall" + "unsafe" +) + +// IsTty returns true if the given file descriptor is a terminal. +func IsTty(fd uintptr) bool { + var termios Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_openbsd.go b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_openbsd.go new file mode 100644 index 0000000..571ece3 --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_openbsd.go @@ -0,0 +1,7 @@ +package term + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_windows.go b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_windows.go new file mode 100644 index 0000000..df3c30c --- /dev/null +++ b/vendor/gopkg.in/inconshreveable/log15.v2/term/terminal_windows.go @@ -0,0 +1,26 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package term + +import ( + "syscall" + "unsafe" +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") +) + +// IsTty returns true if the given file descriptor is a terminal. +func IsTty(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} diff --git a/view.go b/view.go new file mode 100644 index 0000000..073dfc5 --- /dev/null +++ b/view.go @@ -0,0 +1,221 @@ +package main + +import ( + "fmt" + "github.com/nsf/termbox-go" + "time" +) + +const ( + boardXOffset = 4 + boardYOffset = 2 +) + +type View struct { + drawDropMarkerDisabled bool +} + +func NewView() *View { + err := termbox.Init() + if err != nil { + panic(err) + } + termbox.SetInputMode(termbox.InputEsc) + termbox.Flush() + return &View{} +} + +func (view *View) Stop() { + logger.Info("View Stop start") + + termbox.Close() + + logger.Info("View Stop end") +} + +func (view *View) RefreshScreen() { + termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) + + view.drawBackground() + view.drawTexts() + board.DrawPreviewMino() + + if engine.gameOver { + view.drawGameOver() + } else { + board.DrawBoard() + board.DrawDropMino() + board.DrawCurrentMino() + } + + termbox.Flush() +} + +func (view *View) drawBackground() { + // playing board + xOffset := boardXOffset + yOffset := boardYOffset + xEnd := boardXOffset + boardWidth*2 + 4 + yEnd := boardYOffset + boardHeight + 2 + for x := xOffset; x < xEnd; x++ { + for y := yOffset; y < yEnd; y++ { + if x == xOffset || x == xOffset+1 || x == xEnd-1 || x == xEnd-2 || + y == yOffset || y == yEnd-1 { + termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorWhite) + } else { + termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorBlack) + } + } + } + + // piece preview + xOffset = boardXOffset + boardWidth*2 + 8 + yOffset = boardYOffset + xEnd = xOffset + 14 + yEnd = yOffset + 6 + for x := xOffset; x < xEnd; x++ { + for y := yOffset; y < yEnd; y++ { + if x == xOffset || x == xOffset+1 || x == xEnd-1 || x == xEnd-2 || + y == yOffset || y == yEnd-1 { + termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorWhite) + } else { + termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorBlack) + } + } + } + +} + +func (view *View) drawTexts() { + xOffset := boardXOffset + boardWidth*2 + 8 + yOffset := boardYOffset + 7 + + view.drawText(xOffset, yOffset, "SCORE:", termbox.ColorWhite, termbox.ColorBlue) + view.drawText(xOffset+7, yOffset, fmt.Sprintf("%7d", engine.score), termbox.ColorBlack, termbox.ColorWhite) + + yOffset += 2 + + view.drawText(xOffset, yOffset, "LINES:", termbox.ColorWhite, termbox.ColorBlue) + view.drawText(xOffset+7, yOffset, fmt.Sprintf("%7d", engine.deleteLines), termbox.ColorBlack, termbox.ColorWhite) + + yOffset += 2 + + view.drawText(xOffset, yOffset, "LEVEL:", termbox.ColorWhite, termbox.ColorBlue) + view.drawText(xOffset+7, yOffset, fmt.Sprintf("%4d", engine.level), termbox.ColorBlack, termbox.ColorWhite) + + yOffset += 2 + + // ascii arrow characters add extra two spaces + view.drawText(xOffset, yOffset, "← - left", termbox.ColorWhite, termbox.ColorBlack) + yOffset++ + view.drawText(xOffset, yOffset, "z - rotate left", termbox.ColorWhite, termbox.ColorBlack) + yOffset++ + view.drawText(xOffset, yOffset, "x - rotate right", termbox.ColorWhite, termbox.ColorBlack) + yOffset++ + view.drawText(xOffset, yOffset, "→ - right", termbox.ColorWhite, termbox.ColorBlack) + yOffset++ + view.drawText(xOffset, yOffset, "↓ - soft drop", termbox.ColorWhite, termbox.ColorBlack) + yOffset++ + view.drawText(xOffset, yOffset, "↑ - hard drop", termbox.ColorWhite, termbox.ColorBlack) + yOffset++ + view.drawText(xOffset, yOffset, "sbar - hard drop", termbox.ColorWhite, termbox.ColorBlack) + yOffset++ + view.drawText(xOffset, yOffset, "p - pause", termbox.ColorWhite, termbox.ColorBlack) + yOffset++ + view.drawText(xOffset, yOffset, "q - quit", termbox.ColorWhite, termbox.ColorBlack) +} + +func (view *View) DrawPreviewMinoBlock(x int, y int, color termbox.Attribute, rotation int, length int) { + var char1 rune + var char2 rune + if rotation < 2 { + char1 = '▓' + char2 = ' ' + } else { + char1 = ' ' + char2 = '▓' + } + xOffset := 2*x + 2*boardWidth + boardXOffset + 11 + (4 - length) + termbox.SetCell(xOffset, y+boardYOffset+2, char1, color, color^termbox.AttrBold) + termbox.SetCell(xOffset+1, y+boardYOffset+2, char2, color, color^termbox.AttrBold) +} + +func (view *View) DrawBlock(x int, y int, color termbox.Attribute, rotation int) { + var char1 rune + var char2 rune + if rotation < 2 { + char1 = '▓' + char2 = ' ' + } else { + char1 = ' ' + char2 = '▓' + } + if color == blankColor { + // blankColor means drop Mino + termbox.SetCell(2*x+boardXOffset+2, y+boardYOffset+1, char1, termbox.ColorBlack|termbox.AttrBold, termbox.ColorWhite) + termbox.SetCell(2*x+boardXOffset+3, y+boardYOffset+1, char2, termbox.ColorBlack|termbox.AttrBold, termbox.ColorWhite) + } else { + termbox.SetCell(2*x+boardXOffset+2, y+boardYOffset+1, char1, color, color^termbox.AttrBold) + termbox.SetCell(2*x+boardXOffset+3, y+boardYOffset+1, char2, color, color^termbox.AttrBold) + } +} + +func (view *View) drawGameOver() { + xOffset := boardXOffset + 4 + yOffset := boardYOffset + 2 + + view.drawText(xOffset, yOffset, " GAME OVER", termbox.ColorWhite, termbox.ColorBlack) + yOffset += 2 + view.drawText(xOffset, yOffset, "sbar for new game", termbox.ColorWhite, termbox.ColorBlack) + yOffset += 2 + xOffset += 2 + for index, line := range engine.ranking.scores { + view.drawText(xOffset, yOffset+index, fmt.Sprintf("%2d: %6d", index+1, line), termbox.ColorWhite, termbox.ColorBlack) + } +} + +func (view *View) drawText(x int, y int, text string, fg termbox.Attribute, bg termbox.Attribute) { + for index, char := range text { + termbox.SetCell(x+index, y, rune(char), fg, bg) + } +} + +func (view *View) ShowDeleteAnimation(lines []int) { + view.drawDropMarkerDisabled = true + + view.RefreshScreen() + + for times := 0; times < 3; times++ { + for _, y := range lines { + view.colorizeLine(y, termbox.ColorCyan) + } + termbox.Flush() + time.Sleep(140 * time.Millisecond) + + view.RefreshScreen() + time.Sleep(140 * time.Millisecond) + } + + view.drawDropMarkerDisabled = false +} + +func (view *View) ShowGameOverAnimation() { + view.drawDropMarkerDisabled = true + + view.RefreshScreen() + + for y := boardHeight - 1; y >= 0; y-- { + view.colorizeLine(y, termbox.ColorBlack) + termbox.Flush() + time.Sleep(60 * time.Millisecond) + } + + view.drawDropMarkerDisabled = false +} + +func (view *View) colorizeLine(y int, color termbox.Attribute) { + for x := 0; x < boardWidth; x++ { + termbox.SetCell(x*2+boardXOffset+2, y+boardYOffset+1, ' ', termbox.ColorDefault, color) + termbox.SetCell(x*2+boardXOffset+3, y+boardYOffset+1, ' ', termbox.ColorDefault, color) + } +}