1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
|
package term
import (
"fmt"
"io"
"log"
"os"
"sync"
"unicode/utf16"
"golang.org/x/sys/windows"
"src.elv.sh/pkg/sys/ewindows"
"src.elv.sh/pkg/ui"
)
type reader struct {
console windows.Handle
stopEvent windows.Handle
// A mutex that is held during ReadEvent.
mutex sync.Mutex
}
// Creates a new Reader instance.
func newReader(file *os.File) Reader {
console, err := windows.GetStdHandle(windows.STD_INPUT_HANDLE)
if err != nil {
panic(fmt.Errorf("GetStdHandle(STD_INPUT_HANDLE): %v", err))
}
stopEvent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
panic(fmt.Errorf("CreateEvent: %v", err))
}
return &reader{console: console, stopEvent: stopEvent}
}
func (r *reader) ReadEvent() (Event, error) {
r.mutex.Lock()
defer r.mutex.Unlock()
handles := []windows.Handle{r.console, r.stopEvent}
var leadingSurrogate *surrogateKeyEvent
for {
triggered, _, err := ewindows.WaitForMultipleObjects(handles, false, ewindows.INFINITE)
if err != nil {
return nil, err
}
if triggered == 1 {
return nil, ErrStopped
}
var buf [1]ewindows.InputRecord
nr, err := ewindows.ReadConsoleInput(r.console, buf[:])
if nr == 0 {
return nil, io.ErrNoProgress
}
if err != nil {
return nil, err
}
event := convertEvent(buf[0].GetEvent())
if surrogate, ok := event.(surrogateKeyEvent); ok {
if leadingSurrogate == nil {
leadingSurrogate = &surrogate
// Keep reading the trailing surrogate.
continue
} else {
r := utf16.DecodeRune(leadingSurrogate.r, surrogate.r)
return KeyEvent{Rune: r}, nil
}
}
if event != nil {
return event, nil
}
// Got an event that should be ignored; keep going.
}
}
func (r *reader) ReadRawEvent() (Event, error) {
return r.ReadEvent()
}
func (r *reader) Close() {
err := windows.SetEvent(r.stopEvent)
if err != nil {
log.Println("SetEvent:", err)
}
r.mutex.Lock()
//lint:ignore SA2001 We only lock the mutex to make sure that ReadEvent has
//exited, so we unlock it immediately.
r.mutex.Unlock()
err = windows.CloseHandle(r.stopEvent)
if err != nil {
log.Println("Closing stopEvent handle for reader:", err)
}
}
// A subset of virtual key codes listed in
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
var keyCodeToRune = map[uint16]rune{
0x08: ui.Backspace, 0x09: ui.Tab,
0x0d: ui.Enter,
0x1b: '\x1b',
0x20: ' ',
0x23: ui.End, 0x24: ui.Home,
0x25: ui.Left, 0x26: ui.Up, 0x27: ui.Right, 0x28: ui.Down,
0x2d: ui.Insert, 0x2e: ui.Delete,
/* 0x30 - 0x39: digits, same with ASCII */
/* 0x41 - 0x5a: letters, same with ASCII */
/* 0x60 - 0x6f: numpads; currently ignored */
0x70: ui.F1, 0x71: ui.F2, 0x72: ui.F3, 0x73: ui.F4, 0x74: ui.F5, 0x75: ui.F6,
0x76: ui.F7, 0x77: ui.F8, 0x78: ui.F9, 0x79: ui.F10, 0x7a: ui.F11, 0x7b: ui.F12,
/* 0x7c - 0x87: F13 - F24; currently ignored */
0xba: ';', 0xbb: '=', 0xbc: ',', 0xbd: '-', 0xbe: '.', 0xbf: '/', 0xc0: '`',
0xdb: '[', 0xdc: '\\', 0xdd: ']', 0xde: '\'',
}
// A subset of constants listed in
// https://docs.microsoft.com/en-us/windows/console/key-event-record-str
const (
leftAlt = 0x02
leftCtrl = 0x08
rightAlt = 0x01
rightCtrl = 0x04
shift = 0x10
)
type surrogateKeyEvent struct{ r rune }
func (surrogateKeyEvent) isEvent() {}
// Converts the native ewindows.InputEvent type to a suitable Event type. It returns
// nil if the event should be ignored.
func convertEvent(event ewindows.InputEvent) Event {
switch event := event.(type) {
case *ewindows.KeyEvent:
if event.BKeyDown == 0 {
// Ignore keyup events.
return nil
}
r := rune(event.UChar[0]) + rune(event.UChar[1])<<8
filteredMod := event.DwControlKeyState & (leftAlt | leftCtrl | rightAlt | rightCtrl | shift)
if r >= 0x20 && r != 0x7f {
// This key inputs a character. The flags present in
// DwControlKeyState might indicate modifier keys that are needed to
// input this character (e.g. the Shift key when inputting 'A'), or
// modifier keys that are pressed in addition (e.g. the Alt key when
// pressing Alt-A). There doesn't seem to be an easy way to tell
// which is the case, so we rely on heuristics derived from
// real-world observations.
if filteredMod == 0 {
if utf16.IsSurrogate(r) {
return surrogateKeyEvent{r}
} else {
return KeyEvent(ui.Key{Rune: r})
}
} else if filteredMod == shift {
// A lone Shift seems to be always part of the character.
return KeyEvent(ui.Key{Rune: r})
} else if filteredMod == leftCtrl|rightAlt || filteredMod == leftCtrl|rightAlt|shift {
// The combination leftCtrl|rightAlt is used to represent AltGr.
// Furthermore, when the actual left Ctrl and right Alt are used
// together, the UChar field seems to be always 0; so if we are
// here, we can actually be sure that it's AltGr.
//
// Some characters require AltGr+Shift to input, such as the
// upper-case sharp S on a German keyboard.
return KeyEvent(ui.Key{Rune: r})
}
}
mod := convertMod(filteredMod)
if mod == 0 && event.WVirtualKeyCode == 0x1b {
// Special case: Normalize 0x1b to Ctrl-[.
//
// TODO(xiaq): This is Unix-centric. Maybe the normalized form
// should be Escape.
return KeyEvent(ui.Key{Rune: '[', Mod: ui.Ctrl})
}
r = convertRune(event.WVirtualKeyCode, mod)
if r == 0 {
return nil
}
return KeyEvent(ui.Key{Rune: r, Mod: mod})
default:
// Other events are ignored.
return nil
}
}
func convertRune(keyCode uint16, mod ui.Mod) rune {
r, ok := keyCodeToRune[keyCode]
if ok {
return r
}
if '0' <= keyCode && keyCode <= '9' {
return rune(keyCode)
}
if 'A' <= keyCode && keyCode <= 'Z' {
// If Ctrl is involved, emulate Unix's convention and use upper case;
// otherwise use lower case.
//
// TODO(xiaq): This is quite Unix-centric. Maybe we should make the
// base rune case-insensitive when there are modifiers involved.
if mod&ui.Ctrl != 0 {
return rune(keyCode)
}
return rune(keyCode - 'A' + 'a')
}
return 0
}
func convertMod(state uint32) ui.Mod {
mod := ui.Mod(0)
if state&(leftAlt|rightAlt) != 0 {
mod |= ui.Alt
}
if state&(leftCtrl|rightCtrl) != 0 {
mod |= ui.Ctrl
}
if state&shift != 0 {
mod |= ui.Shift
}
return mod
}
|