github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/mattn/go-colorable/colorable_windows.go (about)

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