123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- package door
- import (
- "log"
- "regexp"
- "strconv"
- "strings"
- )
- var FindANSIColor *regexp.Regexp
- func init() {
- FindANSIColor = regexp.MustCompile("\x1b\\[([0-9;]*)m")
- }
- /*
- find \x1b[0;1;37;40m codes.
- return array of start/end positions in the given string.
- */
- func find_ansicolor(text string) [][]int {
- var color_codes [][]int
- // word, _ := regexp.Compile("\x1b\\[([0-9;]+)m")
- var colors [][]int = FindANSIColor.FindAllStringIndex(text, -1)
- // regexp seems to be ignoring the capture groups.
- // colors := word.FindAllSubmatchIndex([]byte(text), len(text))
- for _, pos := range colors {
- var txt string = text[pos[0]+2 : pos[1]-1]
- if txt == "" {
- txt = "0"
- }
- // log.Printf("Text: [%s]\n", txt)
- var codes []string = strings.Split(txt, ";")
- // log.Printf("Codes: [%#v]\n", codes)
- var code []int = make([]int, len(codes))
- for idx, c := range codes {
- var err error
- code[idx], err = strconv.Atoi(c)
- if err != nil {
- log.Printf("Atoi: %#v [%s]\n", err, c)
- }
- color_codes = append(color_codes, code)
- }
- }
- return color_codes
- }
- type ANSIColorParts struct {
- Bold bool
- Blink bool
- FG int
- BG int
- }
- // Parse an array of codes into their ANSIColorParts.
- func ParseColorArray(colorarray []int) ANSIColorParts {
- var acp ANSIColorParts
- acp.FG = -1
- acp.BG = -1
- for _, c := range colorarray {
- switch c {
- case 0:
- acp.FG = 7
- acp.BG = 0
- case 1:
- acp.Bold = true
- case 5:
- acp.Blink = true
- case 30, 31, 32, 33, 34, 35, 36, 37:
- acp.FG = c - 30
- case 40, 41, 42, 43, 44, 45, 46, 47:
- acp.BG = c - 40
- }
- }
- return acp
- }
- // Update the LastColor with the latest codes.
- func (d *Door) UpdateLastColor(output string, LastColor *[]int) {
- // use the last color information + whatever is in the string to
- // track the last color set
- var updated ANSIColorParts = ParseColorArray(*LastColor)
- var colors [][]int = find_ansicolor(output)
- for _, codes := range colors {
- if codes[0] == 0 {
- updated = ParseColorArray(codes)
- } else {
- newCode := ParseColorArray(codes)
- if newCode.Bold {
- updated.Bold = true
- }
- if newCode.Blink {
- updated.Blink = true
- }
- if (newCode.FG != -1) && (newCode.FG != updated.FG) {
- updated.FG = newCode.FG
- }
- if (newCode.BG != -1) && (newCode.BG != updated.BG) {
- updated.BG = newCode.BG
- }
- }
- }
- *LastColor = make([]int, 1)
- (*LastColor)[0] = 0
- if updated.Blink {
- *LastColor = append(*LastColor, 5)
- }
- if updated.Bold {
- *LastColor = append(*LastColor, 1)
- }
- if updated.FG != -1 {
- *LastColor = append(*LastColor, updated.FG+30)
- }
- if updated.BG != -1 {
- *LastColor = append(*LastColor, updated.BG+40)
- }
- }
- /*
- reading from a closed channel is easy to detect.
- res, ok := <-channel
- ok == false
- writing to a closed channel is a panic.
- Have the Write manage itself.
- */
- // For now, update is SavePos + output + RestorePos.
- // FUTURE: Access the screen struct to "save" Color+Attr+Pos.
- func (d *Door) Update(output string) {
- d.Write(SAVE_POS + output + RESTORE_POS)
- }
- // Write string to client.
- func (d *Door) Write(output string) {
- if output == "" {
- // That was easy.
- return
- }
- /*
- if d.Disconnect() {
- return
- }
- defer func() {
- if err := recover(); err != nil {
- log.Println("Write FAILURE:", err)
- // Display error to user
- // This displays stack trace stderr
- // debug.PrintStack()
- }
- }()
- if d.Disconnect() {
- return
- }
- */
- // DATA RACE. Because WriterClosed is getting changed by
- // the closeChannel ... this isn't synchronized/safe.
- // DATA RACE. If I just close the writerChannel, there's
- // a data race on the channel. (writing to and closing)
- // DATA RACE. Sending a "" to the channel for a "close"
- // signal doesn't work either ... (called from go routine)
- // The channel <- is synced, the access to the bool isn't. :(
- //if !d.WriterClosed {
- // Apparently, when I get to the next line, there's
- // already a data race here...
- /*
- if !d.WriterIsClosed() {
- d.writerChannel <- output
- }
- */
- defer func() {
- if err := recover(); err != nil {
- log.Println("Write failed.", err)
- }
- }()
- //if !d.WriterIsClosed() {
- d.writerChannel <- output
- //}
- /*
- d.writerMutex.Lock()
- defer d.writerMutex.Unlock()
- if d.WriterClosed {
- return
- }
- d.writerChannel <- output
- */
- }
|