1// +build windows 2// +build !appengine 3 4package colorable 5 6import ( 7 "bytes" 8 "io" 9 "math" 10 "os" 11 "strconv" 12 "strings" 13 "syscall" 14 "unsafe" 15 16 "github.com/mattn/go-isatty" 17) 18 19const ( 20 foregroundBlue = 0x1 21 foregroundGreen = 0x2 22 foregroundRed = 0x4 23 foregroundIntensity = 0x8 24 foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) 25 backgroundBlue = 0x10 26 backgroundGreen = 0x20 27 backgroundRed = 0x40 28 backgroundIntensity = 0x80 29 backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) 30 31 cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 32) 33 34const ( 35 genericRead = 0x80000000 36 genericWrite = 0x40000000 37) 38 39const ( 40 consoleTextmodeBuffer = 0x1 41) 42 43type wchar uint16 44type short int16 45type dword uint32 46type word uint16 47 48type coord struct { 49 x short 50 y short 51} 52 53type smallRect struct { 54 left short 55 top short 56 right short 57 bottom short 58} 59 60type consoleScreenBufferInfo struct { 61 size coord 62 cursorPosition coord 63 attributes word 64 window smallRect 65 maximumWindowSize coord 66} 67 68type consoleCursorInfo struct { 69 size dword 70 visible int32 71} 72 73var ( 74 kernel32 = syscall.NewLazyDLL("kernel32.dll") 75 procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 76 procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") 77 procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") 78 procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") 79 procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") 80 procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") 81 procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") 82 procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW") 83 procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 84 procSetConsoleMode = kernel32.NewProc("SetConsoleMode") 85 procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") 86) 87 88// Writer provides colorable Writer to the console 89type Writer struct { 90 out io.Writer 91 handle syscall.Handle 92 althandle syscall.Handle 93 oldattr word 94 oldpos coord 95 rest bytes.Buffer 96} 97 98// NewColorable returns new instance of Writer which handles escape sequence from File. 99func NewColorable(file *os.File) io.Writer { 100 if file == nil { 101 panic("nil passed instead of *os.File to NewColorable()") 102 } 103 104 if isatty.IsTerminal(file.Fd()) { 105 var mode uint32 106 if r, _, _ := procGetConsoleMode.Call(file.Fd(), uintptr(unsafe.Pointer(&mode))); r != 0 && mode&cENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 { 107 return file 108 } 109 var csbi consoleScreenBufferInfo 110 handle := syscall.Handle(file.Fd()) 111 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 112 return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} 113 } 114 return file 115} 116 117// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. 118func NewColorableStdout() io.Writer { 119 return NewColorable(os.Stdout) 120} 121 122// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. 123func NewColorableStderr() io.Writer { 124 return NewColorable(os.Stderr) 125} 126 127var color256 = map[int]int{ 128 0: 0x000000, 129 1: 0x800000, 130 2: 0x008000, 131 3: 0x808000, 132 4: 0x000080, 133 5: 0x800080, 134 6: 0x008080, 135 7: 0xc0c0c0, 136 8: 0x808080, 137 9: 0xff0000, 138 10: 0x00ff00, 139 11: 0xffff00, 140 12: 0x0000ff, 141 13: 0xff00ff, 142 14: 0x00ffff, 143 15: 0xffffff, 144 16: 0x000000, 145 17: 0x00005f, 146 18: 0x000087, 147 19: 0x0000af, 148 20: 0x0000d7, 149 21: 0x0000ff, 150 22: 0x005f00, 151 23: 0x005f5f, 152 24: 0x005f87, 153 25: 0x005faf, 154 26: 0x005fd7, 155 27: 0x005fff, 156 28: 0x008700, 157 29: 0x00875f, 158 30: 0x008787, 159 31: 0x0087af, 160 32: 0x0087d7, 161 33: 0x0087ff, 162 34: 0x00af00, 163 35: 0x00af5f, 164 36: 0x00af87, 165 37: 0x00afaf, 166 38: 0x00afd7, 167 39: 0x00afff, 168 40: 0x00d700, 169 41: 0x00d75f, 170 42: 0x00d787, 171 43: 0x00d7af, 172 44: 0x00d7d7, 173 45: 0x00d7ff, 174 46: 0x00ff00, 175 47: 0x00ff5f, 176 48: 0x00ff87, 177 49: 0x00ffaf, 178 50: 0x00ffd7, 179 51: 0x00ffff, 180 52: 0x5f0000, 181 53: 0x5f005f, 182 54: 0x5f0087, 183 55: 0x5f00af, 184 56: 0x5f00d7, 185 57: 0x5f00ff, 186 58: 0x5f5f00, 187 59: 0x5f5f5f, 188 60: 0x5f5f87, 189 61: 0x5f5faf, 190 62: 0x5f5fd7, 191 63: 0x5f5fff, 192 64: 0x5f8700, 193 65: 0x5f875f, 194 66: 0x5f8787, 195 67: 0x5f87af, 196 68: 0x5f87d7, 197 69: 0x5f87ff, 198 70: 0x5faf00, 199 71: 0x5faf5f, 200 72: 0x5faf87, 201 73: 0x5fafaf, 202 74: 0x5fafd7, 203 75: 0x5fafff, 204 76: 0x5fd700, 205 77: 0x5fd75f, 206 78: 0x5fd787, 207 79: 0x5fd7af, 208 80: 0x5fd7d7, 209 81: 0x5fd7ff, 210 82: 0x5fff00, 211 83: 0x5fff5f, 212 84: 0x5fff87, 213 85: 0x5fffaf, 214 86: 0x5fffd7, 215 87: 0x5fffff, 216 88: 0x870000, 217 89: 0x87005f, 218 90: 0x870087, 219 91: 0x8700af, 220 92: 0x8700d7, 221 93: 0x8700ff, 222 94: 0x875f00, 223 95: 0x875f5f, 224 96: 0x875f87, 225 97: 0x875faf, 226 98: 0x875fd7, 227 99: 0x875fff, 228 100: 0x878700, 229 101: 0x87875f, 230 102: 0x878787, 231 103: 0x8787af, 232 104: 0x8787d7, 233 105: 0x8787ff, 234 106: 0x87af00, 235 107: 0x87af5f, 236 108: 0x87af87, 237 109: 0x87afaf, 238 110: 0x87afd7, 239 111: 0x87afff, 240 112: 0x87d700, 241 113: 0x87d75f, 242 114: 0x87d787, 243 115: 0x87d7af, 244 116: 0x87d7d7, 245 117: 0x87d7ff, 246 118: 0x87ff00, 247 119: 0x87ff5f, 248 120: 0x87ff87, 249 121: 0x87ffaf, 250 122: 0x87ffd7, 251 123: 0x87ffff, 252 124: 0xaf0000, 253 125: 0xaf005f, 254 126: 0xaf0087, 255 127: 0xaf00af, 256 128: 0xaf00d7, 257 129: 0xaf00ff, 258 130: 0xaf5f00, 259 131: 0xaf5f5f, 260 132: 0xaf5f87, 261 133: 0xaf5faf, 262 134: 0xaf5fd7, 263 135: 0xaf5fff, 264 136: 0xaf8700, 265 137: 0xaf875f, 266 138: 0xaf8787, 267 139: 0xaf87af, 268 140: 0xaf87d7, 269 141: 0xaf87ff, 270 142: 0xafaf00, 271 143: 0xafaf5f, 272 144: 0xafaf87, 273 145: 0xafafaf, 274 146: 0xafafd7, 275 147: 0xafafff, 276 148: 0xafd700, 277 149: 0xafd75f, 278 150: 0xafd787, 279 151: 0xafd7af, 280 152: 0xafd7d7, 281 153: 0xafd7ff, 282 154: 0xafff00, 283 155: 0xafff5f, 284 156: 0xafff87, 285 157: 0xafffaf, 286 158: 0xafffd7, 287 159: 0xafffff, 288 160: 0xd70000, 289 161: 0xd7005f, 290 162: 0xd70087, 291 163: 0xd700af, 292 164: 0xd700d7, 293 165: 0xd700ff, 294 166: 0xd75f00, 295 167: 0xd75f5f, 296 168: 0xd75f87, 297 169: 0xd75faf, 298 170: 0xd75fd7, 299 171: 0xd75fff, 300 172: 0xd78700, 301 173: 0xd7875f, 302 174: 0xd78787, 303 175: 0xd787af, 304 176: 0xd787d7, 305 177: 0xd787ff, 306 178: 0xd7af00, 307 179: 0xd7af5f, 308 180: 0xd7af87, 309 181: 0xd7afaf, 310 182: 0xd7afd7, 311 183: 0xd7afff, 312 184: 0xd7d700, 313 185: 0xd7d75f, 314 186: 0xd7d787, 315 187: 0xd7d7af, 316 188: 0xd7d7d7, 317 189: 0xd7d7ff, 318 190: 0xd7ff00, 319 191: 0xd7ff5f, 320 192: 0xd7ff87, 321 193: 0xd7ffaf, 322 194: 0xd7ffd7, 323 195: 0xd7ffff, 324 196: 0xff0000, 325 197: 0xff005f, 326 198: 0xff0087, 327 199: 0xff00af, 328 200: 0xff00d7, 329 201: 0xff00ff, 330 202: 0xff5f00, 331 203: 0xff5f5f, 332 204: 0xff5f87, 333 205: 0xff5faf, 334 206: 0xff5fd7, 335 207: 0xff5fff, 336 208: 0xff8700, 337 209: 0xff875f, 338 210: 0xff8787, 339 211: 0xff87af, 340 212: 0xff87d7, 341 213: 0xff87ff, 342 214: 0xffaf00, 343 215: 0xffaf5f, 344 216: 0xffaf87, 345 217: 0xffafaf, 346 218: 0xffafd7, 347 219: 0xffafff, 348 220: 0xffd700, 349 221: 0xffd75f, 350 222: 0xffd787, 351 223: 0xffd7af, 352 224: 0xffd7d7, 353 225: 0xffd7ff, 354 226: 0xffff00, 355 227: 0xffff5f, 356 228: 0xffff87, 357 229: 0xffffaf, 358 230: 0xffffd7, 359 231: 0xffffff, 360 232: 0x080808, 361 233: 0x121212, 362 234: 0x1c1c1c, 363 235: 0x262626, 364 236: 0x303030, 365 237: 0x3a3a3a, 366 238: 0x444444, 367 239: 0x4e4e4e, 368 240: 0x585858, 369 241: 0x626262, 370 242: 0x6c6c6c, 371 243: 0x767676, 372 244: 0x808080, 373 245: 0x8a8a8a, 374 246: 0x949494, 375 247: 0x9e9e9e, 376 248: 0xa8a8a8, 377 249: 0xb2b2b2, 378 250: 0xbcbcbc, 379 251: 0xc6c6c6, 380 252: 0xd0d0d0, 381 253: 0xdadada, 382 254: 0xe4e4e4, 383 255: 0xeeeeee, 384} 385 386// `\033]0;TITLESTR\007` 387func doTitleSequence(er *bytes.Reader) error { 388 var c byte 389 var err error 390 391 c, err = er.ReadByte() 392 if err != nil { 393 return err 394 } 395 if c != '0' && c != '2' { 396 return nil 397 } 398 c, err = er.ReadByte() 399 if err != nil { 400 return err 401 } 402 if c != ';' { 403 return nil 404 } 405 title := make([]byte, 0, 80) 406 for { 407 c, err = er.ReadByte() 408 if err != nil { 409 return err 410 } 411 if c == 0x07 || c == '\n' { 412 break 413 } 414 title = append(title, c) 415 } 416 if len(title) > 0 { 417 title8, err := syscall.UTF16PtrFromString(string(title)) 418 if err == nil { 419 procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8))) 420 } 421 } 422 return nil 423} 424 425// returns Atoi(s) unless s == "" in which case it returns def 426func atoiWithDefault(s string, def int) (int, error) { 427 if s == "" { 428 return def, nil 429 } 430 return strconv.Atoi(s) 431} 432 433// Write writes data on console 434func (w *Writer) Write(data []byte) (n int, err error) { 435 var csbi consoleScreenBufferInfo 436 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 437 438 handle := w.handle 439 440 var er *bytes.Reader 441 if w.rest.Len() > 0 { 442 var rest bytes.Buffer 443 w.rest.WriteTo(&rest) 444 w.rest.Reset() 445 rest.Write(data) 446 er = bytes.NewReader(rest.Bytes()) 447 } else { 448 er = bytes.NewReader(data) 449 } 450 var bw [1]byte 451loop: 452 for { 453 c1, err := er.ReadByte() 454 if err != nil { 455 break loop 456 } 457 if c1 != 0x1b { 458 bw[0] = c1 459 w.out.Write(bw[:]) 460 continue 461 } 462 c2, err := er.ReadByte() 463 if err != nil { 464 break loop 465 } 466 467 switch c2 { 468 case '>': 469 continue 470 case ']': 471 w.rest.WriteByte(c1) 472 w.rest.WriteByte(c2) 473 er.WriteTo(&w.rest) 474 if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 { 475 break loop 476 } 477 er = bytes.NewReader(w.rest.Bytes()[2:]) 478 err := doTitleSequence(er) 479 if err != nil { 480 break loop 481 } 482 w.rest.Reset() 483 continue 484 // https://github.com/mattn/go-colorable/issues/27 485 case '7': 486 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 487 w.oldpos = csbi.cursorPosition 488 continue 489 case '8': 490 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) 491 continue 492 case 0x5b: 493 // execute part after switch 494 default: 495 continue 496 } 497 498 w.rest.WriteByte(c1) 499 w.rest.WriteByte(c2) 500 er.WriteTo(&w.rest) 501 502 var buf bytes.Buffer 503 var m byte 504 for i, c := range w.rest.Bytes()[2:] { 505 if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { 506 m = c 507 er = bytes.NewReader(w.rest.Bytes()[2+i+1:]) 508 w.rest.Reset() 509 break 510 } 511 buf.Write([]byte(string(c))) 512 } 513 if m == 0 { 514 break loop 515 } 516 517 switch m { 518 case 'A': 519 n, err = atoiWithDefault(buf.String(), 1) 520 if err != nil { 521 continue 522 } 523 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 524 csbi.cursorPosition.y -= short(n) 525 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 526 case 'B': 527 n, err = atoiWithDefault(buf.String(), 1) 528 if err != nil { 529 continue 530 } 531 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 532 csbi.cursorPosition.y += short(n) 533 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 534 case 'C': 535 n, err = atoiWithDefault(buf.String(), 1) 536 if err != nil { 537 continue 538 } 539 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 540 csbi.cursorPosition.x += short(n) 541 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 542 case 'D': 543 n, err = atoiWithDefault(buf.String(), 1) 544 if err != nil { 545 continue 546 } 547 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 548 csbi.cursorPosition.x -= short(n) 549 if csbi.cursorPosition.x < 0 { 550 csbi.cursorPosition.x = 0 551 } 552 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 553 case 'E': 554 n, err = strconv.Atoi(buf.String()) 555 if err != nil { 556 continue 557 } 558 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 559 csbi.cursorPosition.x = 0 560 csbi.cursorPosition.y += short(n) 561 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 562 case 'F': 563 n, err = strconv.Atoi(buf.String()) 564 if err != nil { 565 continue 566 } 567 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 568 csbi.cursorPosition.x = 0 569 csbi.cursorPosition.y -= short(n) 570 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 571 case 'G': 572 n, err = strconv.Atoi(buf.String()) 573 if err != nil { 574 continue 575 } 576 if n < 1 { 577 n = 1 578 } 579 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 580 csbi.cursorPosition.x = short(n - 1) 581 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 582 case 'H', 'f': 583 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 584 if buf.Len() > 0 { 585 token := strings.Split(buf.String(), ";") 586 switch len(token) { 587 case 1: 588 n1, err := strconv.Atoi(token[0]) 589 if err != nil { 590 continue 591 } 592 csbi.cursorPosition.y = short(n1 - 1) 593 case 2: 594 n1, err := strconv.Atoi(token[0]) 595 if err != nil { 596 continue 597 } 598 n2, err := strconv.Atoi(token[1]) 599 if err != nil { 600 continue 601 } 602 csbi.cursorPosition.x = short(n2 - 1) 603 csbi.cursorPosition.y = short(n1 - 1) 604 } 605 } else { 606 csbi.cursorPosition.y = 0 607 } 608 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 609 case 'J': 610 n := 0 611 if buf.Len() > 0 { 612 n, err = strconv.Atoi(buf.String()) 613 if err != nil { 614 continue 615 } 616 } 617 var count, written dword 618 var cursor coord 619 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 620 switch n { 621 case 0: 622 cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} 623 count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) 624 case 1: 625 cursor = coord{x: csbi.window.left, y: csbi.window.top} 626 count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x) 627 case 2: 628 cursor = coord{x: csbi.window.left, y: csbi.window.top} 629 count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) 630 } 631 procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 632 procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 633 case 'K': 634 n := 0 635 if buf.Len() > 0 { 636 n, err = strconv.Atoi(buf.String()) 637 if err != nil { 638 continue 639 } 640 } 641 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 642 var cursor coord 643 var count, written dword 644 switch n { 645 case 0: 646 cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} 647 count = dword(csbi.size.x - csbi.cursorPosition.x) 648 case 1: 649 cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} 650 count = dword(csbi.size.x - csbi.cursorPosition.x) 651 case 2: 652 cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} 653 count = dword(csbi.size.x) 654 } 655 procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 656 procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 657 case 'X': 658 n := 0 659 if buf.Len() > 0 { 660 n, err = strconv.Atoi(buf.String()) 661 if err != nil { 662 continue 663 } 664 } 665 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 666 var cursor coord 667 var written dword 668 cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} 669 procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 670 procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 671 case 'm': 672 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 673 attr := csbi.attributes 674 cs := buf.String() 675 if cs == "" { 676 procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) 677 continue 678 } 679 token := strings.Split(cs, ";") 680 for i := 0; i < len(token); i++ { 681 ns := token[i] 682 if n, err = strconv.Atoi(ns); err == nil { 683 switch { 684 case n == 0 || n == 100: 685 attr = w.oldattr 686 case 1 <= n && n <= 5: 687 attr |= foregroundIntensity 688 case n == 7: 689 attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) 690 case n == 22 || n == 25: 691 attr |= foregroundIntensity 692 case n == 27: 693 attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) 694 case 30 <= n && n <= 37: 695 attr &= backgroundMask 696 if (n-30)&1 != 0 { 697 attr |= foregroundRed 698 } 699 if (n-30)&2 != 0 { 700 attr |= foregroundGreen 701 } 702 if (n-30)&4 != 0 { 703 attr |= foregroundBlue 704 } 705 case n == 38: // set foreground color. 706 if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { 707 if n256, err := strconv.Atoi(token[i+2]); err == nil { 708 if n256foreAttr == nil { 709 n256setup() 710 } 711 attr &= backgroundMask 712 attr |= n256foreAttr[n256] 713 i += 2 714 } 715 } else if len(token) == 5 && token[i+1] == "2" { 716 var r, g, b int 717 r, _ = strconv.Atoi(token[i+2]) 718 g, _ = strconv.Atoi(token[i+3]) 719 b, _ = strconv.Atoi(token[i+4]) 720 i += 4 721 if r > 127 { 722 attr |= foregroundRed 723 } 724 if g > 127 { 725 attr |= foregroundGreen 726 } 727 if b > 127 { 728 attr |= foregroundBlue 729 } 730 } else { 731 attr = attr & (w.oldattr & backgroundMask) 732 } 733 case n == 39: // reset foreground color. 734 attr &= backgroundMask 735 attr |= w.oldattr & foregroundMask 736 case 40 <= n && n <= 47: 737 attr &= foregroundMask 738 if (n-40)&1 != 0 { 739 attr |= backgroundRed 740 } 741 if (n-40)&2 != 0 { 742 attr |= backgroundGreen 743 } 744 if (n-40)&4 != 0 { 745 attr |= backgroundBlue 746 } 747 case n == 48: // set background color. 748 if i < len(token)-2 && token[i+1] == "5" { 749 if n256, err := strconv.Atoi(token[i+2]); err == nil { 750 if n256backAttr == nil { 751 n256setup() 752 } 753 attr &= foregroundMask 754 attr |= n256backAttr[n256] 755 i += 2 756 } 757 } else if len(token) == 5 && token[i+1] == "2" { 758 var r, g, b int 759 r, _ = strconv.Atoi(token[i+2]) 760 g, _ = strconv.Atoi(token[i+3]) 761 b, _ = strconv.Atoi(token[i+4]) 762 i += 4 763 if r > 127 { 764 attr |= backgroundRed 765 } 766 if g > 127 { 767 attr |= backgroundGreen 768 } 769 if b > 127 { 770 attr |= backgroundBlue 771 } 772 } else { 773 attr = attr & (w.oldattr & foregroundMask) 774 } 775 case n == 49: // reset foreground color. 776 attr &= foregroundMask 777 attr |= w.oldattr & backgroundMask 778 case 90 <= n && n <= 97: 779 attr = (attr & backgroundMask) 780 attr |= foregroundIntensity 781 if (n-90)&1 != 0 { 782 attr |= foregroundRed 783 } 784 if (n-90)&2 != 0 { 785 attr |= foregroundGreen 786 } 787 if (n-90)&4 != 0 { 788 attr |= foregroundBlue 789 } 790 case 100 <= n && n <= 107: 791 attr = (attr & foregroundMask) 792 attr |= backgroundIntensity 793 if (n-100)&1 != 0 { 794 attr |= backgroundRed 795 } 796 if (n-100)&2 != 0 { 797 attr |= backgroundGreen 798 } 799 if (n-100)&4 != 0 { 800 attr |= backgroundBlue 801 } 802 } 803 procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) 804 } 805 } 806 case 'h': 807 var ci consoleCursorInfo 808 cs := buf.String() 809 if cs == "5>" { 810 procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 811 ci.visible = 0 812 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 813 } else if cs == "?25" { 814 procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 815 ci.visible = 1 816 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 817 } else if cs == "?1049" { 818 if w.althandle == 0 { 819 h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0) 820 w.althandle = syscall.Handle(h) 821 if w.althandle != 0 { 822 handle = w.althandle 823 } 824 } 825 } 826 case 'l': 827 var ci consoleCursorInfo 828 cs := buf.String() 829 if cs == "5>" { 830 procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 831 ci.visible = 1 832 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 833 } else if cs == "?25" { 834 procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 835 ci.visible = 0 836 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 837 } else if cs == "?1049" { 838 if w.althandle != 0 { 839 syscall.CloseHandle(w.althandle) 840 w.althandle = 0 841 handle = w.handle 842 } 843 } 844 case 's': 845 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 846 w.oldpos = csbi.cursorPosition 847 case 'u': 848 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) 849 } 850 } 851 852 return len(data), nil 853} 854 855type consoleColor struct { 856 rgb int 857 red bool 858 green bool 859 blue bool 860 intensity bool 861} 862 863func (c consoleColor) foregroundAttr() (attr word) { 864 if c.red { 865 attr |= foregroundRed 866 } 867 if c.green { 868 attr |= foregroundGreen 869 } 870 if c.blue { 871 attr |= foregroundBlue 872 } 873 if c.intensity { 874 attr |= foregroundIntensity 875 } 876 return 877} 878 879func (c consoleColor) backgroundAttr() (attr word) { 880 if c.red { 881 attr |= backgroundRed 882 } 883 if c.green { 884 attr |= backgroundGreen 885 } 886 if c.blue { 887 attr |= backgroundBlue 888 } 889 if c.intensity { 890 attr |= backgroundIntensity 891 } 892 return 893} 894 895var color16 = []consoleColor{ 896 {0x000000, false, false, false, false}, 897 {0x000080, false, false, true, false}, 898 {0x008000, false, true, false, false}, 899 {0x008080, false, true, true, false}, 900 {0x800000, true, false, false, false}, 901 {0x800080, true, false, true, false}, 902 {0x808000, true, true, false, false}, 903 {0xc0c0c0, true, true, true, false}, 904 {0x808080, false, false, false, true}, 905 {0x0000ff, false, false, true, true}, 906 {0x00ff00, false, true, false, true}, 907 {0x00ffff, false, true, true, true}, 908 {0xff0000, true, false, false, true}, 909 {0xff00ff, true, false, true, true}, 910 {0xffff00, true, true, false, true}, 911 {0xffffff, true, true, true, true}, 912} 913 914type hsv struct { 915 h, s, v float32 916} 917 918func (a hsv) dist(b hsv) float32 { 919 dh := a.h - b.h 920 switch { 921 case dh > 0.5: 922 dh = 1 - dh 923 case dh < -0.5: 924 dh = -1 - dh 925 } 926 ds := a.s - b.s 927 dv := a.v - b.v 928 return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) 929} 930 931func toHSV(rgb int) hsv { 932 r, g, b := float32((rgb&0xFF0000)>>16)/256.0, 933 float32((rgb&0x00FF00)>>8)/256.0, 934 float32(rgb&0x0000FF)/256.0 935 min, max := minmax3f(r, g, b) 936 h := max - min 937 if h > 0 { 938 if max == r { 939 h = (g - b) / h 940 if h < 0 { 941 h += 6 942 } 943 } else if max == g { 944 h = 2 + (b-r)/h 945 } else { 946 h = 4 + (r-g)/h 947 } 948 } 949 h /= 6.0 950 s := max - min 951 if max != 0 { 952 s /= max 953 } 954 v := max 955 return hsv{h: h, s: s, v: v} 956} 957 958type hsvTable []hsv 959 960func toHSVTable(rgbTable []consoleColor) hsvTable { 961 t := make(hsvTable, len(rgbTable)) 962 for i, c := range rgbTable { 963 t[i] = toHSV(c.rgb) 964 } 965 return t 966} 967 968func (t hsvTable) find(rgb int) consoleColor { 969 hsv := toHSV(rgb) 970 n := 7 971 l := float32(5.0) 972 for i, p := range t { 973 d := hsv.dist(p) 974 if d < l { 975 l, n = d, i 976 } 977 } 978 return color16[n] 979} 980 981func minmax3f(a, b, c float32) (min, max float32) { 982 if a < b { 983 if b < c { 984 return a, c 985 } else if a < c { 986 return a, b 987 } else { 988 return c, b 989 } 990 } else { 991 if a < c { 992 return b, c 993 } else if b < c { 994 return b, a 995 } else { 996 return c, a 997 } 998 } 999} 1000 1001var n256foreAttr []word 1002var n256backAttr []word 1003 1004func n256setup() { 1005 n256foreAttr = make([]word, 256) 1006 n256backAttr = make([]word, 256) 1007 t := toHSVTable(color16) 1008 for i, rgb := range color256 { 1009 c := t.find(rgb) 1010 n256foreAttr[i] = c.foregroundAttr() 1011 n256backAttr[i] = c.backgroundAttr() 1012 } 1013} 1014 1015// EnableColorsStdout enable colors if possible. 1016func EnableColorsStdout(enabled *bool) func() { 1017 var mode uint32 1018 h := os.Stdout.Fd() 1019 if r, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))); r != 0 { 1020 if r, _, _ = procSetConsoleMode.Call(h, uintptr(mode|cENABLE_VIRTUAL_TERMINAL_PROCESSING)); r != 0 { 1021 if enabled != nil { 1022 *enabled = true 1023 } 1024 return func() { 1025 procSetConsoleMode.Call(h, uintptr(mode)) 1026 } 1027 } 1028 } 1029 if enabled != nil { 1030 *enabled = true 1031 } 1032 return func() {} 1033} 1034