diff --git a/README.md b/README.md index cd9a7cb..9ff9349 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ Ensure you have your latest graphics card drivers installed before use. |-----------------------------|------|-------| | Pty allocation | ✔ | Needs work for OSX + Windows | OpenGL rendering | ✔ | +| 8-bit (256) colour | ✔ | +| 24-bit (true) colour | ✔ | | Resizing/content reordering | ⏳ | | ANSI escape codes | ⏳ | Most of these are handled now | UTF-8 input | ✔ | diff --git a/demo/256.sh b/demo/256.sh new file mode 100755 index 0000000..43e485e --- /dev/null +++ b/demo/256.sh @@ -0,0 +1,12 @@ + +#!/bin/bash + +for i in {0..255} ; do + printf "\x1b[48;5;%sm%3d\e[0m " "$i" "$i" + if (( i == 15 )) || (( i > 15 )) && (( (i-15) % 6 == 0 )); then + printf "\n"; + fi + done + + + diff --git a/demo/true.sh b/demo/true.sh new file mode 100755 index 0000000..92d9e45 --- /dev/null +++ b/demo/true.sh @@ -0,0 +1,19 @@ + +#!/bin/bash + +awk -v term_cols="${width:-$(tput cols || echo 80)}" 'BEGIN{ + + + s=" "; + for (colnum = 0; colnum255) g = 510-g; + printf "\033[48;2;%d;%d;%dm", r,g,b; + printf "\033[38;2;%d;%d;%dm", 255-r,255-g,255-b; + printf "%s\033[0m", substr(s,colnum%2+1,1); + } + printf "\n"; + + }' diff --git a/terminal/csi.go b/terminal/csi.go index 2ea40b2..96fa116 100644 --- a/terminal/csi.go +++ b/terminal/csi.go @@ -52,8 +52,7 @@ func csiInsertLinesHandler(params []string, intermediate string, terminal *Termi } } terminal.logger.Debugf("Inserting %d lines", count) - panic("Not supported") - return nil + return fmt.Errorf("Not supported") } func csiScrollDownHandler(params []string, intermediate string, terminal *Terminal) error { diff --git a/terminal/sgr.go b/terminal/sgr.go index e438062..7e08c66 100644 --- a/terminal/sgr.go +++ b/terminal/sgr.go @@ -2,15 +2,20 @@ package terminal import ( "fmt" + "strconv" "github.com/liamg/aminal/buffer" + "github.com/liamg/aminal/config" ) func sgrSequenceHandler(params []string, intermediate string, terminal *Terminal) error { + if len(params) == 0 { + return nil + } + for i := range params { - param := params[i] - switch param { + switch params[i] { case "00", "0", "": attr := terminal.ActiveBuffer().CursorAttr() *attr = buffer.CellAttributes{ @@ -109,11 +114,143 @@ func sgrSequenceHandler(params []string, intermediate string, terminal *Terminal terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.LightCyan case "107": terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.White + case "38": // set foreground + c, err := terminal.getANSIColour(params[i:]) + if err != nil { + return err + } + terminal.ActiveBuffer().CursorAttr().FgColour = c + return nil + case "48": // set background + c, err := terminal.getANSIColour(params[i:]) + if err != nil { + return err + } + terminal.ActiveBuffer().CursorAttr().BgColour = c + return nil default: - return fmt.Errorf("Unknown SGR control sequence: (ESC[%s%sm)", param, intermediate) + return fmt.Errorf("Unknown SGR control sequence: (ESC[%s%sm)", params[i:], intermediate) } - - //terminal.logger.Debugf("SGR control sequence: (ESC[%s%sm)", param, intermediate) } + + //terminal.logger.Debugf("SGR control sequence: (ESC[%s%sm)", param, intermediate) + return nil } + +func (terminal *Terminal) getANSIColour(params []string) (config.Colour, error) { + + if len(params) > 2 { + switch params[1] { + case "5": + // 8 bit colour + colNum, err := strconv.Atoi(params[2]) + + if err != nil || colNum >= 256 || colNum < 0 { + return [3]float32{0, 0, 0}, fmt.Errorf("Invalid 8-bit colour specifier") + } + return terminal.get8BitSGRColour(uint8(colNum)), nil + + case "2": + if len(params) < 4 { + return [3]float32{0, 0, 0}, fmt.Errorf("Invalid true colour specifier") + } + // 24 bit colour + if len(params) == 5 { // standard true colour + + r, err := strconv.Atoi(params[2]) + if err != nil { + return [3]float32{0, 0, 0}, fmt.Errorf("Invalid true colour specifier") + } + g, err := strconv.Atoi(params[3]) + if err != nil { + return [3]float32{0, 0, 0}, fmt.Errorf("Invalid true colour specifier") + } + b, err := strconv.Atoi(params[4]) + if err != nil { + return [3]float32{0, 0, 0}, fmt.Errorf("Invalid true colour specifier") + } + return [3]float32{ + float32(r) / 0xff, + float32(g) / 0xff, + float32(b) / 0xff, + }, nil + } else if len(params) > 5 { // ISO/IEC International Standard 8613-6 + r, err := strconv.Atoi(params[3]) + if err != nil { + return [3]float32{0, 0, 0}, fmt.Errorf("Invalid true colour specifier") + } + g, err := strconv.Atoi(params[4]) + if err != nil { + return [3]float32{0, 0, 0}, fmt.Errorf("Invalid true colour specifier") + } + b, err := strconv.Atoi(params[5]) + if err != nil { + return [3]float32{0, 0, 0}, fmt.Errorf("Invalid true colour specifier") + } + return [3]float32{ + float32(r) / 0xff, + float32(g) / 0xff, + float32(b) / 0xff, + }, nil + } + } + } + + return [3]float32{}, fmt.Errorf("Unknown ANSI colour format identifier") + +} + +func (terminal *Terminal) get8BitSGRColour(colNum uint8) [3]float32 { + + // https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + + switch colNum { + case 0: + return terminal.config.ColourScheme.Black + case 1: + return terminal.config.ColourScheme.Red + case 2: + return terminal.config.ColourScheme.Green + case 3: + return terminal.config.ColourScheme.Yellow + case 4: + return terminal.config.ColourScheme.Blue + case 5: + return terminal.config.ColourScheme.Magenta + case 6: + return terminal.config.ColourScheme.Cyan + case 7: + return terminal.config.ColourScheme.White + case 8: + return terminal.config.ColourScheme.DarkGrey + case 9: + return terminal.config.ColourScheme.LightRed + case 10: + return terminal.config.ColourScheme.LightGreen + case 11: + return terminal.config.ColourScheme.LightYellow + case 12: + return terminal.config.ColourScheme.LightBlue + case 13: + return terminal.config.ColourScheme.LightMagenta + case 14: + return terminal.config.ColourScheme.LightCyan + case 15: + return terminal.config.ColourScheme.White + } + + if colNum < 232 { + + index := int(colNum - 16) // 0-216 + rgb := (index * 0xffffff) / 216 + r := float32((rgb&0xff0000)>>16) / 0xff + g := float32((rgb&0xff00)>>8) / 0xff + b := float32(rgb&0xff) / 0xff + + return [3]float32{r, g, b} + } + + c := float32(colNum-232) / 0x18 + return [3]float32{c, c, c} +}