nt

A sensible note-taking program
git clone git://git.laack.co/nt.git
Log | Files | Refs | README

console_win.go (31003B)


      1 //go:build windows
      2 // +build windows
      3 
      4 // Copyright 2024 The TCell Authors
      5 //
      6 // Licensed under the Apache License, Version 2.0 (the "License");
      7 // you may not use file except in compliance with the License.
      8 // You may obtain a copy of the license at
      9 //
     10 //    http://www.apache.org/licenses/LICENSE-2.0
     11 //
     12 // Unless required by applicable law or agreed to in writing, software
     13 // distributed under the License is distributed on an "AS IS" BASIS,
     14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 // See the License for the specific language governing permissions and
     16 // limitations under the License.
     17 
     18 package tcell
     19 
     20 import (
     21 	"errors"
     22 	"fmt"
     23 	"os"
     24 	"strings"
     25 	"sync"
     26 	"syscall"
     27 	"unicode/utf16"
     28 	"unsafe"
     29 )
     30 
     31 type cScreen struct {
     32 	in         syscall.Handle
     33 	out        syscall.Handle
     34 	cancelflag syscall.Handle
     35 	scandone   chan struct{}
     36 	quit       chan struct{}
     37 	curx       int
     38 	cury       int
     39 	style      Style
     40 	fini       bool
     41 	vten       bool
     42 	truecolor  bool
     43 	running    bool
     44 	disableAlt bool // disable the alternate screen
     45 	title      string
     46 
     47 	w int
     48 	h int
     49 
     50 	oscreen     consoleInfo
     51 	ocursor     cursorInfo
     52 	cursorStyle CursorStyle
     53 	cursorColor Color
     54 	oimode      uint32
     55 	oomode      uint32
     56 	cells       CellBuffer
     57 	focusEnable bool
     58 
     59 	mouseEnabled bool
     60 	wg           sync.WaitGroup
     61 	eventQ       chan Event
     62 	stopQ        chan struct{}
     63 	finiOnce     sync.Once
     64 
     65 	sync.Mutex
     66 }
     67 
     68 var winLock sync.Mutex
     69 
     70 var winPalette = []Color{
     71 	ColorBlack,
     72 	ColorMaroon,
     73 	ColorGreen,
     74 	ColorNavy,
     75 	ColorOlive,
     76 	ColorPurple,
     77 	ColorTeal,
     78 	ColorSilver,
     79 	ColorGray,
     80 	ColorRed,
     81 	ColorLime,
     82 	ColorBlue,
     83 	ColorYellow,
     84 	ColorFuchsia,
     85 	ColorAqua,
     86 	ColorWhite,
     87 }
     88 
     89 var winColors = map[Color]Color{
     90 	ColorBlack:   ColorBlack,
     91 	ColorMaroon:  ColorMaroon,
     92 	ColorGreen:   ColorGreen,
     93 	ColorNavy:    ColorNavy,
     94 	ColorOlive:   ColorOlive,
     95 	ColorPurple:  ColorPurple,
     96 	ColorTeal:    ColorTeal,
     97 	ColorSilver:  ColorSilver,
     98 	ColorGray:    ColorGray,
     99 	ColorRed:     ColorRed,
    100 	ColorLime:    ColorLime,
    101 	ColorBlue:    ColorBlue,
    102 	ColorYellow:  ColorYellow,
    103 	ColorFuchsia: ColorFuchsia,
    104 	ColorAqua:    ColorAqua,
    105 	ColorWhite:   ColorWhite,
    106 }
    107 
    108 var (
    109 	k32 = syscall.NewLazyDLL("kernel32.dll")
    110 	u32 = syscall.NewLazyDLL("user32.dll")
    111 )
    112 
    113 // We have to bring in the kernel32 and user32 DLLs directly, so we can get
    114 // access to some system calls that the core Go API lacks.
    115 //
    116 // Note that Windows appends some functions with W to indicate that wide
    117 // characters (Unicode) are in use.  The documentation refers to them
    118 // without this suffix, as the resolution is made via preprocessor.
    119 var (
    120 	procReadConsoleInput            = k32.NewProc("ReadConsoleInputW")
    121 	procWaitForMultipleObjects      = k32.NewProc("WaitForMultipleObjects")
    122 	procCreateEvent                 = k32.NewProc("CreateEventW")
    123 	procSetEvent                    = k32.NewProc("SetEvent")
    124 	procGetConsoleCursorInfo        = k32.NewProc("GetConsoleCursorInfo")
    125 	procSetConsoleCursorInfo        = k32.NewProc("SetConsoleCursorInfo")
    126 	procSetConsoleCursorPosition    = k32.NewProc("SetConsoleCursorPosition")
    127 	procSetConsoleMode              = k32.NewProc("SetConsoleMode")
    128 	procGetConsoleMode              = k32.NewProc("GetConsoleMode")
    129 	procGetConsoleScreenBufferInfo  = k32.NewProc("GetConsoleScreenBufferInfo")
    130 	procFillConsoleOutputAttribute  = k32.NewProc("FillConsoleOutputAttribute")
    131 	procFillConsoleOutputCharacter  = k32.NewProc("FillConsoleOutputCharacterW")
    132 	procSetConsoleWindowInfo        = k32.NewProc("SetConsoleWindowInfo")
    133 	procSetConsoleScreenBufferSize  = k32.NewProc("SetConsoleScreenBufferSize")
    134 	procSetConsoleTextAttribute     = k32.NewProc("SetConsoleTextAttribute")
    135 	procGetLargestConsoleWindowSize = k32.NewProc("GetLargestConsoleWindowSize")
    136 	procMessageBeep                 = u32.NewProc("MessageBeep")
    137 )
    138 
    139 const (
    140 	w32Infinite    = ^uintptr(0)
    141 	w32WaitObject0 = uintptr(0)
    142 )
    143 
    144 const (
    145 	// VT100/XTerm escapes understood by the console
    146 	vtShowCursor              = "\x1b[?25h"
    147 	vtHideCursor              = "\x1b[?25l"
    148 	vtCursorPos               = "\x1b[%d;%dH" // Note that it is Y then X
    149 	vtSgr0                    = "\x1b[0m"
    150 	vtBold                    = "\x1b[1m"
    151 	vtUnderline               = "\x1b[4m"
    152 	vtBlink                   = "\x1b[5m" // Not sure if this is processed
    153 	vtReverse                 = "\x1b[7m"
    154 	vtSetFg                   = "\x1b[38;5;%dm"
    155 	vtSetBg                   = "\x1b[48;5;%dm"
    156 	vtSetFgRGB                = "\x1b[38;2;%d;%d;%dm" // RGB
    157 	vtSetBgRGB                = "\x1b[48;2;%d;%d;%dm" // RGB
    158 	vtCursorDefault           = "\x1b[0 q"
    159 	vtCursorBlinkingBlock     = "\x1b[1 q"
    160 	vtCursorSteadyBlock       = "\x1b[2 q"
    161 	vtCursorBlinkingUnderline = "\x1b[3 q"
    162 	vtCursorSteadyUnderline   = "\x1b[4 q"
    163 	vtCursorBlinkingBar       = "\x1b[5 q"
    164 	vtCursorSteadyBar         = "\x1b[6 q"
    165 	vtDisableAm               = "\x1b[?7l"
    166 	vtEnableAm                = "\x1b[?7h"
    167 	vtEnterCA                 = "\x1b[?1049h\x1b[22;0;0t"
    168 	vtExitCA                  = "\x1b[?1049l\x1b[23;0;0t"
    169 	vtDoubleUnderline         = "\x1b[4:2m"
    170 	vtCurlyUnderline          = "\x1b[4:3m"
    171 	vtDottedUnderline         = "\x1b[4:4m"
    172 	vtDashedUnderline         = "\x1b[4:5m"
    173 	vtUnderColor              = "\x1b[58:5:%dm"
    174 	vtUnderColorRGB           = "\x1b[58:2::%d:%d:%dm"
    175 	vtUnderColorReset         = "\x1b[59m"
    176 	vtEnterUrl                = "\x1b]8;%s;%s\x1b\\" // NB arg 1 is id, arg 2 is url
    177 	vtExitUrl                 = "\x1b]8;;\x1b\\"
    178 	vtCursorColorRGB          = "\x1b]12;#%02x%02x%02x\007"
    179 	vtCursorColorReset        = "\x1b]112\007"
    180 	vtSaveTitle               = "\x1b[22;2t"
    181 	vtRestoreTitle            = "\x1b[23;2t"
    182 	vtSetTitle                = "\x1b]2;%s\x1b\\"
    183 )
    184 
    185 var vtCursorStyles = map[CursorStyle]string{
    186 	CursorStyleDefault:           vtCursorDefault,
    187 	CursorStyleBlinkingBlock:     vtCursorBlinkingBlock,
    188 	CursorStyleSteadyBlock:       vtCursorSteadyBlock,
    189 	CursorStyleBlinkingUnderline: vtCursorBlinkingUnderline,
    190 	CursorStyleSteadyUnderline:   vtCursorSteadyUnderline,
    191 	CursorStyleBlinkingBar:       vtCursorBlinkingBar,
    192 	CursorStyleSteadyBar:         vtCursorSteadyBar,
    193 }
    194 
    195 // NewConsoleScreen returns a Screen for the Windows console associated
    196 // with the current process.  The Screen makes use of the Windows Console
    197 // API to display content and read events.
    198 func NewConsoleScreen() (Screen, error) {
    199 	return &baseScreen{screenImpl: &cScreen{}}, nil
    200 }
    201 
    202 func (s *cScreen) Init() error {
    203 	s.eventQ = make(chan Event, 10)
    204 	s.quit = make(chan struct{})
    205 	s.scandone = make(chan struct{})
    206 	in, e := syscall.Open("CONIN$", syscall.O_RDWR, 0)
    207 	if e != nil {
    208 		return e
    209 	}
    210 	s.in = in
    211 	out, e := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
    212 	if e != nil {
    213 		_ = syscall.Close(s.in)
    214 		return e
    215 	}
    216 	s.out = out
    217 
    218 	s.truecolor = true
    219 
    220 	// ConEmu handling of colors and scrolling when in VT output mode is extremely poor.
    221 	// The color palette will scroll even though characters do not, when
    222 	// emitting stuff for the last character.  In the future we might change this to
    223 	// look at specific versions of ConEmu if they fix the bug.
    224 	// We can also try disabling auto margin mode.
    225 	tryVt := true
    226 	if os.Getenv("ConEmuPID") != "" {
    227 		s.truecolor = false
    228 		tryVt = false
    229 	}
    230 	switch os.Getenv("TCELL_TRUECOLOR") {
    231 	case "disable":
    232 		s.truecolor = false
    233 	case "enable":
    234 		s.truecolor = true
    235 		tryVt = true
    236 	}
    237 
    238 	s.Lock()
    239 
    240 	s.curx = -1
    241 	s.cury = -1
    242 	s.style = StyleDefault
    243 	s.getCursorInfo(&s.ocursor)
    244 	s.getConsoleInfo(&s.oscreen)
    245 	s.getOutMode(&s.oomode)
    246 	s.getInMode(&s.oimode)
    247 	s.resize()
    248 
    249 	s.fini = false
    250 	s.setInMode(modeResizeEn | modeExtendFlg)
    251 
    252 	// If a user needs to force old style console, they may do so
    253 	// by setting TCELL_VTMODE to disable.  This is an undocumented safety net for now.
    254 	// It may be removed in the future.  (This mostly exists because of ConEmu.)
    255 	switch os.Getenv("TCELL_VTMODE") {
    256 	case "disable":
    257 		tryVt = false
    258 	case "enable":
    259 		tryVt = true
    260 	}
    261 	switch os.Getenv("TCELL_ALTSCREEN") {
    262 	case "enable":
    263 		s.disableAlt = false // also the default
    264 	case "disable":
    265 		s.disableAlt = true
    266 	}
    267 	if tryVt {
    268 		s.setOutMode(modeVtOutput | modeNoAutoNL | modeCookedOut | modeUnderline)
    269 		var om uint32
    270 		s.getOutMode(&om)
    271 		if om&modeVtOutput == modeVtOutput {
    272 			s.vten = true
    273 		} else {
    274 			s.truecolor = false
    275 			s.setOutMode(0)
    276 		}
    277 	} else {
    278 		s.setOutMode(0)
    279 	}
    280 
    281 	s.Unlock()
    282 
    283 	return s.engage()
    284 }
    285 
    286 func (s *cScreen) CharacterSet() string {
    287 	// We are always UTF-16LE on Windows
    288 	return "UTF-16LE"
    289 }
    290 
    291 func (s *cScreen) EnableMouse(...MouseFlags) {
    292 	s.Lock()
    293 	s.mouseEnabled = true
    294 	s.enableMouse(true)
    295 	s.Unlock()
    296 }
    297 
    298 func (s *cScreen) DisableMouse() {
    299 	s.Lock()
    300 	s.mouseEnabled = false
    301 	s.enableMouse(false)
    302 	s.Unlock()
    303 }
    304 
    305 func (s *cScreen) enableMouse(on bool) {
    306 	if on {
    307 		s.setInMode(modeResizeEn | modeMouseEn | modeExtendFlg)
    308 	} else {
    309 		s.setInMode(modeResizeEn | modeExtendFlg)
    310 	}
    311 }
    312 
    313 // Windows lacks bracketed paste (for now)
    314 
    315 func (s *cScreen) EnablePaste() {}
    316 
    317 func (s *cScreen) DisablePaste() {}
    318 
    319 func (s *cScreen) EnableFocus() {
    320 	s.Lock()
    321 	s.focusEnable = true
    322 	s.Unlock()
    323 }
    324 
    325 func (s *cScreen) DisableFocus() {
    326 	s.Lock()
    327 	s.focusEnable = false
    328 	s.Unlock()
    329 }
    330 
    331 func (s *cScreen) Fini() {
    332 	s.finiOnce.Do(func() {
    333 		close(s.quit)
    334 		s.disengage()
    335 	})
    336 }
    337 
    338 func (s *cScreen) disengage() {
    339 	s.Lock()
    340 	if !s.running {
    341 		s.Unlock()
    342 		return
    343 	}
    344 	s.running = false
    345 	stopQ := s.stopQ
    346 	_, _, _ = procSetEvent.Call(uintptr(s.cancelflag))
    347 	close(stopQ)
    348 	s.Unlock()
    349 
    350 	s.wg.Wait()
    351 
    352 	if s.vten {
    353 		s.emitVtString(vtCursorStyles[CursorStyleDefault])
    354 		s.emitVtString(vtCursorColorReset)
    355 		s.emitVtString(vtEnableAm)
    356 		if !s.disableAlt {
    357 			s.emitVtString(vtRestoreTitle)
    358 			s.emitVtString(vtExitCA)
    359 		}
    360 	} else if !s.disableAlt {
    361 		s.clearScreen(StyleDefault, s.vten)
    362 		s.setCursorPos(0, 0, false)
    363 	}
    364 	s.setCursorInfo(&s.ocursor)
    365 	s.setBufferSize(int(s.oscreen.size.x), int(s.oscreen.size.y))
    366 	s.setInMode(s.oimode)
    367 	s.setOutMode(s.oomode)
    368 	_, _, _ = procSetConsoleTextAttribute.Call(
    369 		uintptr(s.out),
    370 		uintptr(s.mapStyle(StyleDefault)))
    371 }
    372 
    373 func (s *cScreen) engage() error {
    374 	s.Lock()
    375 	defer s.Unlock()
    376 	if s.running {
    377 		return errors.New("already engaged")
    378 	}
    379 	s.stopQ = make(chan struct{})
    380 	cf, _, e := procCreateEvent.Call(
    381 		uintptr(0),
    382 		uintptr(1),
    383 		uintptr(0),
    384 		uintptr(0))
    385 	if cf == uintptr(0) {
    386 		return e
    387 	}
    388 	s.running = true
    389 	s.cancelflag = syscall.Handle(cf)
    390 	s.enableMouse(s.mouseEnabled)
    391 
    392 	if s.vten {
    393 		s.setOutMode(modeVtOutput | modeNoAutoNL | modeCookedOut | modeUnderline)
    394 		if !s.disableAlt {
    395 			s.emitVtString(vtSaveTitle)
    396 			s.emitVtString(vtEnterCA)
    397 		}
    398 		s.emitVtString(vtDisableAm)
    399 		if s.title != "" {
    400 			s.emitVtString(fmt.Sprintf(vtSetTitle, s.title))
    401 		}
    402 	} else {
    403 		s.setOutMode(0)
    404 	}
    405 
    406 	s.clearScreen(s.style, s.vten)
    407 	s.hideCursor()
    408 
    409 	s.cells.Invalidate()
    410 	s.hideCursor()
    411 	s.resize()
    412 	s.draw()
    413 	s.doCursor()
    414 
    415 	s.wg.Add(1)
    416 	go s.scanInput(s.stopQ)
    417 	return nil
    418 }
    419 
    420 type cursorInfo struct {
    421 	size    uint32
    422 	visible uint32
    423 }
    424 
    425 type coord struct {
    426 	x int16
    427 	y int16
    428 }
    429 
    430 func (c coord) uintptr() uintptr {
    431 	// little endian, put x first
    432 	return uintptr(c.x) | (uintptr(c.y) << 16)
    433 }
    434 
    435 type rect struct {
    436 	left   int16
    437 	top    int16
    438 	right  int16
    439 	bottom int16
    440 }
    441 
    442 func (s *cScreen) emitVtString(vs string) {
    443 	esc := utf16.Encode([]rune(vs))
    444 	_ = syscall.WriteConsole(s.out, &esc[0], uint32(len(esc)), nil, nil)
    445 }
    446 
    447 func (s *cScreen) showCursor() {
    448 	if s.vten {
    449 		s.emitVtString(vtShowCursor)
    450 		s.emitVtString(vtCursorStyles[s.cursorStyle])
    451 		if s.cursorColor == ColorReset {
    452 			s.emitVtString(vtCursorColorReset)
    453 		} else if s.cursorColor.Valid() {
    454 			r, g, b := s.cursorColor.RGB()
    455 			s.emitVtString(fmt.Sprintf(vtCursorColorRGB, r, g, b))
    456 		}
    457 	} else {
    458 		s.setCursorInfo(&cursorInfo{size: 100, visible: 1})
    459 	}
    460 }
    461 
    462 func (s *cScreen) hideCursor() {
    463 	if s.vten {
    464 		s.emitVtString(vtHideCursor)
    465 	} else {
    466 		s.setCursorInfo(&cursorInfo{size: 1, visible: 0})
    467 	}
    468 }
    469 
    470 func (s *cScreen) ShowCursor(x, y int) {
    471 	s.Lock()
    472 	if !s.fini {
    473 		s.curx = x
    474 		s.cury = y
    475 	}
    476 	s.doCursor()
    477 	s.Unlock()
    478 }
    479 
    480 func (s *cScreen) SetCursor(cs CursorStyle, cc Color) {
    481 	s.Lock()
    482 	if !s.fini {
    483 		if _, ok := vtCursorStyles[cs]; ok {
    484 			s.cursorStyle = cs
    485 			s.cursorColor = cc
    486 			s.doCursor()
    487 		}
    488 	}
    489 	s.Unlock()
    490 }
    491 
    492 func (s *cScreen) doCursor() {
    493 	x, y := s.curx, s.cury
    494 
    495 	if x < 0 || y < 0 || x >= s.w || y >= s.h {
    496 		s.hideCursor()
    497 	} else {
    498 		s.setCursorPos(x, y, s.vten)
    499 		s.showCursor()
    500 	}
    501 }
    502 
    503 func (s *cScreen) HideCursor() {
    504 	s.ShowCursor(-1, -1)
    505 }
    506 
    507 type inputRecord struct {
    508 	typ  uint16
    509 	_    uint16
    510 	data [16]byte
    511 }
    512 
    513 const (
    514 	keyEvent    uint16 = 1
    515 	mouseEvent  uint16 = 2
    516 	resizeEvent uint16 = 4
    517 	menuEvent   uint16 = 8 // don't use
    518 	focusEvent  uint16 = 16
    519 )
    520 
    521 type mouseRecord struct {
    522 	x     int16
    523 	y     int16
    524 	btns  uint32
    525 	mod   uint32
    526 	flags uint32
    527 }
    528 
    529 type focusRecord struct {
    530 	focused int32 // actually BOOL
    531 }
    532 
    533 const (
    534 	mouseHWheeled uint32 = 0x8
    535 	mouseVWheeled uint32 = 0x4
    536 	// mouseDoubleClick uint32 = 0x2
    537 	// mouseMoved       uint32 = 0x1
    538 )
    539 
    540 type resizeRecord struct {
    541 	x int16
    542 	y int16
    543 }
    544 
    545 type keyRecord struct {
    546 	isdown int32
    547 	repeat uint16
    548 	kcode  uint16
    549 	scode  uint16
    550 	ch     uint16
    551 	mod    uint32
    552 }
    553 
    554 const (
    555 	// Constants per Microsoft.  We don't put the modifiers
    556 	// here.
    557 	vkCancel = 0x03
    558 	vkBack   = 0x08 // Backspace
    559 	vkTab    = 0x09
    560 	vkClear  = 0x0c
    561 	vkReturn = 0x0d
    562 	vkPause  = 0x13
    563 	vkEscape = 0x1b
    564 	vkSpace  = 0x20
    565 	vkPrior  = 0x21 // PgUp
    566 	vkNext   = 0x22 // PgDn
    567 	vkEnd    = 0x23
    568 	vkHome   = 0x24
    569 	vkLeft   = 0x25
    570 	vkUp     = 0x26
    571 	vkRight  = 0x27
    572 	vkDown   = 0x28
    573 	vkPrint  = 0x2a
    574 	vkPrtScr = 0x2c
    575 	vkInsert = 0x2d
    576 	vkDelete = 0x2e
    577 	vkHelp   = 0x2f
    578 	vkF1     = 0x70
    579 	vkF2     = 0x71
    580 	vkF3     = 0x72
    581 	vkF4     = 0x73
    582 	vkF5     = 0x74
    583 	vkF6     = 0x75
    584 	vkF7     = 0x76
    585 	vkF8     = 0x77
    586 	vkF9     = 0x78
    587 	vkF10    = 0x79
    588 	vkF11    = 0x7a
    589 	vkF12    = 0x7b
    590 	vkF13    = 0x7c
    591 	vkF14    = 0x7d
    592 	vkF15    = 0x7e
    593 	vkF16    = 0x7f
    594 	vkF17    = 0x80
    595 	vkF18    = 0x81
    596 	vkF19    = 0x82
    597 	vkF20    = 0x83
    598 	vkF21    = 0x84
    599 	vkF22    = 0x85
    600 	vkF23    = 0x86
    601 	vkF24    = 0x87
    602 )
    603 
    604 var vkKeys = map[uint16]Key{
    605 	vkCancel: KeyCancel,
    606 	vkBack:   KeyBackspace,
    607 	vkTab:    KeyTab,
    608 	vkClear:  KeyClear,
    609 	vkPause:  KeyPause,
    610 	vkPrint:  KeyPrint,
    611 	vkPrtScr: KeyPrint,
    612 	vkPrior:  KeyPgUp,
    613 	vkNext:   KeyPgDn,
    614 	vkReturn: KeyEnter,
    615 	vkEnd:    KeyEnd,
    616 	vkHome:   KeyHome,
    617 	vkLeft:   KeyLeft,
    618 	vkUp:     KeyUp,
    619 	vkRight:  KeyRight,
    620 	vkDown:   KeyDown,
    621 	vkInsert: KeyInsert,
    622 	vkDelete: KeyDelete,
    623 	vkHelp:   KeyHelp,
    624 	vkEscape: KeyEscape,
    625 	vkSpace:  ' ',
    626 	vkF1:     KeyF1,
    627 	vkF2:     KeyF2,
    628 	vkF3:     KeyF3,
    629 	vkF4:     KeyF4,
    630 	vkF5:     KeyF5,
    631 	vkF6:     KeyF6,
    632 	vkF7:     KeyF7,
    633 	vkF8:     KeyF8,
    634 	vkF9:     KeyF9,
    635 	vkF10:    KeyF10,
    636 	vkF11:    KeyF11,
    637 	vkF12:    KeyF12,
    638 	vkF13:    KeyF13,
    639 	vkF14:    KeyF14,
    640 	vkF15:    KeyF15,
    641 	vkF16:    KeyF16,
    642 	vkF17:    KeyF17,
    643 	vkF18:    KeyF18,
    644 	vkF19:    KeyF19,
    645 	vkF20:    KeyF20,
    646 	vkF21:    KeyF21,
    647 	vkF22:    KeyF22,
    648 	vkF23:    KeyF23,
    649 	vkF24:    KeyF24,
    650 }
    651 
    652 // NB: All Windows platforms are little endian.  We assume this
    653 // never, ever change.  The following code is endian safe. and does
    654 // not use unsafe pointers.
    655 func getu32(v []byte) uint32 {
    656 	return uint32(v[0]) + (uint32(v[1]) << 8) + (uint32(v[2]) << 16) + (uint32(v[3]) << 24)
    657 }
    658 func geti32(v []byte) int32 {
    659 	return int32(getu32(v))
    660 }
    661 func getu16(v []byte) uint16 {
    662 	return uint16(v[0]) + (uint16(v[1]) << 8)
    663 }
    664 func geti16(v []byte) int16 {
    665 	return int16(getu16(v))
    666 }
    667 
    668 // Convert windows dwControlKeyState to modifier mask
    669 func mod2mask(cks uint32) ModMask {
    670 	mm := ModNone
    671 	// Left or right control
    672 	ctrl := (cks & (0x0008 | 0x0004)) != 0
    673 	// Left or right alt
    674 	alt := (cks & (0x0002 | 0x0001)) != 0
    675 	// Filter out ctrl+alt (it means AltGr)
    676 	if !(ctrl && alt) {
    677 		if ctrl {
    678 			mm |= ModCtrl
    679 		}
    680 		if alt {
    681 			mm |= ModAlt
    682 		}
    683 	}
    684 	// Any shift
    685 	if (cks & 0x0010) != 0 {
    686 		mm |= ModShift
    687 	}
    688 	return mm
    689 }
    690 
    691 func mrec2btns(mbtns, flags uint32) ButtonMask {
    692 	btns := ButtonNone
    693 	if mbtns&0x1 != 0 {
    694 		btns |= Button1
    695 	}
    696 	if mbtns&0x2 != 0 {
    697 		btns |= Button2
    698 	}
    699 	if mbtns&0x4 != 0 {
    700 		btns |= Button3
    701 	}
    702 	if mbtns&0x8 != 0 {
    703 		btns |= Button4
    704 	}
    705 	if mbtns&0x10 != 0 {
    706 		btns |= Button5
    707 	}
    708 	if mbtns&0x20 != 0 {
    709 		btns |= Button6
    710 	}
    711 	if mbtns&0x40 != 0 {
    712 		btns |= Button7
    713 	}
    714 	if mbtns&0x80 != 0 {
    715 		btns |= Button8
    716 	}
    717 
    718 	if flags&mouseVWheeled != 0 {
    719 		if mbtns&0x80000000 == 0 {
    720 			btns |= WheelUp
    721 		} else {
    722 			btns |= WheelDown
    723 		}
    724 	}
    725 	if flags&mouseHWheeled != 0 {
    726 		if mbtns&0x80000000 == 0 {
    727 			btns |= WheelRight
    728 		} else {
    729 			btns |= WheelLeft
    730 		}
    731 	}
    732 	return btns
    733 }
    734 
    735 func (s *cScreen) postEvent(ev Event) {
    736 	select {
    737 	case s.eventQ <- ev:
    738 	case <-s.quit:
    739 	}
    740 }
    741 
    742 func (s *cScreen) getConsoleInput() error {
    743 	// cancelFlag comes first as WaitForMultipleObjects returns the lowest index
    744 	// in the event that both events are signalled.
    745 	waitObjects := []syscall.Handle{s.cancelflag, s.in}
    746 	// As arrays are contiguous in memory, a pointer to the first object is the
    747 	// same as a pointer to the array itself.
    748 	pWaitObjects := unsafe.Pointer(&waitObjects[0])
    749 
    750 	rv, _, er := procWaitForMultipleObjects.Call(
    751 		uintptr(len(waitObjects)),
    752 		uintptr(pWaitObjects),
    753 		uintptr(0),
    754 		w32Infinite)
    755 	// WaitForMultipleObjects returns WAIT_OBJECT_0 + the index.
    756 	switch rv {
    757 	case w32WaitObject0: // s.cancelFlag
    758 		return errors.New("cancelled")
    759 	case w32WaitObject0 + 1: // s.in
    760 		rec := &inputRecord{}
    761 		var nrec int32
    762 		rv, _, er := procReadConsoleInput.Call(
    763 			uintptr(s.in),
    764 			uintptr(unsafe.Pointer(rec)),
    765 			uintptr(1),
    766 			uintptr(unsafe.Pointer(&nrec)))
    767 		if rv == 0 {
    768 			return er
    769 		}
    770 		if nrec != 1 {
    771 			return nil
    772 		}
    773 		switch rec.typ {
    774 		case keyEvent:
    775 			krec := &keyRecord{}
    776 			krec.isdown = geti32(rec.data[0:])
    777 			krec.repeat = getu16(rec.data[4:])
    778 			krec.kcode = getu16(rec.data[6:])
    779 			krec.scode = getu16(rec.data[8:])
    780 			krec.ch = getu16(rec.data[10:])
    781 			krec.mod = getu32(rec.data[12:])
    782 
    783 			if krec.isdown == 0 || krec.repeat < 1 {
    784 				// it's a key release event, ignore it
    785 				return nil
    786 			}
    787 			if krec.ch != 0 {
    788 				// synthesized key code
    789 				for krec.repeat > 0 {
    790 					// convert shift+tab to backtab
    791 					if mod2mask(krec.mod) == ModShift && krec.ch == vkTab {
    792 						s.postEvent(NewEventKey(KeyBacktab, 0, ModNone))
    793 					} else {
    794 						s.postEvent(NewEventKey(KeyRune, rune(krec.ch), mod2mask(krec.mod)))
    795 					}
    796 					krec.repeat--
    797 				}
    798 				return nil
    799 			}
    800 			key := KeyNUL // impossible on Windows
    801 			ok := false
    802 			if key, ok = vkKeys[krec.kcode]; !ok {
    803 				return nil
    804 			}
    805 			for krec.repeat > 0 {
    806 				s.postEvent(NewEventKey(key, rune(krec.ch), mod2mask(krec.mod)))
    807 				krec.repeat--
    808 			}
    809 
    810 		case mouseEvent:
    811 			var mrec mouseRecord
    812 			mrec.x = geti16(rec.data[0:])
    813 			mrec.y = geti16(rec.data[2:])
    814 			mrec.btns = getu32(rec.data[4:])
    815 			mrec.mod = getu32(rec.data[8:])
    816 			mrec.flags = getu32(rec.data[12:])
    817 			btns := mrec2btns(mrec.btns, mrec.flags)
    818 			// we ignore double click, events are delivered normally
    819 			s.postEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns, mod2mask(mrec.mod)))
    820 
    821 		case resizeEvent:
    822 			var rrec resizeRecord
    823 			rrec.x = geti16(rec.data[0:])
    824 			rrec.y = geti16(rec.data[2:])
    825 			s.postEvent(NewEventResize(int(rrec.x), int(rrec.y)))
    826 
    827 		case focusEvent:
    828 			var focus focusRecord
    829 			focus.focused = geti32(rec.data[0:])
    830 			s.Lock()
    831 			enabled := s.focusEnable
    832 			s.Unlock()
    833 			if enabled {
    834 				s.postEvent(NewEventFocus(focus.focused != 0))
    835 			}
    836 
    837 		default:
    838 		}
    839 	default:
    840 		return er
    841 	}
    842 
    843 	return nil
    844 }
    845 
    846 func (s *cScreen) scanInput(stopQ chan struct{}) {
    847 	defer s.wg.Done()
    848 	for {
    849 		select {
    850 		case <-stopQ:
    851 			return
    852 		default:
    853 		}
    854 		if e := s.getConsoleInput(); e != nil {
    855 			return
    856 		}
    857 	}
    858 }
    859 
    860 func (s *cScreen) Colors() int {
    861 	if s.vten {
    862 		return 1 << 24
    863 	}
    864 	// Windows console can display 8 colors, in either low or high intensity
    865 	return 16
    866 }
    867 
    868 var vgaColors = map[Color]uint16{
    869 	ColorBlack:   0,
    870 	ColorMaroon:  0x4,
    871 	ColorGreen:   0x2,
    872 	ColorNavy:    0x1,
    873 	ColorOlive:   0x6,
    874 	ColorPurple:  0x5,
    875 	ColorTeal:    0x3,
    876 	ColorSilver:  0x7,
    877 	ColorGrey:    0x8,
    878 	ColorRed:     0xc,
    879 	ColorLime:    0xa,
    880 	ColorBlue:    0x9,
    881 	ColorYellow:  0xe,
    882 	ColorFuchsia: 0xd,
    883 	ColorAqua:    0xb,
    884 	ColorWhite:   0xf,
    885 }
    886 
    887 // Windows uses RGB signals
    888 func mapColor2RGB(c Color) uint16 {
    889 	winLock.Lock()
    890 	if v, ok := winColors[c]; ok {
    891 		c = v
    892 	} else {
    893 		v = FindColor(c, winPalette)
    894 		winColors[c] = v
    895 		c = v
    896 	}
    897 	winLock.Unlock()
    898 
    899 	if vc, ok := vgaColors[c]; ok {
    900 		return vc
    901 	}
    902 	return 0
    903 }
    904 
    905 // Map a tcell style to Windows attributes
    906 func (s *cScreen) mapStyle(style Style) uint16 {
    907 	f, b, a := style.fg, style.bg, style.attrs
    908 	fa := s.oscreen.attrs & 0xf
    909 	ba := (s.oscreen.attrs) >> 4 & 0xf
    910 	if f != ColorDefault && f != ColorReset {
    911 		fa = mapColor2RGB(f)
    912 	}
    913 	if b != ColorDefault && b != ColorReset {
    914 		ba = mapColor2RGB(b)
    915 	}
    916 	var attr uint16
    917 	// We simulate reverse by doing the color swap ourselves.
    918 	// Apparently windows cannot really do this except in DBCS
    919 	// views.
    920 	if a&AttrReverse != 0 {
    921 		attr = ba
    922 		attr |= fa << 4
    923 	} else {
    924 		attr = fa
    925 		attr |= ba << 4
    926 	}
    927 	if a&AttrBold != 0 {
    928 		attr |= 0x8
    929 	}
    930 	if a&AttrDim != 0 {
    931 		attr &^= 0x8
    932 	}
    933 	if a&AttrUnderline != 0 {
    934 		// Best effort -- doesn't seem to work though.
    935 		attr |= 0x8000
    936 	}
    937 	// Blink is unsupported
    938 	return attr
    939 }
    940 
    941 func (s *cScreen) sendVtStyle(style Style) {
    942 	esc := &strings.Builder{}
    943 
    944 	fg, bg, attrs := style.fg, style.bg, style.attrs
    945 	us, uc := style.ulStyle, style.ulColor
    946 
    947 	esc.WriteString(vtSgr0)
    948 	if attrs&(AttrBold|AttrDim) == AttrBold {
    949 		esc.WriteString(vtBold)
    950 	}
    951 	if attrs&AttrBlink != 0 {
    952 		esc.WriteString(vtBlink)
    953 	}
    954 	if us != UnderlineStyleNone {
    955 		if uc == ColorReset {
    956 			esc.WriteString(vtUnderColorReset)
    957 		} else if uc.IsRGB() {
    958 			r, g, b := uc.RGB()
    959 			_, _ = fmt.Fprintf(esc, vtUnderColorRGB, int(r), int(g), int(b))
    960 		} else if uc.Valid() {
    961 			_, _ = fmt.Fprintf(esc, vtUnderColor, uc&0xff)
    962 		}
    963 
    964 		esc.WriteString(vtUnderline)
    965 		// legacy ConHost does not understand these but Terminal does
    966 		switch us {
    967 		case UnderlineStyleSolid:
    968 		case UnderlineStyleDouble:
    969 			esc.WriteString(vtDoubleUnderline)
    970 		case UnderlineStyleCurly:
    971 			esc.WriteString(vtCurlyUnderline)
    972 		case UnderlineStyleDotted:
    973 			esc.WriteString(vtDottedUnderline)
    974 		case UnderlineStyleDashed:
    975 			esc.WriteString(vtDashedUnderline)
    976 		}
    977 	}
    978 
    979 	if attrs&AttrReverse != 0 {
    980 		esc.WriteString(vtReverse)
    981 	}
    982 	if fg.IsRGB() {
    983 		r, g, b := fg.RGB()
    984 		_, _ = fmt.Fprintf(esc, vtSetFgRGB, r, g, b)
    985 	} else if fg.Valid() {
    986 		_, _ = fmt.Fprintf(esc, vtSetFg, fg&0xff)
    987 	}
    988 	if bg.IsRGB() {
    989 		r, g, b := bg.RGB()
    990 		_, _ = fmt.Fprintf(esc, vtSetBgRGB, r, g, b)
    991 	} else if bg.Valid() {
    992 		_, _ = fmt.Fprintf(esc, vtSetBg, bg&0xff)
    993 	}
    994 	// URL string can be long, so don't send it unless we really need to
    995 	if style.url != "" {
    996 		_, _ = fmt.Fprintf(esc, vtEnterUrl, style.urlId, style.url)
    997 	} else {
    998 		esc.WriteString(vtExitUrl)
    999 	}
   1000 
   1001 	s.emitVtString(esc.String())
   1002 }
   1003 
   1004 func (s *cScreen) writeString(x, y int, style Style, ch []uint16) {
   1005 	// we assume the caller has hidden the cursor
   1006 	if len(ch) == 0 {
   1007 		return
   1008 	}
   1009 	s.setCursorPos(x, y, s.vten)
   1010 
   1011 	if s.vten {
   1012 		s.sendVtStyle(style)
   1013 	} else {
   1014 		_, _, _ = procSetConsoleTextAttribute.Call(
   1015 			uintptr(s.out),
   1016 			uintptr(s.mapStyle(style)))
   1017 	}
   1018 	_ = syscall.WriteConsole(s.out, &ch[0], uint32(len(ch)), nil, nil)
   1019 }
   1020 
   1021 func (s *cScreen) draw() {
   1022 	// allocate a scratch line bit enough for no combining chars.
   1023 	// if you have combining characters, you may pay for extra allocations.
   1024 	buf := make([]uint16, 0, s.w)
   1025 	wcs := buf[:]
   1026 	lstyle := styleInvalid
   1027 
   1028 	lx, ly := -1, -1
   1029 	ra := make([]rune, 1)
   1030 
   1031 	for y := 0; y < s.h; y++ {
   1032 		for x := 0; x < s.w; x++ {
   1033 			mainc, combc, style, width := s.cells.GetContent(x, y)
   1034 			dirty := s.cells.Dirty(x, y)
   1035 			if style == StyleDefault {
   1036 				style = s.style
   1037 			}
   1038 
   1039 			if !dirty || style != lstyle {
   1040 				// write out any data queued thus far
   1041 				// because we are going to skip over some
   1042 				// cells, or because we need to change styles
   1043 				s.writeString(lx, ly, lstyle, wcs)
   1044 				wcs = buf[0:0]
   1045 				lstyle = StyleDefault
   1046 				if !dirty {
   1047 					continue
   1048 				}
   1049 			}
   1050 			if x > s.w-width {
   1051 				mainc = ' '
   1052 				combc = nil
   1053 				width = 1
   1054 			}
   1055 			if len(wcs) == 0 {
   1056 				lstyle = style
   1057 				lx = x
   1058 				ly = y
   1059 			}
   1060 			ra[0] = mainc
   1061 			wcs = append(wcs, utf16.Encode(ra)...)
   1062 			if len(combc) != 0 {
   1063 				wcs = append(wcs, utf16.Encode(combc)...)
   1064 			}
   1065 			for dx := 0; dx < width; dx++ {
   1066 				s.cells.SetDirty(x+dx, y, false)
   1067 			}
   1068 			x += width - 1
   1069 		}
   1070 		s.writeString(lx, ly, lstyle, wcs)
   1071 		wcs = buf[0:0]
   1072 		lstyle = styleInvalid
   1073 	}
   1074 }
   1075 
   1076 func (s *cScreen) Show() {
   1077 	s.Lock()
   1078 	if !s.fini {
   1079 		s.hideCursor()
   1080 		s.resize()
   1081 		s.draw()
   1082 		s.doCursor()
   1083 	}
   1084 	s.Unlock()
   1085 }
   1086 
   1087 func (s *cScreen) Sync() {
   1088 	s.Lock()
   1089 	if !s.fini {
   1090 		s.cells.Invalidate()
   1091 		s.hideCursor()
   1092 		s.resize()
   1093 		s.draw()
   1094 		s.doCursor()
   1095 	}
   1096 	s.Unlock()
   1097 }
   1098 
   1099 type consoleInfo struct {
   1100 	size  coord
   1101 	pos   coord
   1102 	attrs uint16
   1103 	win   rect
   1104 	maxsz coord
   1105 }
   1106 
   1107 func (s *cScreen) getConsoleInfo(info *consoleInfo) {
   1108 	_, _, _ = procGetConsoleScreenBufferInfo.Call(
   1109 		uintptr(s.out),
   1110 		uintptr(unsafe.Pointer(info)))
   1111 }
   1112 
   1113 func (s *cScreen) getCursorInfo(info *cursorInfo) {
   1114 	_, _, _ = procGetConsoleCursorInfo.Call(
   1115 		uintptr(s.out),
   1116 		uintptr(unsafe.Pointer(info)))
   1117 }
   1118 
   1119 func (s *cScreen) setCursorInfo(info *cursorInfo) {
   1120 	_, _, _ = procSetConsoleCursorInfo.Call(
   1121 		uintptr(s.out),
   1122 		uintptr(unsafe.Pointer(info)))
   1123 }
   1124 
   1125 func (s *cScreen) setCursorPos(x, y int, vtEnable bool) {
   1126 	if vtEnable {
   1127 		// Note that the string is Y first.  Origin is 1,1.
   1128 		s.emitVtString(fmt.Sprintf(vtCursorPos, y+1, x+1))
   1129 	} else {
   1130 		_, _, _ = procSetConsoleCursorPosition.Call(
   1131 			uintptr(s.out),
   1132 			coord{int16(x), int16(y)}.uintptr())
   1133 	}
   1134 }
   1135 
   1136 func (s *cScreen) setBufferSize(x, y int) {
   1137 	_, _, _ = procSetConsoleScreenBufferSize.Call(
   1138 		uintptr(s.out),
   1139 		coord{int16(x), int16(y)}.uintptr())
   1140 }
   1141 
   1142 func (s *cScreen) Size() (int, int) {
   1143 	s.Lock()
   1144 	w, h := s.w, s.h
   1145 	s.Unlock()
   1146 
   1147 	return w, h
   1148 }
   1149 
   1150 func (s *cScreen) SetSize(w, h int) {
   1151 	xy, _, _ := procGetLargestConsoleWindowSize.Call(uintptr(s.out))
   1152 
   1153 	// xy is little endian packed
   1154 	y := int(xy >> 16)
   1155 	x := int(xy & 0xffff)
   1156 
   1157 	if x == 0 || y == 0 {
   1158 		return
   1159 	}
   1160 
   1161 	// This is a hacky workaround for Windows Terminal.
   1162 	// Essentially Windows Terminal (Windows 11) does not support application
   1163 	// initiated resizing.  To detect this, we look for an extremely large size
   1164 	// for the maximum width.  If it is > 500, then this is almost certainly
   1165 	// Windows Terminal, and won't support this.  (Note that the legacy console
   1166 	// does support application resizing.)
   1167 	if x >= 500 {
   1168 		return
   1169 	}
   1170 
   1171 	s.setBufferSize(x, y)
   1172 	r := rect{0, 0, int16(w - 1), int16(h - 1)}
   1173 	_, _, _ = procSetConsoleWindowInfo.Call(
   1174 		uintptr(s.out),
   1175 		uintptr(1),
   1176 		uintptr(unsafe.Pointer(&r)))
   1177 
   1178 	s.resize()
   1179 }
   1180 
   1181 func (s *cScreen) resize() {
   1182 	info := consoleInfo{}
   1183 	s.getConsoleInfo(&info)
   1184 
   1185 	w := int((info.win.right - info.win.left) + 1)
   1186 	h := int((info.win.bottom - info.win.top) + 1)
   1187 
   1188 	if s.w == w && s.h == h {
   1189 		return
   1190 	}
   1191 
   1192 	s.cells.Resize(w, h)
   1193 	s.w = w
   1194 	s.h = h
   1195 
   1196 	s.setBufferSize(w, h)
   1197 
   1198 	r := rect{0, 0, int16(w - 1), int16(h - 1)}
   1199 	_, _, _ = procSetConsoleWindowInfo.Call(
   1200 		uintptr(s.out),
   1201 		uintptr(1),
   1202 		uintptr(unsafe.Pointer(&r)))
   1203 	select {
   1204 	case s.eventQ <- NewEventResize(w, h):
   1205 	default:
   1206 	}
   1207 }
   1208 
   1209 func (s *cScreen) clearScreen(style Style, vtEnable bool) {
   1210 	if vtEnable {
   1211 		s.sendVtStyle(style)
   1212 		row := strings.Repeat(" ", s.w)
   1213 		for y := 0; y < s.h; y++ {
   1214 			s.setCursorPos(0, y, vtEnable)
   1215 			s.emitVtString(row)
   1216 		}
   1217 		s.setCursorPos(0, 0, vtEnable)
   1218 
   1219 	} else {
   1220 		pos := coord{0, 0}
   1221 		attr := s.mapStyle(style)
   1222 		x, y := s.w, s.h
   1223 		scratch := uint32(0)
   1224 		count := uint32(x * y)
   1225 
   1226 		_, _, _ = procFillConsoleOutputAttribute.Call(
   1227 			uintptr(s.out),
   1228 			uintptr(attr),
   1229 			uintptr(count),
   1230 			pos.uintptr(),
   1231 			uintptr(unsafe.Pointer(&scratch)))
   1232 		_, _, _ = procFillConsoleOutputCharacter.Call(
   1233 			uintptr(s.out),
   1234 			uintptr(' '),
   1235 			uintptr(count),
   1236 			pos.uintptr(),
   1237 			uintptr(unsafe.Pointer(&scratch)))
   1238 	}
   1239 }
   1240 
   1241 const (
   1242 	// Input modes
   1243 	modeExtendFlg uint32 = 0x0080
   1244 	modeMouseEn          = 0x0010
   1245 	modeResizeEn         = 0x0008
   1246 	// modeCooked          = 0x0001
   1247 	// modeVtInput         = 0x0200
   1248 
   1249 	// Output modes
   1250 	modeCookedOut uint32 = 0x0001
   1251 	modeVtOutput         = 0x0004
   1252 	modeNoAutoNL         = 0x0008
   1253 	modeUnderline        = 0x0010 // ENABLE_LVB_GRID_WORLDWIDE, needed for underlines
   1254 	// modeWrapEOL          = 0x0002
   1255 )
   1256 
   1257 func (s *cScreen) setInMode(mode uint32) {
   1258 	_, _, _ = procSetConsoleMode.Call(
   1259 		uintptr(s.in),
   1260 		uintptr(mode))
   1261 }
   1262 
   1263 func (s *cScreen) setOutMode(mode uint32) {
   1264 	_, _, _ = procSetConsoleMode.Call(
   1265 		uintptr(s.out),
   1266 		uintptr(mode))
   1267 }
   1268 
   1269 func (s *cScreen) getInMode(v *uint32) {
   1270 	_, _, _ = procGetConsoleMode.Call(
   1271 		uintptr(s.in),
   1272 		uintptr(unsafe.Pointer(v)))
   1273 }
   1274 
   1275 func (s *cScreen) getOutMode(v *uint32) {
   1276 	_, _, _ = procGetConsoleMode.Call(
   1277 		uintptr(s.out),
   1278 		uintptr(unsafe.Pointer(v)))
   1279 }
   1280 
   1281 func (s *cScreen) SetStyle(style Style) {
   1282 	s.Lock()
   1283 	s.style = style
   1284 	s.Unlock()
   1285 }
   1286 
   1287 func (s *cScreen) SetTitle(title string) {
   1288 	s.Lock()
   1289 	s.title = title
   1290 	if s.vten {
   1291 		s.emitVtString(fmt.Sprintf(vtSetTitle, title))
   1292 	}
   1293 	s.Unlock()
   1294 }
   1295 
   1296 // No fallback rune support, since we have Unicode.  Yay!
   1297 
   1298 func (s *cScreen) RegisterRuneFallback(_ rune, _ string) {
   1299 }
   1300 
   1301 func (s *cScreen) UnregisterRuneFallback(_ rune) {
   1302 }
   1303 
   1304 func (s *cScreen) CanDisplay(_ rune, _ bool) bool {
   1305 	// We presume we can display anything -- we're Unicode.
   1306 	// (Sadly this not precisely true.  Combining characters are especially
   1307 	// poorly supported under Windows.)
   1308 	return true
   1309 }
   1310 
   1311 func (s *cScreen) HasMouse() bool {
   1312 	return true
   1313 }
   1314 
   1315 func (s *cScreen) SetClipboard(_ []byte) {
   1316 }
   1317 
   1318 func (s *cScreen) GetClipboard() {
   1319 }
   1320 
   1321 func (s *cScreen) Resize(int, int, int, int) {}
   1322 
   1323 func (s *cScreen) HasKey(k Key) bool {
   1324 	// Microsoft has codes for some keys, but they are unusual,
   1325 	// so we don't include them.  We include all the typical
   1326 	// 101, 105 key layout keys.
   1327 	valid := map[Key]bool{
   1328 		KeyBackspace: true,
   1329 		KeyTab:       true,
   1330 		KeyEscape:    true,
   1331 		KeyPause:     true,
   1332 		KeyPrint:     true,
   1333 		KeyPgUp:      true,
   1334 		KeyPgDn:      true,
   1335 		KeyEnter:     true,
   1336 		KeyEnd:       true,
   1337 		KeyHome:      true,
   1338 		KeyLeft:      true,
   1339 		KeyUp:        true,
   1340 		KeyRight:     true,
   1341 		KeyDown:      true,
   1342 		KeyInsert:    true,
   1343 		KeyDelete:    true,
   1344 		KeyF1:        true,
   1345 		KeyF2:        true,
   1346 		KeyF3:        true,
   1347 		KeyF4:        true,
   1348 		KeyF5:        true,
   1349 		KeyF6:        true,
   1350 		KeyF7:        true,
   1351 		KeyF8:        true,
   1352 		KeyF9:        true,
   1353 		KeyF10:       true,
   1354 		KeyF11:       true,
   1355 		KeyF12:       true,
   1356 		KeyRune:      true,
   1357 	}
   1358 
   1359 	return valid[k]
   1360 }
   1361 
   1362 func (s *cScreen) Beep() error {
   1363 	// A simple beep. If the sound card is not available, the sound is generated
   1364 	// using the speaker.
   1365 	//
   1366 	// Reference:
   1367 	// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebeep
   1368 	const simpleBeep = 0xffffffff
   1369 	if rv, _, err := procMessageBeep.Call(simpleBeep); rv == 0 {
   1370 		return err
   1371 	}
   1372 	return nil
   1373 }
   1374 
   1375 func (s *cScreen) Suspend() error {
   1376 	s.disengage()
   1377 	return nil
   1378 }
   1379 
   1380 func (s *cScreen) Resume() error {
   1381 	return s.engage()
   1382 }
   1383 
   1384 func (s *cScreen) Tty() (Tty, bool) {
   1385 	return nil, false
   1386 }
   1387 
   1388 func (s *cScreen) GetCells() *CellBuffer {
   1389 	return &s.cells
   1390 }
   1391 
   1392 func (s *cScreen) EventQ() chan Event {
   1393 	return s.eventQ
   1394 }
   1395 
   1396 func (s *cScreen) StopQ() <-chan struct{} {
   1397 	return s.quit
   1398 }