281 lines
4.6 KiB
Go
281 lines
4.6 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"math/rand"
|
||
|
"os"
|
||
|
"os/signal"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/buger/goterm"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// Reset all custom styles
|
||
|
ansiReset = "\033[0m"
|
||
|
|
||
|
// Reset to default color
|
||
|
ansiResetColor = "\033[32m"
|
||
|
|
||
|
// Return curor to start of line and clean it
|
||
|
ansiResetLine = "\r\033[K"
|
||
|
)
|
||
|
|
||
|
// List of possible colors
|
||
|
const (
|
||
|
black = iota
|
||
|
red
|
||
|
green
|
||
|
yellow
|
||
|
blue
|
||
|
magenta
|
||
|
cyan
|
||
|
white
|
||
|
)
|
||
|
|
||
|
func getFgColor(code int) string {
|
||
|
return fmt.Sprintf("\033[3%dm", code)
|
||
|
}
|
||
|
|
||
|
func getBgColor(code int) string {
|
||
|
return fmt.Sprintf("\033[4%dm", code)
|
||
|
}
|
||
|
|
||
|
func fgColor(str string, color int) string {
|
||
|
return fmt.Sprintf("%s%s%s", getFgColor(color), str, ansiReset)
|
||
|
}
|
||
|
|
||
|
func bgColor(str string, color int) string {
|
||
|
return fmt.Sprintf("%s%s%s", getBgColor(color), str, ansiReset)
|
||
|
}
|
||
|
|
||
|
type xy [2]int
|
||
|
|
||
|
func (p xy) x() int {
|
||
|
return p[0]
|
||
|
}
|
||
|
|
||
|
func (p xy) y() int {
|
||
|
return p[1]
|
||
|
}
|
||
|
|
||
|
func (p xy) add(p2 xy) xy {
|
||
|
p[0] += p2[0]
|
||
|
p[1] += p2[1]
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
type terminal struct {
|
||
|
cursorPos xy
|
||
|
}
|
||
|
|
||
|
func (t *terminal) moveAbs(to xy) {
|
||
|
t.cursorPos = to
|
||
|
goterm.MoveCursor(to.x()+1, to.y()+1)
|
||
|
}
|
||
|
|
||
|
func (t *terminal) size() xy {
|
||
|
return xy{goterm.Width(), goterm.Height()}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
const (
|
||
|
boxBorderHoriz = iota
|
||
|
boxBorderVert
|
||
|
boxBorderTL
|
||
|
boxBorderTR
|
||
|
boxBorderBL
|
||
|
boxBorderBR
|
||
|
)
|
||
|
|
||
|
var boxDefault = []string{
|
||
|
"─",
|
||
|
"│",
|
||
|
"┌",
|
||
|
"┐",
|
||
|
"└",
|
||
|
"┘",
|
||
|
}
|
||
|
|
||
|
type box struct {
|
||
|
pos xy
|
||
|
size xy // if unset, auto-determined
|
||
|
body string
|
||
|
|
||
|
transparent bool
|
||
|
}
|
||
|
|
||
|
func (b box) lines() []string {
|
||
|
lines := strings.Split(b.body, "\n")
|
||
|
// if the last line is empty don't include it, it means there was a trailing
|
||
|
// newline (or the whole string is empty)
|
||
|
if lines[len(lines)-1] == "" {
|
||
|
lines = lines[:len(lines)-1]
|
||
|
}
|
||
|
return lines
|
||
|
}
|
||
|
|
||
|
func (b box) getSize() xy {
|
||
|
if b.size != (xy{}) {
|
||
|
return b.size
|
||
|
}
|
||
|
var size xy
|
||
|
for _, line := range b.lines() {
|
||
|
size[1]++
|
||
|
if l := len(line); l > size[0] {
|
||
|
size[0] = l
|
||
|
}
|
||
|
}
|
||
|
return size
|
||
|
}
|
||
|
|
||
|
func (b box) draw(term *terminal) {
|
||
|
chars := boxDefault
|
||
|
pos := b.pos
|
||
|
size := b.getSize()
|
||
|
w, h := size.x(), size.y()
|
||
|
|
||
|
// draw top line
|
||
|
term.moveAbs(pos)
|
||
|
goterm.Print(chars[boxBorderTL])
|
||
|
for i := 0; i < w; i++ {
|
||
|
goterm.Print(chars[boxBorderHoriz])
|
||
|
}
|
||
|
goterm.Print(chars[boxBorderTR])
|
||
|
|
||
|
drawLine := func(line string) {
|
||
|
pos[1]++
|
||
|
term.moveAbs(pos)
|
||
|
goterm.Print(chars[boxBorderVert])
|
||
|
if len(line) > w {
|
||
|
line = line[:w]
|
||
|
}
|
||
|
goterm.Print(line)
|
||
|
if b.transparent {
|
||
|
term.moveAbs(pos.add(xy{w + 1, 0}))
|
||
|
} else {
|
||
|
goterm.Print(strings.Repeat(" ", w-len(line)))
|
||
|
}
|
||
|
goterm.Print(chars[boxBorderVert])
|
||
|
}
|
||
|
|
||
|
// truncate lines if necessary
|
||
|
lines := b.lines()
|
||
|
if len(lines) > h {
|
||
|
lines = lines[:h]
|
||
|
}
|
||
|
|
||
|
// draw body
|
||
|
for _, line := range lines {
|
||
|
drawLine(line)
|
||
|
}
|
||
|
|
||
|
// draw empty lines
|
||
|
for i := 0; i < h-len(lines); i++ {
|
||
|
drawLine("")
|
||
|
}
|
||
|
|
||
|
// draw bottom line
|
||
|
pos[1]++
|
||
|
term.moveAbs(pos)
|
||
|
goterm.Print(chars[boxBorderBL])
|
||
|
for i := 0; i < w; i++ {
|
||
|
goterm.Print(chars[boxBorderHoriz])
|
||
|
}
|
||
|
goterm.Print(chars[boxBorderBR])
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
const (
|
||
|
framerate = 30
|
||
|
frameperiod = time.Second / time.Duration(framerate)
|
||
|
)
|
||
|
|
||
|
func debugf(str string, args ...interface{}) {
|
||
|
fmt.Fprintf(os.Stderr, str, args...)
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
rand.Seed(time.Now().UnixNano())
|
||
|
{ // exit signal handling, cause ctrl-c doesn't work with goterm otherwise
|
||
|
c := make(chan os.Signal, 1)
|
||
|
signal.Notify(c, os.Interrupt)
|
||
|
go func() {
|
||
|
<-c
|
||
|
goterm.Clear()
|
||
|
goterm.Flush()
|
||
|
os.Stdout.Sync()
|
||
|
os.Exit(0)
|
||
|
}()
|
||
|
}
|
||
|
|
||
|
term := new(terminal)
|
||
|
|
||
|
type movingBox struct {
|
||
|
box
|
||
|
xRight bool
|
||
|
yDown bool
|
||
|
}
|
||
|
|
||
|
randBox := func() movingBox {
|
||
|
tsize := term.size()
|
||
|
return movingBox{
|
||
|
box: box{
|
||
|
pos: xy{rand.Intn(tsize[0]), rand.Intn(tsize[1])},
|
||
|
},
|
||
|
xRight: rand.Intn(1) == 0,
|
||
|
yDown: rand.Intn(1) == 0,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
boxes := []movingBox{
|
||
|
randBox(),
|
||
|
randBox(),
|
||
|
randBox(),
|
||
|
randBox(),
|
||
|
randBox(),
|
||
|
}
|
||
|
|
||
|
for range time.Tick(frameperiod) {
|
||
|
goterm.Clear()
|
||
|
|
||
|
termSize := term.size()
|
||
|
now := time.Now()
|
||
|
for i := range boxes {
|
||
|
b := &boxes[i]
|
||
|
b.body = fmt.Sprintf("%d\n%s", now.Unix(), now.String())
|
||
|
|
||
|
size := b.getSize()
|
||
|
if b.pos[0] <= 0 {
|
||
|
b.xRight = true
|
||
|
} else if b.pos[0]+size[0]+2 > termSize[0] {
|
||
|
b.xRight = false
|
||
|
}
|
||
|
if b.pos[1] <= 0 {
|
||
|
b.yDown = true
|
||
|
} else if b.pos[1]+size[1]+2 > termSize[1] {
|
||
|
b.yDown = false
|
||
|
}
|
||
|
|
||
|
if b.xRight {
|
||
|
b.pos[0] += 3
|
||
|
} else {
|
||
|
b.pos[0] -= 3
|
||
|
}
|
||
|
if b.yDown {
|
||
|
b.pos[1]++
|
||
|
} else {
|
||
|
b.pos[1]--
|
||
|
}
|
||
|
|
||
|
b.draw(term)
|
||
|
}
|
||
|
goterm.Flush()
|
||
|
}
|
||
|
}
|