readline/ansi_windows.go

262 lines
4.9 KiB
Go
Raw Permalink Normal View History

2015-09-29 12:49:58 +03:00
// +build windows
package readline
import (
"bufio"
2015-09-29 12:49:58 +03:00
"io"
"strconv"
"strings"
"sync"
"unicode/utf8"
"unsafe"
)
2015-09-29 18:28:12 +03:00
const (
_ = uint16(0)
COLOR_FBLUE = 0x0001
COLOR_FGREEN = 0x0002
COLOR_FRED = 0x0004
COLOR_FINTENSITY = 0x0008
COLOR_BBLUE = 0x0010
COLOR_BGREEN = 0x0020
COLOR_BRED = 0x0040
COLOR_BINTENSITY = 0x0080
COMMON_LVB_UNDERSCORE = 0x8000
)
var ColorTableFg = []word{
0, // 30: Black
COLOR_FRED, // 31: Red
COLOR_FGREEN, // 32: Green
COLOR_FRED | COLOR_FGREEN, // 33: Yellow
COLOR_FBLUE, // 34: Blue
COLOR_FRED | COLOR_FBLUE, // 35: Magenta
COLOR_FGREEN | COLOR_FBLUE, // 36: Cyan
COLOR_FRED | COLOR_FBLUE | COLOR_FGREEN, // 37: White
}
var ColorTableBg = []word{
0, // 40: Black
COLOR_BRED, // 41: Red
COLOR_BGREEN, // 42: Green
COLOR_BRED | COLOR_BGREEN, // 43: Yellow
COLOR_BBLUE, // 44: Blue
COLOR_BRED | COLOR_BBLUE, // 45: Magenta
COLOR_BGREEN | COLOR_BBLUE, // 46: Cyan
COLOR_BRED | COLOR_BBLUE | COLOR_BGREEN, // 47: White
2015-09-29 12:49:58 +03:00
}
type ANSIWriter struct {
target io.Writer
ch chan rune
wg sync.WaitGroup
sync.Mutex
}
func NewANSIWriter(w io.Writer) *ANSIWriter {
a := &ANSIWriter{
target: w,
ch: make(chan rune),
2015-09-29 12:49:58 +03:00
}
go a.ioloop()
return a
}
func (a *ANSIWriter) Close() error {
close(a.ch)
a.wg.Wait()
return nil
}
func (a *ANSIWriter) ioloop() {
a.wg.Add(1)
defer a.wg.Done()
var (
ok bool
isEsc bool
isEscSeq bool
char rune
arg []string
target = bufio.NewWriter(a.target)
)
peek := func() rune {
select {
case ch := <-a.ch:
return ch
default:
return 0
}
}
read:
r := char
if char == 0 {
r, ok = <-a.ch
if !ok {
target.Flush()
return
}
} else {
char = 0
}
if isEscSeq {
isEscSeq = a.ioloopEscSeq(target, r, &arg)
goto read
}
switch r {
case CharEsc:
isEsc = true
case '[':
if isEsc {
arg = nil
isEscSeq = true
isEsc = false
break
}
fallthrough
default:
target.WriteRune(r)
char = peek()
if char == 0 || char == CharEsc {
target.Flush()
}
}
goto read
}
func (a *ANSIWriter) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) bool {
arg := *argptr
var err error
if r >= 'A' && r <= 'D' {
count := short(GetInt(arg, 1))
info, err := GetConsoleScreenBufferInfo()
if err != nil {
return false
}
switch r {
case 'A': // up
info.dwCursorPosition.y -= count
case 'B': // down
info.dwCursorPosition.y += count
case 'C': // right
info.dwCursorPosition.x += count
case 'D': // left
info.dwCursorPosition.x -= count
}
SetConsoleCursorPosition(&info.dwCursorPosition)
return false
}
2015-09-29 12:49:58 +03:00
switch r {
case 'J':
killLines()
2015-09-29 12:49:58 +03:00
case 'K':
eraseLine()
case 'm':
color := word(0)
for _, item := range arg {
var c int
c, err = strconv.Atoi(item)
if err != nil {
w.WriteString("[" + strings.Join(arg, ";") + "m")
break
}
if c >= 30 && c < 40 {
2015-09-29 18:28:12 +03:00
color ^= COLOR_FINTENSITY
2015-09-29 12:49:58 +03:00
color |= ColorTableFg[c-30]
} else if c >= 40 && c < 50 {
2015-09-29 18:28:12 +03:00
color ^= COLOR_BINTENSITY
color |= ColorTableBg[c-40]
} else if c == 4 {
2015-09-29 18:28:12 +03:00
color |= COMMON_LVB_UNDERSCORE | ColorTableFg[7]
} else { // unknown code treat as reset
2015-09-29 12:49:58 +03:00
color = ColorTableFg[7]
}
}
if err != nil {
break
}
kernel.SetConsoleTextAttribute(stdout, uintptr(color))
case '\007': // set title
2015-09-29 12:49:58 +03:00
case ';':
if len(arg) == 0 || arg[len(arg)-1] != "" {
arg = append(arg, "")
*argptr = arg
2015-09-29 12:49:58 +03:00
}
return true
2015-09-29 12:49:58 +03:00
default:
if len(arg) == 0 {
arg = append(arg, "")
}
arg[len(arg)-1] += string(r)
*argptr = arg
return true
}
*argptr = nil
return false
}
func (a *ANSIWriter) Write(b []byte) (int, error) {
a.Lock()
defer a.Unlock()
off := 0
for len(b) > off {
r, size := utf8.DecodeRune(b[off:])
if size == 0 {
return off, io.ErrShortWrite
}
off += size
a.ch <- r
}
return off, nil
}
func killLines() error {
sbi, err := GetConsoleScreenBufferInfo()
if err != nil {
return err
}
size := (sbi.dwCursorPosition.y - sbi.dwSize.y) * sbi.dwSize.x
size += sbi.dwCursorPosition.x
var written int
kernel.FillConsoleOutputAttribute(stdout, uintptr(ColorTableFg[7]),
uintptr(size),
sbi.dwCursorPosition.ptr(),
uintptr(unsafe.Pointer(&written)),
)
return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
uintptr(size),
sbi.dwCursorPosition.ptr(),
uintptr(unsafe.Pointer(&written)),
)
}
2015-09-29 12:49:58 +03:00
func eraseLine() error {
sbi, err := GetConsoleScreenBufferInfo()
if err != nil {
return err
}
size := sbi.dwSize.x
sbi.dwCursorPosition.x = 0
2015-09-29 12:49:58 +03:00
var written int
return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
uintptr(size),
2015-09-29 12:49:58 +03:00
sbi.dwCursorPosition.ptr(),
uintptr(unsafe.Pointer(&written)),
)
}