1
0
mirror of https://github.com/gdamore/tcell.git synced 2025-04-26 13:48:53 +08:00
tcell/console_win.go
Graham Clark 6d0b0fa74e On windows, don't change cursor position if about to hide cursor.
A gowid user reported that mouse movement in their gowid(tcell) application
was causing the console cursor to flicker on windows, jumping from the current
position in the console to the top left corner, and back again. In part, this
is an artifact of the way I render a gowid "canvas" - calling HideCursor()
unilaterally, then calling ShowCursor(x, y) if the canvas contains a logical
cursor that is within bounds. Because currently every mouse event causes a
gowid application redraw, on windows the effect is to cause the cursor to jump
to the top left briefly, disappear, then reappear in the previous
position. You can see it most easily by moving the mouse rapidly left and
right.

An upstream fix is for me to only call HideCursor() if I know I'm not going to
call ShowCursor(x, y). But the flicker can also be eliminated by removing the
call to setCursorPos(0, 0) in console_win.go before hiding the cursor. The
single call to showCursor() in console_win.go is preceded by setting cursor
coordinates explicitly, so I don't think anything depends on the assumption
that a hidden cursor has been set to position (0, 0) first.
2019-06-11 17:21:45 -07:00

1034 lines
21 KiB
Go

// +build windows
// Copyright 2019 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
"errors"
"sync"
"syscall"
"unicode/utf16"
"unsafe"
)
type cScreen struct {
in syscall.Handle
out syscall.Handle
cancelflag syscall.Handle
scandone chan struct{}
evch chan Event
quit chan struct{}
curx int
cury int
style Style
clear bool
fini bool
w int
h int
oscreen consoleInfo
ocursor cursorInfo
oimode uint32
oomode uint32
cells CellBuffer
colors map[Color]Color
sync.Mutex
}
var winLock sync.Mutex
var winPalette = []Color{
ColorBlack,
ColorMaroon,
ColorGreen,
ColorNavy,
ColorOlive,
ColorPurple,
ColorTeal,
ColorSilver,
ColorGray,
ColorRed,
ColorLime,
ColorBlue,
ColorYellow,
ColorFuchsia,
ColorAqua,
ColorWhite,
}
var winColors = map[Color]Color{
ColorBlack: ColorBlack,
ColorMaroon: ColorMaroon,
ColorGreen: ColorGreen,
ColorNavy: ColorNavy,
ColorOlive: ColorOlive,
ColorPurple: ColorPurple,
ColorTeal: ColorTeal,
ColorSilver: ColorSilver,
ColorGray: ColorGray,
ColorRed: ColorRed,
ColorLime: ColorLime,
ColorBlue: ColorBlue,
ColorYellow: ColorYellow,
ColorFuchsia: ColorFuchsia,
ColorAqua: ColorAqua,
ColorWhite: ColorWhite,
}
var k32 = syscall.NewLazyDLL("kernel32.dll")
// We have to bring in the kernel32.dll directly, so we can get access to some
// system calls that the core Go API lacks.
//
// Note that Windows appends some functions with W to indicate that wide
// characters (Unicode) are in use. The documentation refers to them
// without this suffix, as the resolution is made via preprocessor.
var (
procReadConsoleInput = k32.NewProc("ReadConsoleInputW")
procWaitForMultipleObjects = k32.NewProc("WaitForMultipleObjects")
procCreateEvent = k32.NewProc("CreateEventW")
procSetEvent = k32.NewProc("SetEvent")
procGetConsoleCursorInfo = k32.NewProc("GetConsoleCursorInfo")
procSetConsoleCursorInfo = k32.NewProc("SetConsoleCursorInfo")
procSetConsoleCursorPosition = k32.NewProc("SetConsoleCursorPosition")
procSetConsoleMode = k32.NewProc("SetConsoleMode")
procGetConsoleMode = k32.NewProc("GetConsoleMode")
procGetConsoleScreenBufferInfo = k32.NewProc("GetConsoleScreenBufferInfo")
procFillConsoleOutputAttribute = k32.NewProc("FillConsoleOutputAttribute")
procFillConsoleOutputCharacter = k32.NewProc("FillConsoleOutputCharacterW")
procSetConsoleWindowInfo = k32.NewProc("SetConsoleWindowInfo")
procSetConsoleScreenBufferSize = k32.NewProc("SetConsoleScreenBufferSize")
procSetConsoleTextAttribute = k32.NewProc("SetConsoleTextAttribute")
)
const (
w32Infinite = ^uintptr(0)
w32WaitObject0 = uintptr(0)
)
// NewConsoleScreen returns a Screen for the Windows console associated
// with the current process. The Screen makes use of the Windows Console
// API to display content and read events.
func NewConsoleScreen() (Screen, error) {
return &cScreen{}, nil
}
func (s *cScreen) Init() error {
s.evch = make(chan Event, 10)
s.quit = make(chan struct{})
s.scandone = make(chan struct{})
in, e := syscall.Open("CONIN$", syscall.O_RDWR, 0)
if e != nil {
return e
}
s.in = in
out, e := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
if e != nil {
syscall.Close(s.in)
return e
}
s.out = out
cf, _, e := procCreateEvent.Call(
uintptr(0),
uintptr(1),
uintptr(0),
uintptr(0))
if cf == uintptr(0) {
return e
}
s.cancelflag = syscall.Handle(cf)
s.Lock()
s.curx = -1
s.cury = -1
s.style = StyleDefault
s.getCursorInfo(&s.ocursor)
s.getConsoleInfo(&s.oscreen)
s.getOutMode(&s.oomode)
s.getInMode(&s.oimode)
s.resize()
s.fini = false
s.setInMode(modeResizeEn)
s.setOutMode(0)
s.clearScreen(s.style)
s.hideCursor()
s.Unlock()
go s.scanInput()
return nil
}
func (s *cScreen) CharacterSet() string {
// We are always UTF-16LE on Windows
return "UTF-16LE"
}
func (s *cScreen) EnableMouse() {
s.setInMode(modeResizeEn | modeMouseEn | modeExtndFlg)
}
func (s *cScreen) DisableMouse() {
s.setInMode(modeResizeEn)
}
func (s *cScreen) Fini() {
s.Lock()
s.style = StyleDefault
s.curx = -1
s.cury = -1
s.fini = true
s.Unlock()
s.setCursorInfo(&s.ocursor)
s.setInMode(s.oimode)
s.setOutMode(s.oomode)
s.setBufferSize(int(s.oscreen.size.x), int(s.oscreen.size.y))
s.clearScreen(StyleDefault)
s.setCursorPos(0, 0)
procSetConsoleTextAttribute.Call(
uintptr(s.out),
uintptr(s.mapStyle(StyleDefault)))
close(s.quit)
procSetEvent.Call(uintptr(s.cancelflag))
// Block until scanInput returns; this prevents a race condition on Win 8+
// which causes syscall.Close to block until another keypress is read.
<-s.scandone
syscall.Close(s.in)
syscall.Close(s.out)
}
func (s *cScreen) PostEventWait(ev Event) {
s.evch <- ev
}
func (s *cScreen) PostEvent(ev Event) error {
select {
case s.evch <- ev:
return nil
default:
return ErrEventQFull
}
}
func (s *cScreen) PollEvent() Event {
select {
case <-s.quit:
return nil
case ev := <-s.evch:
return ev
}
}
type cursorInfo struct {
size uint32
visible uint32
}
type coord struct {
x int16
y int16
}
func (c coord) uintptr() uintptr {
// little endian, put x first
return uintptr(c.x) | (uintptr(c.y) << 16)
}
type rect struct {
left int16
top int16
right int16
bottom int16
}
func (s *cScreen) showCursor() {
s.setCursorInfo(&cursorInfo{size: 100, visible: 1})
}
func (s *cScreen) hideCursor() {
s.setCursorInfo(&cursorInfo{size: 1, visible: 0})
}
func (s *cScreen) ShowCursor(x, y int) {
s.Lock()
if !s.fini {
s.curx = x
s.cury = y
}
s.doCursor()
s.Unlock()
}
func (s *cScreen) doCursor() {
x, y := s.curx, s.cury
if x < 0 || y < 0 || x >= s.w || y >= s.h {
s.hideCursor()
} else {
s.setCursorPos(x, y)
s.showCursor()
}
}
func (s *cScreen) HideCursor() {
s.ShowCursor(-1, -1)
}
type charInfo struct {
ch uint16
attr uint16
}
type inputRecord struct {
typ uint16
_ uint16
data [16]byte
}
const (
keyEvent uint16 = 1
mouseEvent uint16 = 2
resizeEvent uint16 = 4
menuEvent uint16 = 8 // don't use
focusEvent uint16 = 16 // don't use
)
type mouseRecord struct {
x int16
y int16
btns uint32
mod uint32
flags uint32
}
const (
mouseDoubleClick uint32 = 0x2
mouseHWheeled uint32 = 0x8
mouseVWheeled uint32 = 0x4
mouseMoved uint32 = 0x1
)
type resizeRecord struct {
x int16
y int16
}
type keyRecord struct {
isdown int32
repeat uint16
kcode uint16
scode uint16
ch uint16
mod uint32
}
const (
// Constants per Microsoft. We don't put the modifiers
// here.
vkCancel = 0x03
vkBack = 0x08 // Backspace
vkTab = 0x09
vkClear = 0x0c
vkReturn = 0x0d
vkPause = 0x13
vkEscape = 0x1b
vkSpace = 0x20
vkPrior = 0x21 // PgUp
vkNext = 0x22 // PgDn
vkEnd = 0x23
vkHome = 0x24
vkLeft = 0x25
vkUp = 0x26
vkRight = 0x27
vkDown = 0x28
vkPrint = 0x2a
vkPrtScr = 0x2c
vkInsert = 0x2d
vkDelete = 0x2e
vkHelp = 0x2f
vkF1 = 0x70
vkF2 = 0x71
vkF3 = 0x72
vkF4 = 0x73
vkF5 = 0x74
vkF6 = 0x75
vkF7 = 0x76
vkF8 = 0x77
vkF9 = 0x78
vkF10 = 0x79
vkF11 = 0x7a
vkF12 = 0x7b
vkF13 = 0x7c
vkF14 = 0x7d
vkF15 = 0x7e
vkF16 = 0x7f
vkF17 = 0x80
vkF18 = 0x81
vkF19 = 0x82
vkF20 = 0x83
vkF21 = 0x84
vkF22 = 0x85
vkF23 = 0x86
vkF24 = 0x87
)
var vkKeys = map[uint16]Key{
vkCancel: KeyCancel,
vkBack: KeyBackspace,
vkTab: KeyTab,
vkClear: KeyClear,
vkPause: KeyPause,
vkPrint: KeyPrint,
vkPrtScr: KeyPrint,
vkPrior: KeyPgUp,
vkNext: KeyPgDn,
vkReturn: KeyEnter,
vkEnd: KeyEnd,
vkHome: KeyHome,
vkLeft: KeyLeft,
vkUp: KeyUp,
vkRight: KeyRight,
vkDown: KeyDown,
vkInsert: KeyInsert,
vkDelete: KeyDelete,
vkHelp: KeyHelp,
vkF1: KeyF1,
vkF2: KeyF2,
vkF3: KeyF3,
vkF4: KeyF4,
vkF5: KeyF5,
vkF6: KeyF6,
vkF7: KeyF7,
vkF8: KeyF8,
vkF9: KeyF9,
vkF10: KeyF10,
vkF11: KeyF11,
vkF12: KeyF12,
vkF13: KeyF13,
vkF14: KeyF14,
vkF15: KeyF15,
vkF16: KeyF16,
vkF17: KeyF17,
vkF18: KeyF18,
vkF19: KeyF19,
vkF20: KeyF20,
vkF21: KeyF21,
vkF22: KeyF22,
vkF23: KeyF23,
vkF24: KeyF24,
}
// NB: All Windows platforms are little endian. We assume this
// never, ever change. The following code is endian safe. and does
// not use unsafe pointers.
func getu32(v []byte) uint32 {
return uint32(v[0]) + (uint32(v[1]) << 8) + (uint32(v[2]) << 16) + (uint32(v[3]) << 24)
}
func geti32(v []byte) int32 {
return int32(getu32(v))
}
func getu16(v []byte) uint16 {
return uint16(v[0]) + (uint16(v[1]) << 8)
}
func geti16(v []byte) int16 {
return int16(getu16(v))
}
// Convert windows dwControlKeyState to modifier mask
func mod2mask(cks uint32) ModMask {
mm := ModNone
// Left or right control
if (cks & (0x0008 | 0x0004)) != 0 {
mm |= ModCtrl
}
// Left or right alt
if (cks & (0x0002 | 0x0001)) != 0 {
mm |= ModAlt
}
// Any shift
if (cks & 0x0010) != 0 {
mm |= ModShift
}
return mm
}
func mrec2btns(mbtns, flags uint32) ButtonMask {
btns := ButtonNone
if mbtns&0x1 != 0 {
btns |= Button1
}
if mbtns&0x2 != 0 {
btns |= Button2
}
if mbtns&0x4 != 0 {
btns |= Button3
}
if mbtns&0x8 != 0 {
btns |= Button4
}
if mbtns&0x10 != 0 {
btns |= Button5
}
if mbtns&0x20 != 0 {
btns |= Button6
}
if mbtns&0x40 != 0 {
btns |= Button7
}
if mbtns&0x80 != 0 {
btns |= Button8
}
if flags&mouseVWheeled != 0 {
if mbtns&0x80000000 == 0 {
btns |= WheelUp
} else {
btns |= WheelDown
}
}
if flags&mouseHWheeled != 0 {
if mbtns&0x80000000 == 0 {
btns |= WheelRight
} else {
btns |= WheelLeft
}
}
return btns
}
func (s *cScreen) getConsoleInput() error {
// cancelFlag comes first as WaitForMultipleObjects returns the lowest index
// in the event that both events are signalled.
waitObjects := []syscall.Handle{s.cancelflag, s.in}
// As arrays are contiguous in memory, a pointer to the first object is the
// same as a pointer to the array itself.
pWaitObjects := unsafe.Pointer(&waitObjects[0])
rv, _, er := procWaitForMultipleObjects.Call(
uintptr(len(waitObjects)),
uintptr(pWaitObjects),
uintptr(0),
w32Infinite)
// WaitForMultipleObjects returns WAIT_OBJECT_0 + the index.
switch rv {
case w32WaitObject0: // s.cancelFlag
return errors.New("cancelled")
case w32WaitObject0 + 1: // s.in
rec := &inputRecord{}
var nrec int32
rv, _, er := procReadConsoleInput.Call(
uintptr(s.in),
uintptr(unsafe.Pointer(rec)),
uintptr(1),
uintptr(unsafe.Pointer(&nrec)))
if rv == 0 {
return er
}
if nrec != 1 {
return nil
}
switch rec.typ {
case keyEvent:
krec := &keyRecord{}
krec.isdown = geti32(rec.data[0:])
krec.repeat = getu16(rec.data[4:])
krec.kcode = getu16(rec.data[6:])
krec.scode = getu16(rec.data[8:])
krec.ch = getu16(rec.data[10:])
krec.mod = getu32(rec.data[12:])
if krec.isdown == 0 || krec.repeat < 1 {
// its a key release event, ignore it
return nil
}
if krec.ch != 0 {
// synthesized key code
for krec.repeat > 0 {
// convert shift+tab to backtab
if mod2mask(krec.mod) == ModShift && krec.ch == vkTab {
s.PostEvent(NewEventKey(KeyBacktab, 0,
ModNone))
} else {
s.PostEvent(NewEventKey(KeyRune, rune(krec.ch),
mod2mask(krec.mod)))
}
krec.repeat--
}
return nil
}
key := KeyNUL // impossible on Windows
ok := false
if key, ok = vkKeys[krec.kcode]; !ok {
return nil
}
for krec.repeat > 0 {
s.PostEvent(NewEventKey(key, rune(krec.ch),
mod2mask(krec.mod)))
krec.repeat--
}
case mouseEvent:
var mrec mouseRecord
mrec.x = geti16(rec.data[0:])
mrec.y = geti16(rec.data[2:])
mrec.btns = getu32(rec.data[4:])
mrec.mod = getu32(rec.data[8:])
mrec.flags = getu32(rec.data[12:])
btns := mrec2btns(mrec.btns, mrec.flags)
// we ignore double click, events are delivered normally
s.PostEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns,
mod2mask(mrec.mod)))
case resizeEvent:
var rrec resizeRecord
rrec.x = geti16(rec.data[0:])
rrec.y = geti16(rec.data[2:])
s.PostEvent(NewEventResize(int(rrec.x), int(rrec.y)))
default:
}
default:
return er
}
return nil
}
func (s *cScreen) scanInput() {
for {
if e := s.getConsoleInput(); e != nil {
close(s.scandone)
return
}
}
}
// Windows console can display 8 characters, in either low or high intensity
func (s *cScreen) Colors() int {
return 16
}
var vgaColors = map[Color]uint16{
ColorBlack: 0,
ColorMaroon: 0x4,
ColorGreen: 0x2,
ColorNavy: 0x1,
ColorOlive: 0x6,
ColorPurple: 0x5,
ColorTeal: 0x3,
ColorSilver: 0x7,
ColorGrey: 0x8,
ColorRed: 0xc,
ColorLime: 0xa,
ColorBlue: 0x9,
ColorYellow: 0xe,
ColorFuchsia: 0xd,
ColorAqua: 0xb,
ColorWhite: 0xf,
}
// Windows uses RGB signals
func mapColor2RGB(c Color) uint16 {
winLock.Lock()
if v, ok := winColors[c]; ok {
c = v
} else {
v = FindColor(c, winPalette)
winColors[c] = v
c = v
}
winLock.Unlock()
if vc, ok := vgaColors[c]; ok {
return vc
}
return 0
}
// Map a tcell style to Windows attributes
func (s *cScreen) mapStyle(style Style) uint16 {
f, b, a := style.Decompose()
fa := s.oscreen.attrs & 0xf
ba := (s.oscreen.attrs) >> 4 & 0xf
if f != ColorDefault {
fa = mapColor2RGB(f)
}
if b != ColorDefault {
ba = mapColor2RGB(b)
}
var attr uint16
// We simulate reverse by doing the color swap ourselves.
// Apparently windows cannot really do this except in DBCS
// views.
if a&AttrReverse != 0 {
attr = ba
attr |= (fa << 4)
} else {
attr = fa
attr |= (ba << 4)
}
if a&AttrBold != 0 {
attr |= 0x8
}
if a&AttrDim != 0 {
attr &^= 0x8
}
if a&AttrUnderline != 0 {
// Best effort -- doesn't seem to work though.
attr |= 0x8000
}
// Blink is unsupported
return attr
}
func (s *cScreen) SetCell(x, y int, style Style, ch ...rune) {
if len(ch) > 0 {
s.SetContent(x, y, ch[0], ch[1:], style)
} else {
s.SetContent(x, y, ' ', nil, style)
}
}
func (s *cScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) {
s.Lock()
if !s.fini {
s.cells.SetContent(x, y, mainc, combc, style)
}
s.Unlock()
}
func (s *cScreen) GetContent(x, y int) (rune, []rune, Style, int) {
s.Lock()
mainc, combc, style, width := s.cells.GetContent(x, y)
s.Unlock()
return mainc, combc, style, width
}
func (s *cScreen) writeString(x, y int, style Style, ch []uint16) {
// we assume the caller has hidden the cursor
if len(ch) == 0 {
return
}
nw := uint32(len(ch))
procSetConsoleTextAttribute.Call(
uintptr(s.out),
uintptr(s.mapStyle(style)))
s.setCursorPos(x, y)
syscall.WriteConsole(s.out, &ch[0], nw, &nw, nil)
}
func (s *cScreen) draw() {
// allocate a scratch line bit enough for no combining chars.
// if you have combining characters, you may pay for extra allocs.
if s.clear {
s.clearScreen(s.style)
s.clear = false
s.cells.Invalidate()
}
buf := make([]uint16, 0, s.w)
wcs := buf[:]
lstyle := Style(-1) // invalid attribute
lx, ly := -1, -1
ra := make([]rune, 1)
for y := 0; y < int(s.h); y++ {
for x := 0; x < int(s.w); x++ {
mainc, combc, style, width := s.cells.GetContent(x, y)
dirty := s.cells.Dirty(x, y)
if style == StyleDefault {
style = s.style
}
if !dirty || style != lstyle {
// write out any data queued thus far
// because we are going to skip over some
// cells, or because we need to change styles
s.writeString(lx, ly, lstyle, wcs)
wcs = buf[0:0]
lstyle = Style(-1)
if !dirty {
continue
}
}
if x > s.w-width {
mainc = ' '
combc = nil
width = 1
}
if len(wcs) == 0 {
lstyle = style
lx = x
ly = y
}
ra[0] = mainc
wcs = append(wcs, utf16.Encode(ra)...)
if len(combc) != 0 {
wcs = append(wcs, utf16.Encode(combc)...)
}
for dx := 0; dx < width; dx++ {
s.cells.SetDirty(x+dx, y, false)
}
x += width - 1
}
s.writeString(lx, ly, lstyle, wcs)
wcs = buf[0:0]
lstyle = Style(-1)
}
}
func (s *cScreen) Show() {
s.Lock()
if !s.fini {
s.hideCursor()
s.resize()
s.draw()
s.doCursor()
}
s.Unlock()
}
func (s *cScreen) Sync() {
s.Lock()
if !s.fini {
s.cells.Invalidate()
s.hideCursor()
s.resize()
s.draw()
s.doCursor()
}
s.Unlock()
}
type consoleInfo struct {
size coord
pos coord
attrs uint16
win rect
maxsz coord
}
func (s *cScreen) getConsoleInfo(info *consoleInfo) {
procGetConsoleScreenBufferInfo.Call(
uintptr(s.out),
uintptr(unsafe.Pointer(info)))
}
func (s *cScreen) getCursorInfo(info *cursorInfo) {
procGetConsoleCursorInfo.Call(
uintptr(s.out),
uintptr(unsafe.Pointer(info)))
}
func (s *cScreen) setCursorInfo(info *cursorInfo) {
procSetConsoleCursorInfo.Call(
uintptr(s.out),
uintptr(unsafe.Pointer(info)))
}
func (s *cScreen) setCursorPos(x, y int) {
procSetConsoleCursorPosition.Call(
uintptr(s.out),
coord{int16(x), int16(y)}.uintptr())
}
func (s *cScreen) setBufferSize(x, y int) {
procSetConsoleScreenBufferSize.Call(
uintptr(s.out),
coord{int16(x), int16(y)}.uintptr())
}
func (s *cScreen) Size() (int, int) {
s.Lock()
w, h := s.w, s.h
s.Unlock()
return w, h
}
func (s *cScreen) resize() {
info := consoleInfo{}
s.getConsoleInfo(&info)
w := int((info.win.right - info.win.left) + 1)
h := int((info.win.bottom - info.win.top) + 1)
if s.w == w && s.h == h {
return
}
s.cells.Resize(w, h)
s.w = w
s.h = h
s.setBufferSize(w, h)
r := rect{0, 0, int16(w - 1), int16(h - 1)}
procSetConsoleWindowInfo.Call(
uintptr(s.out),
uintptr(1),
uintptr(unsafe.Pointer(&r)))
s.PostEvent(NewEventResize(w, h))
}
func (s *cScreen) Clear() {
s.Fill(' ', s.style)
}
func (s *cScreen) Fill(r rune, style Style) {
s.Lock()
if !s.fini {
s.cells.Fill(r, style)
s.clear = true
}
s.Unlock()
}
func (s *cScreen) clearScreen(style Style) {
pos := coord{0, 0}
attr := s.mapStyle(style)
x, y := s.w, s.h
scratch := uint32(0)
count := uint32(x * y)
procFillConsoleOutputAttribute.Call(
uintptr(s.out),
uintptr(attr),
uintptr(count),
pos.uintptr(),
uintptr(unsafe.Pointer(&scratch)))
procFillConsoleOutputCharacter.Call(
uintptr(s.out),
uintptr(' '),
uintptr(count),
pos.uintptr(),
uintptr(unsafe.Pointer(&scratch)))
}
const (
modeExtndFlg uint32 = 0x0080
modeMouseEn uint32 = 0x0010
modeResizeEn uint32 = 0x0008
modeWrapEOL uint32 = 0x0002
modeCooked uint32 = 0x0001
)
func (s *cScreen) setInMode(mode uint32) error {
rv, _, err := procSetConsoleMode.Call(
uintptr(s.in),
uintptr(mode))
if rv == 0 {
return err
}
return nil
}
func (s *cScreen) setOutMode(mode uint32) error {
rv, _, err := procSetConsoleMode.Call(
uintptr(s.out),
uintptr(mode))
if rv == 0 {
return err
}
return nil
}
func (s *cScreen) getInMode(v *uint32) {
procGetConsoleMode.Call(
uintptr(s.in),
uintptr(unsafe.Pointer(v)))
}
func (s *cScreen) getOutMode(v *uint32) {
procGetConsoleMode.Call(
uintptr(s.out),
uintptr(unsafe.Pointer(v)))
}
func (s *cScreen) SetStyle(style Style) {
s.Lock()
s.style = style
s.Unlock()
}
// No fallback rune support, since we have Unicode. Yay!
func (s *cScreen) RegisterRuneFallback(r rune, subst string) {
}
func (s *cScreen) UnregisterRuneFallback(r rune) {
}
func (s *cScreen) CanDisplay(r rune, checkFallbacks bool) bool {
// We presume we can display anything -- we're Unicode.
// (Sadly this not precisely true. Combinings are especially
// poorly supported under Windows.)
return true
}
func (s *cScreen) HasMouse() bool {
return true
}
func (s *cScreen) Resize(int, int, int, int) {}
func (s *cScreen) HasKey(k Key) bool {
// Microsoft has codes for some keys, but they are unusual,
// so we don't include them. We include all the typical
// 101, 105 key layout keys.
valid := map[Key]bool{
KeyBackspace: true,
KeyTab: true,
KeyEscape: true,
KeyPause: true,
KeyPrint: true,
KeyPgUp: true,
KeyPgDn: true,
KeyEnter: true,
KeyEnd: true,
KeyHome: true,
KeyLeft: true,
KeyUp: true,
KeyRight: true,
KeyDown: true,
KeyInsert: true,
KeyDelete: true,
KeyF1: true,
KeyF2: true,
KeyF3: true,
KeyF4: true,
KeyF5: true,
KeyF6: true,
KeyF7: true,
KeyF8: true,
KeyF9: true,
KeyF10: true,
KeyF11: true,
KeyF12: true,
KeyRune: true,
}
return valid[k]
}