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