136 lines
3.2 KiB
Go
136 lines
3.2 KiB
Go
package ansii
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
type ANSI string
|
|
|
|
const (
|
|
reset ANSI = "\033[0m"
|
|
plain ANSI = ""
|
|
bold ANSI = "\033[1m"
|
|
underline ANSI = "\033[4m"
|
|
black ANSI = "\033[30m"
|
|
red ANSI = "\033[31m"
|
|
green ANSI = "\033[32m"
|
|
yellow ANSI = "\033[33m"
|
|
blue ANSI = "\033[34m"
|
|
purple ANSI = "\033[35m"
|
|
cyan ANSI = "\033[36m"
|
|
white ANSI = "\033[37m"
|
|
blackBg ANSI = "\033[40m"
|
|
redBg ANSI = "\033[41m"
|
|
greenBg ANSI = "\033[42m"
|
|
yellowBg ANSI = "\033[43m"
|
|
blueBg ANSI = "\033[44m"
|
|
purpleBg ANSI = "\033[45m"
|
|
cyanBg ANSI = "\033[46m"
|
|
whiteBg ANSI = "\033[47m"
|
|
clearScreen ANSI = "\033[2J"
|
|
hideCursor ANSI = "\033[?25l"
|
|
showCursor ANSI = "\033[?25h"
|
|
)
|
|
|
|
type Offset struct {
|
|
X int
|
|
Y int
|
|
}
|
|
|
|
type style struct {
|
|
Reset ANSI
|
|
Plain ANSI
|
|
Bold ANSI
|
|
Underline ANSI
|
|
}
|
|
|
|
type color struct {
|
|
Black ANSI
|
|
Red ANSI
|
|
Green ANSI
|
|
Yellow ANSI
|
|
Blue ANSI
|
|
Purple ANSI
|
|
Cyan ANSI
|
|
White ANSI
|
|
}
|
|
|
|
type screen struct {
|
|
ClearScreen ANSI
|
|
HideCursor ANSI
|
|
ShowCursor ANSI
|
|
}
|
|
|
|
type ascii struct {
|
|
Block string
|
|
}
|
|
|
|
func GetTermSize() (width int, height int) {
|
|
var fd int = int(os.Stdout.Fd())
|
|
width, height, err := term.GetSize(fd)
|
|
if err != nil {
|
|
fmt.Println(string(Screen.ClearScreen) + "Fatal: error getting terminal size.")
|
|
os.Exit(1)
|
|
}
|
|
return width, height
|
|
}
|
|
|
|
func MakeTermRaw() (*term.State, error) {
|
|
var fd int = int(os.Stdout.Fd())
|
|
return term.MakeRaw(fd)
|
|
}
|
|
|
|
func RestoreTerm(prev *term.State) error {
|
|
var fd int = int(os.Stdout.Fd())
|
|
return term.Restore(fd, prev)
|
|
}
|
|
|
|
func (s screen) PlaceCursor(offset Offset) ANSI {
|
|
return ANSI(fmt.Sprintf("\033[%d;%dH", offset.Y, offset.X))
|
|
}
|
|
|
|
var (
|
|
Styles = style{Bold: bold, Underline: underline, Reset: reset, Plain: plain}
|
|
Colors = color{Red: red, Green: green, Yellow: yellow, Blue: blue, Purple: purple, Cyan: cyan, White: white}
|
|
Screen = screen{ClearScreen: clearScreen, HideCursor: hideCursor, ShowCursor: showCursor}
|
|
Blocks = ascii{Block: "█"}
|
|
)
|
|
|
|
// Draws a box of dimensions `height` and `width` at `offset`.
|
|
// The `offset` is the top left cell of the square.
|
|
// Blocks that would be placed off screen are clipped.
|
|
func DrawBox(builder *strings.Builder, offset Offset, height int, width int, style ANSI) {
|
|
builder.WriteString(string(style))
|
|
for hIdx := 0; hIdx < height; hIdx++ {
|
|
|
|
if hIdx == 0 || hIdx == height-1 {
|
|
for wIdx := 0; wIdx < width; wIdx++ {
|
|
DrawPixel(builder, Offset{X: offset.X + wIdx, Y: offset.Y + hIdx})
|
|
}
|
|
} else {
|
|
DrawPixel(builder, Offset{X: offset.X, Y: offset.Y + hIdx})
|
|
DrawPixel(builder, Offset{X: offset.X + width - 1, Y: offset.Y + hIdx})
|
|
}
|
|
}
|
|
builder.WriteString(string(Styles.Reset))
|
|
return
|
|
}
|
|
|
|
func DrawPixel(builder *strings.Builder, offset Offset) {
|
|
termWidth, termHeight := GetTermSize()
|
|
if offset.X > termWidth || offset.Y > termHeight || offset.X < 0 || offset.Y < 0 {
|
|
return
|
|
}
|
|
builder.WriteString(string(Screen.PlaceCursor(offset) + ANSI(Blocks.Block)))
|
|
}
|
|
|
|
func DrawPixelStyle(builder *strings.Builder, offset Offset, style ANSI) {
|
|
builder.WriteString(string(style))
|
|
DrawPixel(builder, offset)
|
|
builder.WriteString(string(Styles.Reset))
|
|
}
|