1// +build windows 2 3// Copyright 2019 The TCell Authors 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); 6// you may not use file except in compliance with the License. 7// You may obtain a copy of the license at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16 17package tcell 18 19import ( 20 "errors" 21 "sync" 22 "syscall" 23 "unicode/utf16" 24 "unsafe" 25) 26 27type cScreen struct { 28 in syscall.Handle 29 out syscall.Handle 30 cancelflag syscall.Handle 31 scandone chan struct{} 32 evch chan Event 33 quit chan struct{} 34 curx int 35 cury int 36 style Style 37 clear bool 38 fini bool 39 40 w int 41 h int 42 43 oscreen consoleInfo 44 ocursor cursorInfo 45 oimode uint32 46 oomode uint32 47 cells CellBuffer 48 colors map[Color]Color 49 50 sync.Mutex 51} 52 53var winLock sync.Mutex 54 55var winPalette = []Color{ 56 ColorBlack, 57 ColorMaroon, 58 ColorGreen, 59 ColorNavy, 60 ColorOlive, 61 ColorPurple, 62 ColorTeal, 63 ColorSilver, 64 ColorGray, 65 ColorRed, 66 ColorLime, 67 ColorBlue, 68 ColorYellow, 69 ColorFuchsia, 70 ColorAqua, 71 ColorWhite, 72} 73 74var winColors = map[Color]Color{ 75 ColorBlack: ColorBlack, 76 ColorMaroon: ColorMaroon, 77 ColorGreen: ColorGreen, 78 ColorNavy: ColorNavy, 79 ColorOlive: ColorOlive, 80 ColorPurple: ColorPurple, 81 ColorTeal: ColorTeal, 82 ColorSilver: ColorSilver, 83 ColorGray: ColorGray, 84 ColorRed: ColorRed, 85 ColorLime: ColorLime, 86 ColorBlue: ColorBlue, 87 ColorYellow: ColorYellow, 88 ColorFuchsia: ColorFuchsia, 89 ColorAqua: ColorAqua, 90 ColorWhite: ColorWhite, 91} 92 93var k32 = syscall.NewLazyDLL("kernel32.dll") 94 95// We have to bring in the kernel32.dll directly, so we can get access to some 96// system calls that the core Go API lacks. 97// 98// Note that Windows appends some functions with W to indicate that wide 99// characters (Unicode) are in use. The documentation refers to them 100// without this suffix, as the resolution is made via preprocessor. 101var ( 102 procReadConsoleInput = k32.NewProc("ReadConsoleInputW") 103 procWaitForMultipleObjects = k32.NewProc("WaitForMultipleObjects") 104 procCreateEvent = k32.NewProc("CreateEventW") 105 procSetEvent = k32.NewProc("SetEvent") 106 procGetConsoleCursorInfo = k32.NewProc("GetConsoleCursorInfo") 107 procSetConsoleCursorInfo = k32.NewProc("SetConsoleCursorInfo") 108 procSetConsoleCursorPosition = k32.NewProc("SetConsoleCursorPosition") 109 procSetConsoleMode = k32.NewProc("SetConsoleMode") 110 procGetConsoleMode = k32.NewProc("GetConsoleMode") 111 procGetConsoleScreenBufferInfo = k32.NewProc("GetConsoleScreenBufferInfo") 112 procFillConsoleOutputAttribute = k32.NewProc("FillConsoleOutputAttribute") 113 procFillConsoleOutputCharacter = k32.NewProc("FillConsoleOutputCharacterW") 114 procSetConsoleWindowInfo = k32.NewProc("SetConsoleWindowInfo") 115 procSetConsoleScreenBufferSize = k32.NewProc("SetConsoleScreenBufferSize") 116 procSetConsoleTextAttribute = k32.NewProc("SetConsoleTextAttribute") 117) 118 119const ( 120 w32Infinite = ^uintptr(0) 121 w32WaitObject0 = uintptr(0) 122) 123 124// NewConsoleScreen returns a Screen for the Windows console associated 125// with the current process. The Screen makes use of the Windows Console 126// API to display content and read events. 127func NewConsoleScreen() (Screen, error) { 128 return &cScreen{}, nil 129} 130 131func (s *cScreen) Init() error { 132 s.evch = make(chan Event, 10) 133 s.quit = make(chan struct{}) 134 s.scandone = make(chan struct{}) 135 136 in, e := syscall.Open("CONIN$", syscall.O_RDWR, 0) 137 if e != nil { 138 return e 139 } 140 s.in = in 141 out, e := syscall.Open("CONOUT$", syscall.O_RDWR, 0) 142 if e != nil { 143 syscall.Close(s.in) 144 return e 145 } 146 s.out = out 147 148 cf, _, e := procCreateEvent.Call( 149 uintptr(0), 150 uintptr(1), 151 uintptr(0), 152 uintptr(0)) 153 if cf == uintptr(0) { 154 return e 155 } 156 s.cancelflag = syscall.Handle(cf) 157 158 s.Lock() 159 160 s.curx = -1 161 s.cury = -1 162 s.style = StyleDefault 163 s.getCursorInfo(&s.ocursor) 164 s.getConsoleInfo(&s.oscreen) 165 s.getOutMode(&s.oomode) 166 s.getInMode(&s.oimode) 167 s.resize() 168 169 s.fini = false 170 s.setInMode(modeResizeEn) 171 s.setOutMode(0) 172 s.clearScreen(s.style) 173 s.hideCursor() 174 s.Unlock() 175 go s.scanInput() 176 177 return nil 178} 179 180func (s *cScreen) CharacterSet() string { 181 // We are always UTF-16LE on Windows 182 return "UTF-16LE" 183} 184 185func (s *cScreen) EnableMouse() { 186 s.setInMode(modeResizeEn | modeMouseEn | modeExtndFlg) 187} 188 189func (s *cScreen) DisableMouse() { 190 s.setInMode(modeResizeEn) 191} 192 193func (s *cScreen) Fini() { 194 s.Lock() 195 s.style = StyleDefault 196 s.curx = -1 197 s.cury = -1 198 s.fini = true 199 s.Unlock() 200 201 s.setCursorInfo(&s.ocursor) 202 s.setInMode(s.oimode) 203 s.setOutMode(s.oomode) 204 s.setBufferSize(int(s.oscreen.size.x), int(s.oscreen.size.y)) 205 s.clearScreen(StyleDefault) 206 s.setCursorPos(0, 0) 207 procSetConsoleTextAttribute.Call( 208 uintptr(s.out), 209 uintptr(s.mapStyle(StyleDefault))) 210 211 close(s.quit) 212 procSetEvent.Call(uintptr(s.cancelflag)) 213 // Block until scanInput returns; this prevents a race condition on Win 8+ 214 // which causes syscall.Close to block until another keypress is read. 215 <-s.scandone 216 syscall.Close(s.in) 217 syscall.Close(s.out) 218} 219 220func (s *cScreen) PostEventWait(ev Event) { 221 s.evch <- ev 222} 223 224func (s *cScreen) PostEvent(ev Event) error { 225 select { 226 case s.evch <- ev: 227 return nil 228 default: 229 return ErrEventQFull 230 } 231} 232 233func (s *cScreen) PollEvent() Event { 234 select { 235 case <-s.quit: 236 return nil 237 case ev := <-s.evch: 238 return ev 239 } 240} 241 242type cursorInfo struct { 243 size uint32 244 visible uint32 245} 246 247type coord struct { 248 x int16 249 y int16 250} 251 252func (c coord) uintptr() uintptr { 253 // little endian, put x first 254 return uintptr(c.x) | (uintptr(c.y) << 16) 255} 256 257type rect struct { 258 left int16 259 top int16 260 right int16 261 bottom int16 262} 263 264func (s *cScreen) showCursor() { 265 s.setCursorInfo(&cursorInfo{size: 100, visible: 1}) 266} 267 268func (s *cScreen) hideCursor() { 269 s.setCursorInfo(&cursorInfo{size: 1, visible: 0}) 270} 271 272func (s *cScreen) ShowCursor(x, y int) { 273 s.Lock() 274 if !s.fini { 275 s.curx = x 276 s.cury = y 277 } 278 s.doCursor() 279 s.Unlock() 280} 281 282func (s *cScreen) doCursor() { 283 x, y := s.curx, s.cury 284 285 if x < 0 || y < 0 || x >= s.w || y >= s.h { 286 s.hideCursor() 287 } else { 288 s.setCursorPos(x, y) 289 s.showCursor() 290 } 291} 292 293func (s *cScreen) HideCursor() { 294 s.ShowCursor(-1, -1) 295} 296 297type charInfo struct { 298 ch uint16 299 attr uint16 300} 301 302type inputRecord struct { 303 typ uint16 304 _ uint16 305 data [16]byte 306} 307 308const ( 309 keyEvent uint16 = 1 310 mouseEvent uint16 = 2 311 resizeEvent uint16 = 4 312 menuEvent uint16 = 8 // don't use 313 focusEvent uint16 = 16 // don't use 314) 315 316type mouseRecord struct { 317 x int16 318 y int16 319 btns uint32 320 mod uint32 321 flags uint32 322} 323 324const ( 325 mouseDoubleClick uint32 = 0x2 326 mouseHWheeled uint32 = 0x8 327 mouseVWheeled uint32 = 0x4 328 mouseMoved uint32 = 0x1 329) 330 331type resizeRecord struct { 332 x int16 333 y int16 334} 335 336type keyRecord struct { 337 isdown int32 338 repeat uint16 339 kcode uint16 340 scode uint16 341 ch uint16 342 mod uint32 343} 344 345const ( 346 // Constants per Microsoft. We don't put the modifiers 347 // here. 348 vkCancel = 0x03 349 vkBack = 0x08 // Backspace 350 vkTab = 0x09 351 vkClear = 0x0c 352 vkReturn = 0x0d 353 vkPause = 0x13 354 vkEscape = 0x1b 355 vkSpace = 0x20 356 vkPrior = 0x21 // PgUp 357 vkNext = 0x22 // PgDn 358 vkEnd = 0x23 359 vkHome = 0x24 360 vkLeft = 0x25 361 vkUp = 0x26 362 vkRight = 0x27 363 vkDown = 0x28 364 vkPrint = 0x2a 365 vkPrtScr = 0x2c 366 vkInsert = 0x2d 367 vkDelete = 0x2e 368 vkHelp = 0x2f 369 vkF1 = 0x70 370 vkF2 = 0x71 371 vkF3 = 0x72 372 vkF4 = 0x73 373 vkF5 = 0x74 374 vkF6 = 0x75 375 vkF7 = 0x76 376 vkF8 = 0x77 377 vkF9 = 0x78 378 vkF10 = 0x79 379 vkF11 = 0x7a 380 vkF12 = 0x7b 381 vkF13 = 0x7c 382 vkF14 = 0x7d 383 vkF15 = 0x7e 384 vkF16 = 0x7f 385 vkF17 = 0x80 386 vkF18 = 0x81 387 vkF19 = 0x82 388 vkF20 = 0x83 389 vkF21 = 0x84 390 vkF22 = 0x85 391 vkF23 = 0x86 392 vkF24 = 0x87 393) 394 395var vkKeys = map[uint16]Key{ 396 vkCancel: KeyCancel, 397 vkBack: KeyBackspace, 398 vkTab: KeyTab, 399 vkClear: KeyClear, 400 vkPause: KeyPause, 401 vkPrint: KeyPrint, 402 vkPrtScr: KeyPrint, 403 vkPrior: KeyPgUp, 404 vkNext: KeyPgDn, 405 vkReturn: KeyEnter, 406 vkEnd: KeyEnd, 407 vkHome: KeyHome, 408 vkLeft: KeyLeft, 409 vkUp: KeyUp, 410 vkRight: KeyRight, 411 vkDown: KeyDown, 412 vkInsert: KeyInsert, 413 vkDelete: KeyDelete, 414 vkHelp: KeyHelp, 415 vkF1: KeyF1, 416 vkF2: KeyF2, 417 vkF3: KeyF3, 418 vkF4: KeyF4, 419 vkF5: KeyF5, 420 vkF6: KeyF6, 421 vkF7: KeyF7, 422 vkF8: KeyF8, 423 vkF9: KeyF9, 424 vkF10: KeyF10, 425 vkF11: KeyF11, 426 vkF12: KeyF12, 427 vkF13: KeyF13, 428 vkF14: KeyF14, 429 vkF15: KeyF15, 430 vkF16: KeyF16, 431 vkF17: KeyF17, 432 vkF18: KeyF18, 433 vkF19: KeyF19, 434 vkF20: KeyF20, 435 vkF21: KeyF21, 436 vkF22: KeyF22, 437 vkF23: KeyF23, 438 vkF24: KeyF24, 439} 440 441// NB: All Windows platforms are little endian. We assume this 442// never, ever change. The following code is endian safe. and does 443// not use unsafe pointers. 444func getu32(v []byte) uint32 { 445 return uint32(v[0]) + (uint32(v[1]) << 8) + (uint32(v[2]) << 16) + (uint32(v[3]) << 24) 446} 447func geti32(v []byte) int32 { 448 return int32(getu32(v)) 449} 450func getu16(v []byte) uint16 { 451 return uint16(v[0]) + (uint16(v[1]) << 8) 452} 453func geti16(v []byte) int16 { 454 return int16(getu16(v)) 455} 456 457// Convert windows dwControlKeyState to modifier mask 458func mod2mask(cks uint32) ModMask { 459 mm := ModNone 460 // Left or right control 461 if (cks & (0x0008 | 0x0004)) != 0 { 462 mm |= ModCtrl 463 } 464 // Left or right alt 465 if (cks & (0x0002 | 0x0001)) != 0 { 466 mm |= ModAlt 467 } 468 // Any shift 469 if (cks & 0x0010) != 0 { 470 mm |= ModShift 471 } 472 return mm 473} 474 475func mrec2btns(mbtns, flags uint32) ButtonMask { 476 btns := ButtonNone 477 if mbtns&0x1 != 0 { 478 btns |= Button1 479 } 480 if mbtns&0x2 != 0 { 481 btns |= Button2 482 } 483 if mbtns&0x4 != 0 { 484 btns |= Button3 485 } 486 if mbtns&0x8 != 0 { 487 btns |= Button4 488 } 489 if mbtns&0x10 != 0 { 490 btns |= Button5 491 } 492 if mbtns&0x20 != 0 { 493 btns |= Button6 494 } 495 if mbtns&0x40 != 0 { 496 btns |= Button7 497 } 498 if mbtns&0x80 != 0 { 499 btns |= Button8 500 } 501 502 if flags&mouseVWheeled != 0 { 503 if mbtns&0x80000000 == 0 { 504 btns |= WheelUp 505 } else { 506 btns |= WheelDown 507 } 508 } 509 if flags&mouseHWheeled != 0 { 510 if mbtns&0x80000000 == 0 { 511 btns |= WheelRight 512 } else { 513 btns |= WheelLeft 514 } 515 } 516 return btns 517} 518 519func (s *cScreen) getConsoleInput() error { 520 // cancelFlag comes first as WaitForMultipleObjects returns the lowest index 521 // in the event that both events are signalled. 522 waitObjects := []syscall.Handle{s.cancelflag, s.in} 523 // As arrays are contiguous in memory, a pointer to the first object is the 524 // same as a pointer to the array itself. 525 pWaitObjects := unsafe.Pointer(&waitObjects[0]) 526 527 rv, _, er := procWaitForMultipleObjects.Call( 528 uintptr(len(waitObjects)), 529 uintptr(pWaitObjects), 530 uintptr(0), 531 w32Infinite) 532 // WaitForMultipleObjects returns WAIT_OBJECT_0 + the index. 533 switch rv { 534 case w32WaitObject0: // s.cancelFlag 535 return errors.New("cancelled") 536 case w32WaitObject0 + 1: // s.in 537 rec := &inputRecord{} 538 var nrec int32 539 rv, _, er := procReadConsoleInput.Call( 540 uintptr(s.in), 541 uintptr(unsafe.Pointer(rec)), 542 uintptr(1), 543 uintptr(unsafe.Pointer(&nrec))) 544 if rv == 0 { 545 return er 546 } 547 if nrec != 1 { 548 return nil 549 } 550 switch rec.typ { 551 case keyEvent: 552 krec := &keyRecord{} 553 krec.isdown = geti32(rec.data[0:]) 554 krec.repeat = getu16(rec.data[4:]) 555 krec.kcode = getu16(rec.data[6:]) 556 krec.scode = getu16(rec.data[8:]) 557 krec.ch = getu16(rec.data[10:]) 558 krec.mod = getu32(rec.data[12:]) 559 560 if krec.isdown == 0 || krec.repeat < 1 { 561 // its a key release event, ignore it 562 return nil 563 } 564 if krec.ch != 0 { 565 // synthesized key code 566 for krec.repeat > 0 { 567 // convert shift+tab to backtab 568 if mod2mask(krec.mod) == ModShift && krec.ch == vkTab { 569 s.PostEvent(NewEventKey(KeyBacktab, 0, 570 ModNone)) 571 } else { 572 s.PostEvent(NewEventKey(KeyRune, rune(krec.ch), 573 mod2mask(krec.mod))) 574 } 575 krec.repeat-- 576 } 577 return nil 578 } 579 key := KeyNUL // impossible on Windows 580 ok := false 581 if key, ok = vkKeys[krec.kcode]; !ok { 582 return nil 583 } 584 for krec.repeat > 0 { 585 s.PostEvent(NewEventKey(key, rune(krec.ch), 586 mod2mask(krec.mod))) 587 krec.repeat-- 588 } 589 590 case mouseEvent: 591 var mrec mouseRecord 592 mrec.x = geti16(rec.data[0:]) 593 mrec.y = geti16(rec.data[2:]) 594 mrec.btns = getu32(rec.data[4:]) 595 mrec.mod = getu32(rec.data[8:]) 596 mrec.flags = getu32(rec.data[12:]) 597 btns := mrec2btns(mrec.btns, mrec.flags) 598 // we ignore double click, events are delivered normally 599 s.PostEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns, 600 mod2mask(mrec.mod))) 601 602 case resizeEvent: 603 var rrec resizeRecord 604 rrec.x = geti16(rec.data[0:]) 605 rrec.y = geti16(rec.data[2:]) 606 s.PostEvent(NewEventResize(int(rrec.x), int(rrec.y))) 607 608 default: 609 } 610 default: 611 return er 612 } 613 614 return nil 615} 616 617func (s *cScreen) scanInput() { 618 for { 619 if e := s.getConsoleInput(); e != nil { 620 close(s.scandone) 621 return 622 } 623 } 624} 625 626// Windows console can display 8 characters, in either low or high intensity 627func (s *cScreen) Colors() int { 628 return 16 629} 630 631var vgaColors = map[Color]uint16{ 632 ColorBlack: 0, 633 ColorMaroon: 0x4, 634 ColorGreen: 0x2, 635 ColorNavy: 0x1, 636 ColorOlive: 0x6, 637 ColorPurple: 0x5, 638 ColorTeal: 0x3, 639 ColorSilver: 0x7, 640 ColorGrey: 0x8, 641 ColorRed: 0xc, 642 ColorLime: 0xa, 643 ColorBlue: 0x9, 644 ColorYellow: 0xe, 645 ColorFuchsia: 0xd, 646 ColorAqua: 0xb, 647 ColorWhite: 0xf, 648} 649 650// Windows uses RGB signals 651func mapColor2RGB(c Color) uint16 { 652 winLock.Lock() 653 if v, ok := winColors[c]; ok { 654 c = v 655 } else { 656 v = FindColor(c, winPalette) 657 winColors[c] = v 658 c = v 659 } 660 winLock.Unlock() 661 662 if vc, ok := vgaColors[c]; ok { 663 return vc 664 } 665 return 0 666} 667 668// Map a tcell style to Windows attributes 669func (s *cScreen) mapStyle(style Style) uint16 { 670 f, b, a := style.Decompose() 671 fa := s.oscreen.attrs & 0xf 672 ba := (s.oscreen.attrs) >> 4 & 0xf 673 if f != ColorDefault { 674 fa = mapColor2RGB(f) 675 } 676 if b != ColorDefault { 677 ba = mapColor2RGB(b) 678 } 679 var attr uint16 680 // We simulate reverse by doing the color swap ourselves. 681 // Apparently windows cannot really do this except in DBCS 682 // views. 683 if a&AttrReverse != 0 { 684 attr = ba 685 attr |= (fa << 4) 686 } else { 687 attr = fa 688 attr |= (ba << 4) 689 } 690 if a&AttrBold != 0 { 691 attr |= 0x8 692 } 693 if a&AttrDim != 0 { 694 attr &^= 0x8 695 } 696 if a&AttrUnderline != 0 { 697 // Best effort -- doesn't seem to work though. 698 attr |= 0x8000 699 } 700 // Blink is unsupported 701 return attr 702} 703 704func (s *cScreen) SetCell(x, y int, style Style, ch ...rune) { 705 if len(ch) > 0 { 706 s.SetContent(x, y, ch[0], ch[1:], style) 707 } else { 708 s.SetContent(x, y, ' ', nil, style) 709 } 710} 711 712func (s *cScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) { 713 s.Lock() 714 if !s.fini { 715 s.cells.SetContent(x, y, mainc, combc, style) 716 } 717 s.Unlock() 718} 719 720func (s *cScreen) GetContent(x, y int) (rune, []rune, Style, int) { 721 s.Lock() 722 mainc, combc, style, width := s.cells.GetContent(x, y) 723 s.Unlock() 724 return mainc, combc, style, width 725} 726 727func (s *cScreen) writeString(x, y int, style Style, ch []uint16) { 728 // we assume the caller has hidden the cursor 729 if len(ch) == 0 { 730 return 731 } 732 nw := uint32(len(ch)) 733 procSetConsoleTextAttribute.Call( 734 uintptr(s.out), 735 uintptr(s.mapStyle(style))) 736 s.setCursorPos(x, y) 737 syscall.WriteConsole(s.out, &ch[0], nw, &nw, nil) 738} 739 740func (s *cScreen) draw() { 741 // allocate a scratch line bit enough for no combining chars. 742 // if you have combining characters, you may pay for extra allocs. 743 if s.clear { 744 s.clearScreen(s.style) 745 s.clear = false 746 s.cells.Invalidate() 747 } 748 buf := make([]uint16, 0, s.w) 749 wcs := buf[:] 750 lstyle := Style(-1) // invalid attribute 751 752 lx, ly := -1, -1 753 ra := make([]rune, 1) 754 755 for y := 0; y < int(s.h); y++ { 756 for x := 0; x < int(s.w); x++ { 757 mainc, combc, style, width := s.cells.GetContent(x, y) 758 dirty := s.cells.Dirty(x, y) 759 if style == StyleDefault { 760 style = s.style 761 } 762 763 if !dirty || style != lstyle { 764 // write out any data queued thus far 765 // because we are going to skip over some 766 // cells, or because we need to change styles 767 s.writeString(lx, ly, lstyle, wcs) 768 wcs = buf[0:0] 769 lstyle = Style(-1) 770 if !dirty { 771 continue 772 } 773 } 774 if x > s.w-width { 775 mainc = ' ' 776 combc = nil 777 width = 1 778 } 779 if len(wcs) == 0 { 780 lstyle = style 781 lx = x 782 ly = y 783 } 784 ra[0] = mainc 785 wcs = append(wcs, utf16.Encode(ra)...) 786 if len(combc) != 0 { 787 wcs = append(wcs, utf16.Encode(combc)...) 788 } 789 for dx := 0; dx < width; dx++ { 790 s.cells.SetDirty(x+dx, y, false) 791 } 792 x += width - 1 793 } 794 s.writeString(lx, ly, lstyle, wcs) 795 wcs = buf[0:0] 796 lstyle = Style(-1) 797 } 798} 799 800func (s *cScreen) Show() { 801 s.Lock() 802 if !s.fini { 803 s.hideCursor() 804 s.resize() 805 s.draw() 806 s.doCursor() 807 } 808 s.Unlock() 809} 810 811func (s *cScreen) Sync() { 812 s.Lock() 813 if !s.fini { 814 s.cells.Invalidate() 815 s.hideCursor() 816 s.resize() 817 s.draw() 818 s.doCursor() 819 } 820 s.Unlock() 821} 822 823type consoleInfo struct { 824 size coord 825 pos coord 826 attrs uint16 827 win rect 828 maxsz coord 829} 830 831func (s *cScreen) getConsoleInfo(info *consoleInfo) { 832 procGetConsoleScreenBufferInfo.Call( 833 uintptr(s.out), 834 uintptr(unsafe.Pointer(info))) 835} 836 837func (s *cScreen) getCursorInfo(info *cursorInfo) { 838 procGetConsoleCursorInfo.Call( 839 uintptr(s.out), 840 uintptr(unsafe.Pointer(info))) 841} 842 843func (s *cScreen) setCursorInfo(info *cursorInfo) { 844 procSetConsoleCursorInfo.Call( 845 uintptr(s.out), 846 uintptr(unsafe.Pointer(info))) 847} 848 849func (s *cScreen) setCursorPos(x, y int) { 850 procSetConsoleCursorPosition.Call( 851 uintptr(s.out), 852 coord{int16(x), int16(y)}.uintptr()) 853} 854 855func (s *cScreen) setBufferSize(x, y int) { 856 procSetConsoleScreenBufferSize.Call( 857 uintptr(s.out), 858 coord{int16(x), int16(y)}.uintptr()) 859} 860 861func (s *cScreen) Size() (int, int) { 862 s.Lock() 863 w, h := s.w, s.h 864 s.Unlock() 865 866 return w, h 867} 868 869func (s *cScreen) resize() { 870 info := consoleInfo{} 871 s.getConsoleInfo(&info) 872 873 w := int((info.win.right - info.win.left) + 1) 874 h := int((info.win.bottom - info.win.top) + 1) 875 876 if s.w == w && s.h == h { 877 return 878 } 879 880 s.cells.Resize(w, h) 881 s.w = w 882 s.h = h 883 884 s.setBufferSize(w, h) 885 886 r := rect{0, 0, int16(w - 1), int16(h - 1)} 887 procSetConsoleWindowInfo.Call( 888 uintptr(s.out), 889 uintptr(1), 890 uintptr(unsafe.Pointer(&r))) 891 892 s.PostEvent(NewEventResize(w, h)) 893} 894 895func (s *cScreen) Clear() { 896 s.Fill(' ', s.style) 897} 898 899func (s *cScreen) Fill(r rune, style Style) { 900 s.Lock() 901 if !s.fini { 902 s.cells.Fill(r, style) 903 s.clear = true 904 } 905 s.Unlock() 906} 907 908func (s *cScreen) clearScreen(style Style) { 909 pos := coord{0, 0} 910 attr := s.mapStyle(style) 911 x, y := s.w, s.h 912 scratch := uint32(0) 913 count := uint32(x * y) 914 915 procFillConsoleOutputAttribute.Call( 916 uintptr(s.out), 917 uintptr(attr), 918 uintptr(count), 919 pos.uintptr(), 920 uintptr(unsafe.Pointer(&scratch))) 921 procFillConsoleOutputCharacter.Call( 922 uintptr(s.out), 923 uintptr(' '), 924 uintptr(count), 925 pos.uintptr(), 926 uintptr(unsafe.Pointer(&scratch))) 927} 928 929const ( 930 modeExtndFlg uint32 = 0x0080 931 modeMouseEn uint32 = 0x0010 932 modeResizeEn uint32 = 0x0008 933 modeWrapEOL uint32 = 0x0002 934 modeCooked uint32 = 0x0001 935) 936 937func (s *cScreen) setInMode(mode uint32) error { 938 rv, _, err := procSetConsoleMode.Call( 939 uintptr(s.in), 940 uintptr(mode)) 941 if rv == 0 { 942 return err 943 } 944 return nil 945} 946 947func (s *cScreen) setOutMode(mode uint32) error { 948 rv, _, err := procSetConsoleMode.Call( 949 uintptr(s.out), 950 uintptr(mode)) 951 if rv == 0 { 952 return err 953 } 954 return nil 955} 956 957func (s *cScreen) getInMode(v *uint32) { 958 procGetConsoleMode.Call( 959 uintptr(s.in), 960 uintptr(unsafe.Pointer(v))) 961} 962 963func (s *cScreen) getOutMode(v *uint32) { 964 procGetConsoleMode.Call( 965 uintptr(s.out), 966 uintptr(unsafe.Pointer(v))) 967} 968 969func (s *cScreen) SetStyle(style Style) { 970 s.Lock() 971 s.style = style 972 s.Unlock() 973} 974 975// No fallback rune support, since we have Unicode. Yay! 976 977func (s *cScreen) RegisterRuneFallback(r rune, subst string) { 978} 979 980func (s *cScreen) UnregisterRuneFallback(r rune) { 981} 982 983func (s *cScreen) CanDisplay(r rune, checkFallbacks bool) bool { 984 // We presume we can display anything -- we're Unicode. 985 // (Sadly this not precisely true. Combinings are especially 986 // poorly supported under Windows.) 987 return true 988} 989 990func (s *cScreen) HasMouse() bool { 991 return true 992} 993 994func (s *cScreen) Resize(int, int, int, int) {} 995 996func (s *cScreen) HasKey(k Key) bool { 997 // Microsoft has codes for some keys, but they are unusual, 998 // so we don't include them. We include all the typical 999 // 101, 105 key layout keys. 1000 valid := map[Key]bool{ 1001 KeyBackspace: true, 1002 KeyTab: true, 1003 KeyEscape: true, 1004 KeyPause: true, 1005 KeyPrint: true, 1006 KeyPgUp: true, 1007 KeyPgDn: true, 1008 KeyEnter: true, 1009 KeyEnd: true, 1010 KeyHome: true, 1011 KeyLeft: true, 1012 KeyUp: true, 1013 KeyRight: true, 1014 KeyDown: true, 1015 KeyInsert: true, 1016 KeyDelete: true, 1017 KeyF1: true, 1018 KeyF2: true, 1019 KeyF3: true, 1020 KeyF4: true, 1021 KeyF5: true, 1022 KeyF6: true, 1023 KeyF7: true, 1024 KeyF8: true, 1025 KeyF9: true, 1026 KeyF10: true, 1027 KeyF11: true, 1028 KeyF12: true, 1029 KeyRune: true, 1030 } 1031 1032 return valid[k] 1033} 1034