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