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