...

Source file src/github.com/onsi/ginkgo/v2/formatter/colorable_windows.go

Documentation: github.com/onsi/ginkgo/v2/formatter

     1  /*
     2  These packages are used for colorize on Windows and contributed by mattn.jp@gmail.com
     3  
     4    * go-colorable: <https://github.com/mattn/go-colorable>
     5    * go-isatty: <https://github.com/mattn/go-isatty>
     6  
     7  The MIT License (MIT)
     8  
     9  Copyright (c) 2016 Yasuhiro Matsumoto
    10  
    11  Permission is hereby granted, free of charge, to any person obtaining a copy
    12  of this software and associated documentation files (the "Software"), to deal
    13  in the Software without restriction, including without limitation the rights
    14  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    15  copies of the Software, and to permit persons to whom the Software is
    16  furnished to do so, subject to the following conditions:
    17  
    18  The above copyright notice and this permission notice shall be included in all
    19  copies or substantial portions of the Software.
    20  
    21  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    22  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    23  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    24  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    25  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    26  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    27  SOFTWARE.
    28  */
    29  
    30  package formatter
    31  
    32  import (
    33  	"bytes"
    34  	"fmt"
    35  	"io"
    36  	"math"
    37  	"os"
    38  	"strconv"
    39  	"strings"
    40  	"syscall"
    41  	"unsafe"
    42  )
    43  
    44  var (
    45  	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
    46  	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
    47  	procSetConsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
    48  	procSetConsoleCursorPosition   = kernel32.NewProc("SetConsoleCursorPosition")
    49  	procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
    50  	procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
    51  	procGetConsoleMode             = kernel32.NewProc("GetConsoleMode")
    52  )
    53  
    54  func isTerminal(fd uintptr) bool {
    55  	var st uint32
    56  	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
    57  	return r != 0 && e == 0
    58  }
    59  
    60  const (
    61  	foregroundBlue      = 0x1
    62  	foregroundGreen     = 0x2
    63  	foregroundRed       = 0x4
    64  	foregroundIntensity = 0x8
    65  	foregroundMask      = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
    66  	backgroundBlue      = 0x10
    67  	backgroundGreen     = 0x20
    68  	backgroundRed       = 0x40
    69  	backgroundIntensity = 0x80
    70  	backgroundMask      = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
    71  )
    72  
    73  type wchar uint16
    74  type short int16
    75  type dword uint32
    76  type word uint16
    77  
    78  type coord struct {
    79  	x short
    80  	y short
    81  }
    82  
    83  type smallRect struct {
    84  	left   short
    85  	top    short
    86  	right  short
    87  	bottom short
    88  }
    89  
    90  type consoleScreenBufferInfo struct {
    91  	size              coord
    92  	cursorPosition    coord
    93  	attributes        word
    94  	window            smallRect
    95  	maximumWindowSize coord
    96  }
    97  
    98  type writer struct {
    99  	out     io.Writer
   100  	handle  syscall.Handle
   101  	lastbuf bytes.Buffer
   102  	oldattr word
   103  }
   104  
   105  func newColorable(file *os.File) io.Writer {
   106  	if file == nil {
   107  		panic("nil passed instead of *os.File to NewColorable()")
   108  	}
   109  
   110  	if isTerminal(file.Fd()) {
   111  		var csbi consoleScreenBufferInfo
   112  		handle := syscall.Handle(file.Fd())
   113  		procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
   114  		return &writer{out: file, handle: handle, oldattr: csbi.attributes}
   115  	} else {
   116  		return file
   117  	}
   118  }
   119  
   120  var color256 = map[int]int{
   121  	0:   0x000000,
   122  	1:   0x800000,
   123  	2:   0x008000,
   124  	3:   0x808000,
   125  	4:   0x000080,
   126  	5:   0x800080,
   127  	6:   0x008080,
   128  	7:   0xc0c0c0,
   129  	8:   0x808080,
   130  	9:   0xff0000,
   131  	10:  0x00ff00,
   132  	11:  0xffff00,
   133  	12:  0x0000ff,
   134  	13:  0xff00ff,
   135  	14:  0x00ffff,
   136  	15:  0xffffff,
   137  	16:  0x000000,
   138  	17:  0x00005f,
   139  	18:  0x000087,
   140  	19:  0x0000af,
   141  	20:  0x0000d7,
   142  	21:  0x0000ff,
   143  	22:  0x005f00,
   144  	23:  0x005f5f,
   145  	24:  0x005f87,
   146  	25:  0x005faf,
   147  	26:  0x005fd7,
   148  	27:  0x005fff,
   149  	28:  0x008700,
   150  	29:  0x00875f,
   151  	30:  0x008787,
   152  	31:  0x0087af,
   153  	32:  0x0087d7,
   154  	33:  0x0087ff,
   155  	34:  0x00af00,
   156  	35:  0x00af5f,
   157  	36:  0x00af87,
   158  	37:  0x00afaf,
   159  	38:  0x00afd7,
   160  	39:  0x00afff,
   161  	40:  0x00d700,
   162  	41:  0x00d75f,
   163  	42:  0x00d787,
   164  	43:  0x00d7af,
   165  	44:  0x00d7d7,
   166  	45:  0x00d7ff,
   167  	46:  0x00ff00,
   168  	47:  0x00ff5f,
   169  	48:  0x00ff87,
   170  	49:  0x00ffaf,
   171  	50:  0x00ffd7,
   172  	51:  0x00ffff,
   173  	52:  0x5f0000,
   174  	53:  0x5f005f,
   175  	54:  0x5f0087,
   176  	55:  0x5f00af,
   177  	56:  0x5f00d7,
   178  	57:  0x5f00ff,
   179  	58:  0x5f5f00,
   180  	59:  0x5f5f5f,
   181  	60:  0x5f5f87,
   182  	61:  0x5f5faf,
   183  	62:  0x5f5fd7,
   184  	63:  0x5f5fff,
   185  	64:  0x5f8700,
   186  	65:  0x5f875f,
   187  	66:  0x5f8787,
   188  	67:  0x5f87af,
   189  	68:  0x5f87d7,
   190  	69:  0x5f87ff,
   191  	70:  0x5faf00,
   192  	71:  0x5faf5f,
   193  	72:  0x5faf87,
   194  	73:  0x5fafaf,
   195  	74:  0x5fafd7,
   196  	75:  0x5fafff,
   197  	76:  0x5fd700,
   198  	77:  0x5fd75f,
   199  	78:  0x5fd787,
   200  	79:  0x5fd7af,
   201  	80:  0x5fd7d7,
   202  	81:  0x5fd7ff,
   203  	82:  0x5fff00,
   204  	83:  0x5fff5f,
   205  	84:  0x5fff87,
   206  	85:  0x5fffaf,
   207  	86:  0x5fffd7,
   208  	87:  0x5fffff,
   209  	88:  0x870000,
   210  	89:  0x87005f,
   211  	90:  0x870087,
   212  	91:  0x8700af,
   213  	92:  0x8700d7,
   214  	93:  0x8700ff,
   215  	94:  0x875f00,
   216  	95:  0x875f5f,
   217  	96:  0x875f87,
   218  	97:  0x875faf,
   219  	98:  0x875fd7,
   220  	99:  0x875fff,
   221  	100: 0x878700,
   222  	101: 0x87875f,
   223  	102: 0x878787,
   224  	103: 0x8787af,
   225  	104: 0x8787d7,
   226  	105: 0x8787ff,
   227  	106: 0x87af00,
   228  	107: 0x87af5f,
   229  	108: 0x87af87,
   230  	109: 0x87afaf,
   231  	110: 0x87afd7,
   232  	111: 0x87afff,
   233  	112: 0x87d700,
   234  	113: 0x87d75f,
   235  	114: 0x87d787,
   236  	115: 0x87d7af,
   237  	116: 0x87d7d7,
   238  	117: 0x87d7ff,
   239  	118: 0x87ff00,
   240  	119: 0x87ff5f,
   241  	120: 0x87ff87,
   242  	121: 0x87ffaf,
   243  	122: 0x87ffd7,
   244  	123: 0x87ffff,
   245  	124: 0xaf0000,
   246  	125: 0xaf005f,
   247  	126: 0xaf0087,
   248  	127: 0xaf00af,
   249  	128: 0xaf00d7,
   250  	129: 0xaf00ff,
   251  	130: 0xaf5f00,
   252  	131: 0xaf5f5f,
   253  	132: 0xaf5f87,
   254  	133: 0xaf5faf,
   255  	134: 0xaf5fd7,
   256  	135: 0xaf5fff,
   257  	136: 0xaf8700,
   258  	137: 0xaf875f,
   259  	138: 0xaf8787,
   260  	139: 0xaf87af,
   261  	140: 0xaf87d7,
   262  	141: 0xaf87ff,
   263  	142: 0xafaf00,
   264  	143: 0xafaf5f,
   265  	144: 0xafaf87,
   266  	145: 0xafafaf,
   267  	146: 0xafafd7,
   268  	147: 0xafafff,
   269  	148: 0xafd700,
   270  	149: 0xafd75f,
   271  	150: 0xafd787,
   272  	151: 0xafd7af,
   273  	152: 0xafd7d7,
   274  	153: 0xafd7ff,
   275  	154: 0xafff00,
   276  	155: 0xafff5f,
   277  	156: 0xafff87,
   278  	157: 0xafffaf,
   279  	158: 0xafffd7,
   280  	159: 0xafffff,
   281  	160: 0xd70000,
   282  	161: 0xd7005f,
   283  	162: 0xd70087,
   284  	163: 0xd700af,
   285  	164: 0xd700d7,
   286  	165: 0xd700ff,
   287  	166: 0xd75f00,
   288  	167: 0xd75f5f,
   289  	168: 0xd75f87,
   290  	169: 0xd75faf,
   291  	170: 0xd75fd7,
   292  	171: 0xd75fff,
   293  	172: 0xd78700,
   294  	173: 0xd7875f,
   295  	174: 0xd78787,
   296  	175: 0xd787af,
   297  	176: 0xd787d7,
   298  	177: 0xd787ff,
   299  	178: 0xd7af00,
   300  	179: 0xd7af5f,
   301  	180: 0xd7af87,
   302  	181: 0xd7afaf,
   303  	182: 0xd7afd7,
   304  	183: 0xd7afff,
   305  	184: 0xd7d700,
   306  	185: 0xd7d75f,
   307  	186: 0xd7d787,
   308  	187: 0xd7d7af,
   309  	188: 0xd7d7d7,
   310  	189: 0xd7d7ff,
   311  	190: 0xd7ff00,
   312  	191: 0xd7ff5f,
   313  	192: 0xd7ff87,
   314  	193: 0xd7ffaf,
   315  	194: 0xd7ffd7,
   316  	195: 0xd7ffff,
   317  	196: 0xff0000,
   318  	197: 0xff005f,
   319  	198: 0xff0087,
   320  	199: 0xff00af,
   321  	200: 0xff00d7,
   322  	201: 0xff00ff,
   323  	202: 0xff5f00,
   324  	203: 0xff5f5f,
   325  	204: 0xff5f87,
   326  	205: 0xff5faf,
   327  	206: 0xff5fd7,
   328  	207: 0xff5fff,
   329  	208: 0xff8700,
   330  	209: 0xff875f,
   331  	210: 0xff8787,
   332  	211: 0xff87af,
   333  	212: 0xff87d7,
   334  	213: 0xff87ff,
   335  	214: 0xffaf00,
   336  	215: 0xffaf5f,
   337  	216: 0xffaf87,
   338  	217: 0xffafaf,
   339  	218: 0xffafd7,
   340  	219: 0xffafff,
   341  	220: 0xffd700,
   342  	221: 0xffd75f,
   343  	222: 0xffd787,
   344  	223: 0xffd7af,
   345  	224: 0xffd7d7,
   346  	225: 0xffd7ff,
   347  	226: 0xffff00,
   348  	227: 0xffff5f,
   349  	228: 0xffff87,
   350  	229: 0xffffaf,
   351  	230: 0xffffd7,
   352  	231: 0xffffff,
   353  	232: 0x080808,
   354  	233: 0x121212,
   355  	234: 0x1c1c1c,
   356  	235: 0x262626,
   357  	236: 0x303030,
   358  	237: 0x3a3a3a,
   359  	238: 0x444444,
   360  	239: 0x4e4e4e,
   361  	240: 0x585858,
   362  	241: 0x626262,
   363  	242: 0x6c6c6c,
   364  	243: 0x767676,
   365  	244: 0x808080,
   366  	245: 0x8a8a8a,
   367  	246: 0x949494,
   368  	247: 0x9e9e9e,
   369  	248: 0xa8a8a8,
   370  	249: 0xb2b2b2,
   371  	250: 0xbcbcbc,
   372  	251: 0xc6c6c6,
   373  	252: 0xd0d0d0,
   374  	253: 0xdadada,
   375  	254: 0xe4e4e4,
   376  	255: 0xeeeeee,
   377  }
   378  
   379  func (w *writer) Write(data []byte) (n int, err error) {
   380  	var csbi consoleScreenBufferInfo
   381  	procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
   382  
   383  	er := bytes.NewBuffer(data)
   384  loop:
   385  	for {
   386  		r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
   387  		if r1 == 0 {
   388  			break loop
   389  		}
   390  
   391  		c1, _, err := er.ReadRune()
   392  		if err != nil {
   393  			break loop
   394  		}
   395  		if c1 != 0x1b {
   396  			fmt.Fprint(w.out, string(c1))
   397  			continue
   398  		}
   399  		c2, _, err := er.ReadRune()
   400  		if err != nil {
   401  			w.lastbuf.WriteRune(c1)
   402  			break loop
   403  		}
   404  		if c2 != 0x5b {
   405  			w.lastbuf.WriteRune(c1)
   406  			w.lastbuf.WriteRune(c2)
   407  			continue
   408  		}
   409  
   410  		var buf bytes.Buffer
   411  		var m rune
   412  		for {
   413  			c, _, err := er.ReadRune()
   414  			if err != nil {
   415  				w.lastbuf.WriteRune(c1)
   416  				w.lastbuf.WriteRune(c2)
   417  				w.lastbuf.Write(buf.Bytes())
   418  				break loop
   419  			}
   420  			if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
   421  				m = c
   422  				break
   423  			}
   424  			buf.Write([]byte(string(c)))
   425  		}
   426  
   427  		var csbi consoleScreenBufferInfo
   428  		switch m {
   429  		case 'A':
   430  			n, err = strconv.Atoi(buf.String())
   431  			if err != nil {
   432  				continue
   433  			}
   434  			procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
   435  			csbi.cursorPosition.y -= short(n)
   436  			procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
   437  		case 'B':
   438  			n, err = strconv.Atoi(buf.String())
   439  			if err != nil {
   440  				continue
   441  			}
   442  			procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
   443  			csbi.cursorPosition.y += short(n)
   444  			procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
   445  		case 'C':
   446  			n, err = strconv.Atoi(buf.String())
   447  			if err != nil {
   448  				continue
   449  			}
   450  			procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
   451  			csbi.cursorPosition.x -= short(n)
   452  			procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
   453  		case 'D':
   454  			n, err = strconv.Atoi(buf.String())
   455  			if err != nil {
   456  				continue
   457  			}
   458  			if n, err = strconv.Atoi(buf.String()); err == nil {
   459  				var csbi consoleScreenBufferInfo
   460  				procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
   461  				csbi.cursorPosition.x += short(n)
   462  				procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
   463  			}
   464  		case 'E':
   465  			n, err = strconv.Atoi(buf.String())
   466  			if err != nil {
   467  				continue
   468  			}
   469  			procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
   470  			csbi.cursorPosition.x = 0
   471  			csbi.cursorPosition.y += short(n)
   472  			procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
   473  		case 'F':
   474  			n, err = strconv.Atoi(buf.String())
   475  			if err != nil {
   476  				continue
   477  			}
   478  			procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
   479  			csbi.cursorPosition.x = 0
   480  			csbi.cursorPosition.y -= short(n)
   481  			procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
   482  		case 'G':
   483  			n, err = strconv.Atoi(buf.String())
   484  			if err != nil {
   485  				continue
   486  			}
   487  			procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
   488  			csbi.cursorPosition.x = short(n)
   489  			procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
   490  		case 'H':
   491  			token := strings.Split(buf.String(), ";")
   492  			if len(token) != 2 {
   493  				continue
   494  			}
   495  			n1, err := strconv.Atoi(token[0])
   496  			if err != nil {
   497  				continue
   498  			}
   499  			n2, err := strconv.Atoi(token[1])
   500  			if err != nil {
   501  				continue
   502  			}
   503  			csbi.cursorPosition.x = short(n2)
   504  			csbi.cursorPosition.x = short(n1)
   505  			procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
   506  		case 'J':
   507  			n, err := strconv.Atoi(buf.String())
   508  			if err != nil {
   509  				continue
   510  			}
   511  			var cursor coord
   512  			switch n {
   513  			case 0:
   514  				cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
   515  			case 1:
   516  				cursor = coord{x: csbi.window.left, y: csbi.window.top}
   517  			case 2:
   518  				cursor = coord{x: csbi.window.left, y: csbi.window.top}
   519  			}
   520  			var count, written dword
   521  			count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
   522  			procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
   523  			procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
   524  		case 'K':
   525  			n, err := strconv.Atoi(buf.String())
   526  			if err != nil {
   527  				continue
   528  			}
   529  			var cursor coord
   530  			switch n {
   531  			case 0:
   532  				cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
   533  			case 1:
   534  				cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
   535  			case 2:
   536  				cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
   537  			}
   538  			var count, written dword
   539  			count = dword(csbi.size.x - csbi.cursorPosition.x)
   540  			procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
   541  			procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
   542  		case 'm':
   543  			attr := csbi.attributes
   544  			cs := buf.String()
   545  			if cs == "" {
   546  				procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
   547  				continue
   548  			}
   549  			token := strings.Split(cs, ";")
   550  			for i := 0; i < len(token); i += 1 {
   551  				ns := token[i]
   552  				if n, err = strconv.Atoi(ns); err == nil {
   553  					switch {
   554  					case n == 0 || n == 100:
   555  						attr = w.oldattr
   556  					case 1 <= n && n <= 5:
   557  						attr |= foregroundIntensity
   558  					case n == 7:
   559  						attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
   560  					case 22 == n || n == 25 || n == 25:
   561  						attr |= foregroundIntensity
   562  					case n == 27:
   563  						attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
   564  					case 30 <= n && n <= 37:
   565  						attr = (attr & backgroundMask)
   566  						if (n-30)&1 != 0 {
   567  							attr |= foregroundRed
   568  						}
   569  						if (n-30)&2 != 0 {
   570  							attr |= foregroundGreen
   571  						}
   572  						if (n-30)&4 != 0 {
   573  							attr |= foregroundBlue
   574  						}
   575  					case n == 38: // set foreground color.
   576  						if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") {
   577  							if n256, err := strconv.Atoi(token[i+2]); err == nil {
   578  								if n256foreAttr == nil {
   579  									n256setup()
   580  								}
   581  								attr &= backgroundMask
   582  								attr |= n256foreAttr[n256]
   583  								i += 2
   584  							}
   585  						} else {
   586  							attr = attr & (w.oldattr & backgroundMask)
   587  						}
   588  					case n == 39: // reset foreground color.
   589  						attr &= backgroundMask
   590  						attr |= w.oldattr & foregroundMask
   591  					case 40 <= n && n <= 47:
   592  						attr = (attr & foregroundMask)
   593  						if (n-40)&1 != 0 {
   594  							attr |= backgroundRed
   595  						}
   596  						if (n-40)&2 != 0 {
   597  							attr |= backgroundGreen
   598  						}
   599  						if (n-40)&4 != 0 {
   600  							attr |= backgroundBlue
   601  						}
   602  					case n == 48: // set background color.
   603  						if i < len(token)-2 && token[i+1] == "5" {
   604  							if n256, err := strconv.Atoi(token[i+2]); err == nil {
   605  								if n256backAttr == nil {
   606  									n256setup()
   607  								}
   608  								attr &= foregroundMask
   609  								attr |= n256backAttr[n256]
   610  								i += 2
   611  							}
   612  						} else {
   613  							attr = attr & (w.oldattr & foregroundMask)
   614  						}
   615  					case n == 49: // reset foreground color.
   616  						attr &= foregroundMask
   617  						attr |= w.oldattr & backgroundMask
   618  					case 90 <= n && n <= 97:
   619  						attr = (attr & backgroundMask)
   620  						attr |= foregroundIntensity
   621  						if (n-90)&1 != 0 {
   622  							attr |= foregroundRed
   623  						}
   624  						if (n-90)&2 != 0 {
   625  							attr |= foregroundGreen
   626  						}
   627  						if (n-90)&4 != 0 {
   628  							attr |= foregroundBlue
   629  						}
   630  					case 100 <= n && n <= 107:
   631  						attr = (attr & foregroundMask)
   632  						attr |= backgroundIntensity
   633  						if (n-100)&1 != 0 {
   634  							attr |= backgroundRed
   635  						}
   636  						if (n-100)&2 != 0 {
   637  							attr |= backgroundGreen
   638  						}
   639  						if (n-100)&4 != 0 {
   640  							attr |= backgroundBlue
   641  						}
   642  					}
   643  					procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
   644  				}
   645  			}
   646  		}
   647  	}
   648  	return len(data) - w.lastbuf.Len(), nil
   649  }
   650  
   651  type consoleColor struct {
   652  	rgb       int
   653  	red       bool
   654  	green     bool
   655  	blue      bool
   656  	intensity bool
   657  }
   658  
   659  func (c consoleColor) foregroundAttr() (attr word) {
   660  	if c.red {
   661  		attr |= foregroundRed
   662  	}
   663  	if c.green {
   664  		attr |= foregroundGreen
   665  	}
   666  	if c.blue {
   667  		attr |= foregroundBlue
   668  	}
   669  	if c.intensity {
   670  		attr |= foregroundIntensity
   671  	}
   672  	return
   673  }
   674  
   675  func (c consoleColor) backgroundAttr() (attr word) {
   676  	if c.red {
   677  		attr |= backgroundRed
   678  	}
   679  	if c.green {
   680  		attr |= backgroundGreen
   681  	}
   682  	if c.blue {
   683  		attr |= backgroundBlue
   684  	}
   685  	if c.intensity {
   686  		attr |= backgroundIntensity
   687  	}
   688  	return
   689  }
   690  
   691  var color16 = []consoleColor{
   692  	consoleColor{0x000000, false, false, false, false},
   693  	consoleColor{0x000080, false, false, true, false},
   694  	consoleColor{0x008000, false, true, false, false},
   695  	consoleColor{0x008080, false, true, true, false},
   696  	consoleColor{0x800000, true, false, false, false},
   697  	consoleColor{0x800080, true, false, true, false},
   698  	consoleColor{0x808000, true, true, false, false},
   699  	consoleColor{0xc0c0c0, true, true, true, false},
   700  	consoleColor{0x808080, false, false, false, true},
   701  	consoleColor{0x0000ff, false, false, true, true},
   702  	consoleColor{0x00ff00, false, true, false, true},
   703  	consoleColor{0x00ffff, false, true, true, true},
   704  	consoleColor{0xff0000, true, false, false, true},
   705  	consoleColor{0xff00ff, true, false, true, true},
   706  	consoleColor{0xffff00, true, true, false, true},
   707  	consoleColor{0xffffff, true, true, true, true},
   708  }
   709  
   710  type hsv struct {
   711  	h, s, v float32
   712  }
   713  
   714  func (a hsv) dist(b hsv) float32 {
   715  	dh := a.h - b.h
   716  	switch {
   717  	case dh > 0.5:
   718  		dh = 1 - dh
   719  	case dh < -0.5:
   720  		dh = -1 - dh
   721  	}
   722  	ds := a.s - b.s
   723  	dv := a.v - b.v
   724  	return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
   725  }
   726  
   727  func toHSV(rgb int) hsv {
   728  	r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
   729  		float32((rgb&0x00FF00)>>8)/256.0,
   730  		float32(rgb&0x0000FF)/256.0
   731  	min, max := minmax3f(r, g, b)
   732  	h := max - min
   733  	if h > 0 {
   734  		if max == r {
   735  			h = (g - b) / h
   736  			if h < 0 {
   737  				h += 6
   738  			}
   739  		} else if max == g {
   740  			h = 2 + (b-r)/h
   741  		} else {
   742  			h = 4 + (r-g)/h
   743  		}
   744  	}
   745  	h /= 6.0
   746  	s := max - min
   747  	if max != 0 {
   748  		s /= max
   749  	}
   750  	v := max
   751  	return hsv{h: h, s: s, v: v}
   752  }
   753  
   754  type hsvTable []hsv
   755  
   756  func toHSVTable(rgbTable []consoleColor) hsvTable {
   757  	t := make(hsvTable, len(rgbTable))
   758  	for i, c := range rgbTable {
   759  		t[i] = toHSV(c.rgb)
   760  	}
   761  	return t
   762  }
   763  
   764  func (t hsvTable) find(rgb int) consoleColor {
   765  	hsv := toHSV(rgb)
   766  	n := 7
   767  	l := float32(5.0)
   768  	for i, p := range t {
   769  		d := hsv.dist(p)
   770  		if d < l {
   771  			l, n = d, i
   772  		}
   773  	}
   774  	return color16[n]
   775  }
   776  
   777  func minmax3f(a, b, c float32) (min, max float32) {
   778  	if a < b {
   779  		if b < c {
   780  			return a, c
   781  		} else if a < c {
   782  			return a, b
   783  		} else {
   784  			return c, b
   785  		}
   786  	} else {
   787  		if a < c {
   788  			return b, c
   789  		} else if b < c {
   790  			return b, a
   791  		} else {
   792  			return c, a
   793  		}
   794  	}
   795  }
   796  
   797  var n256foreAttr []word
   798  var n256backAttr []word
   799  
   800  func n256setup() {
   801  	n256foreAttr = make([]word, 256)
   802  	n256backAttr = make([]word, 256)
   803  	t := toHSVTable(color16)
   804  	for i, rgb := range color256 {
   805  		c := t.find(rgb)
   806  		n256foreAttr[i] = c.foregroundAttr()
   807  		n256backAttr[i] = c.backgroundAttr()
   808  	}
   809  }
   810  

View as plain text