package tui
import (
"bytes"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"time"
"unicode/utf8"
"github.com/junegunn/uniseg"
"golang.org/x/term"
)
const (
defaultWidth = 80
defaultHeight = 24
defaultEscDelay = 100
escPollInterval = 5
offsetPollTries = 10
maxInputBuffer = 1024 * 1024
)
const consoleDevice string = "/dev/tty"
var offsetRegexp *regexp.Regexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]+)R")
var offsetRegexpBegin *regexp.Regexp = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R")
func (r *LightRenderer) PassThrough(str string) {
r.queued.WriteString("\x1b7" + str + "\x1b8")
}
func (r *LightRenderer) stderr(str string) {
r.stderrInternal(str, true, "")
}
const CR string = "\x1b[2m␍"
const LF string = "\x1b[2m␊"
func (r *LightRenderer) stderrInternal(str string, allowNLCR bool, resetCode string) {
bytes := []byte(str)
runes := []rune{}
for len(bytes) > 0 {
r, sz := utf8.DecodeRune(bytes)
nlcr := r == '\n' || r == '\r'
if r >= 32 || r == '\x1b' || nlcr {
if nlcr && !allowNLCR {
if r == '\r' {
runes = append(runes, []rune(CR+resetCode)...)
} else {
runes = append(runes, []rune(LF+resetCode)...)
}
} else if r != utf8.RuneError {
runes = append(runes, r)
}
}
bytes = bytes[sz:]
}
r.queued.WriteString(string(runes))
}
func (r *LightRenderer) csi(code string) string {
fullcode := "\x1b[" + code
r.stderr(fullcode)
return fullcode
}
func (r *LightRenderer) flush() {
if r.queued.Len() > 0 {
fmt.Fprint(os.Stderr, "\x1b[?25l"+r.queued.String()+"\x1b[?25h")
r.queued.Reset()
}
}
// Light renderer
type LightRenderer struct {
theme *ColorTheme
mouse bool
forceBlack bool
clearOnExit bool
prevDownTime time.Time
clicks [][2]int
ttyin *os.File
buffer []byte
origState *term.State
width int
height int
yoffset int
tabstop int
escDelay int
fullscreen bool
upOneLine bool
queued strings.Builder
y int
x int
maxHeightFunc func(int) int
// Windows only
ttyinChannel chan byte
inHandle uintptr
outHandle uintptr
origStateInput uint32
origStateOutput uint32
}
type LightWindow struct {
renderer *LightRenderer
colored bool
preview bool
border