123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- package door
- import (
- "log"
- "strconv"
- "strings"
- "time"
- "unicode"
- )
- // This is neat, would be neater if it WORKED! go:generate stringer -type=Extended
- type Extended int8
- const (
- NOP Extended = iota
- UP_ARROW
- DOWN_ARROW
- RIGHT_ARROW
- LEFT_ARROW
- HOME
- END
- PAGE_UP
- PAGE_DOWN
- INSERT
- DELETE
- F1
- F2
- F3
- F4
- F5
- F6
- F7
- F8
- F9
- F10
- F11
- F12
- MOUSE
- CURSOR
- UNKNOWN
- )
- // To rebuild this, copy the above into it's own file and generate.
- // Then, copy the generated code to below:
- const _Extended_name = "NOPUP_ARROWDOWN_ARROWRIGHT_ARROWLEFT_ARROWHOMEENDPAGE_UPPAGE_DOWNINSERTDELETEF1F2F3F4F5F6F7F8F9F10F11F12MOUSECURSORUNKNOWN"
- var _Extended_index = [...]uint8{0, 3, 11, 21, 32, 42, 46, 49, 56, 65, 71, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 98, 101, 104, 109, 115, 122}
- func (i Extended) String() string {
- if i < 0 || i >= Extended(len(_Extended_index)-1) {
- return "Extended(" + strconv.FormatInt(int64(i), 10) + ")"
- }
- return _Extended_name[_Extended_index[i]:_Extended_index[i+1]]
- }
- // This is the current list of Extended keys we support:
- const (
- XKEY_UP_ARROW = 0x1001
- XKEY_DOWN_ARROW = 0x1002
- XKEY_RIGHT_ARROW = 0x1003
- XKEY_LEFT_ARROW = 0x1004
- XKEY_HOME = 0x1010
- XKEY_END = 0x1011
- XKEY_PGUP = 0x1012
- XKEY_PGDN = 0x1023
- XKEY_INSERT = 0x1024
- XKEY_DELETE = 0x7f
- XKEY_F1 = 0x1021
- XKEY_F2 = 0x1022
- XKEY_F3 = 0x1023
- XKEY_F4 = 0x1024
- XKEY_F5 = 0x1025
- XKEY_F6 = 0x1026
- XKEY_F7 = 0x1027
- XKEY_F8 = 0x1028
- XKEY_F9 = 0x1029
- XKEY_F10 = 0x102a
- XKEY_F11 = 0x102b
- XKEY_F12 = 0x102c
- XKEY_MOUSE = 0x1100
- XKEY_UNKNOWN = 0x1111
- )
- var extendedKeys map[string]int = map[string]int{
- "[A": XKEY_UP_ARROW,
- "[B": XKEY_DOWN_ARROW,
- "[C": XKEY_RIGHT_ARROW,
- "[D": XKEY_LEFT_ARROW,
- "[H": XKEY_HOME,
- "[F": XKEY_END, // terminal
- "[K": XKEY_END,
- "[V": XKEY_PGUP,
- "[U": XKEY_PGDN,
- "[@": XKEY_INSERT,
- "[2~": XKEY_INSERT, // terminal
- "[3~": XKEY_DELETE, // terminal
- "[5~": XKEY_PGUP, // terminal
- "[6~": XKEY_PGDN, // terminal
- "[15~": XKEY_F5, // terminal
- "[17~": XKEY_F6, // terminal
- "[18~": XKEY_F7, // terminal
- "[19~": XKEY_F8, // terminal
- "[20~": XKEY_F9, // terminal
- "[21~": XKEY_F10, // terminal
- "[23~": XKEY_F11,
- "[24~": XKEY_F12, // terminal
- "OP": XKEY_F1,
- "OQ": XKEY_F2,
- "OR": XKEY_F3,
- "OS": XKEY_F4,
- "Ot": XKEY_F5, // syncterm
- }
- // "[1": XKEY_UNKNOWN, // Syncterm is lost, could be F1..F5?
- // Low level read key function.
- // This gets the raw keys from the client, it doesn't handle extended keys,
- // functions, arrows.
- // Return key, or -1 (Timeout/No key available), -2 hangup
- func (d *Door) getch() int {
- select {
- case res, ok := <-d.readerChannel:
- if ok {
- return int(res)
- } else {
- d.Disconnected = true
- // atomic.StoreInt32(&d.Disconnected, 1)
- return -2
- }
- case <-time.After(time.Duration(100) * time.Millisecond):
- return -1
- }
- }
- func (d *Door) getkey_or_pushback() int {
- if !d.Pushback.Empty() {
- return d.Pushback.Pop()
- }
- if false {
- var key int = d.getch()
- log.Printf("%d / %X\n", key, key)
- return key
- } else {
- return d.getch()
- }
- }
- // Return key received, or XKEY_* values.
- // -1 timeout/no key
- // -2 hangup
- // -3 out of time
- func (d *Door) GetKey() int {
- var c, c2 int
- if d.Disconnect() {
- return -2
- }
- if d.TimeLeft() < 0 {
- return -3
- }
- c = d.getkey_or_pushback()
- if c < 0 {
- return c
- }
- // We get 0x0d 0x00, or 0x0d 0x0a from syncterm.
- if c == 0x0d {
- c2 = d.getkey_or_pushback()
- if c2 > 0 {
- // wasn't an error
- if c2 != 0x00 && c2 != 0x0a {
- // wasn't 0x00 or 0x0a
- d.Pushback.Push(c2)
- // log.Printf("Push 0x0d trailer %d / %x\n", c2, c2)
- }
- }
- return c
- }
- if c == 0 {
- // possibly doorway mode
- var tries int = 0
- c2 = d.getkey_or_pushback()
- for c2 < 0 {
- if tries > 7 {
- return c
- }
- c2 = d.getkey_or_pushback()
- tries++
- }
- switch c2 {
- case 0x50:
- return XKEY_DOWN_ARROW
- case 0x48:
- return XKEY_UP_ARROW
- case 0x4b:
- return XKEY_LEFT_ARROW
- case 0x4d:
- return XKEY_RIGHT_ARROW
- case 0x47:
- return XKEY_HOME
- case 0x4f:
- return XKEY_END
- case 0x49:
- return XKEY_PGUP
- case 0x51:
- return XKEY_PGDN
- case 0x3b:
- return XKEY_F1
- case 0x3c:
- return XKEY_F2
- case 0x3d:
- return XKEY_F3
- case 0x3e:
- return XKEY_F4
- case 0x3f:
- return XKEY_F5
- case 0x40:
- return XKEY_F6
- case 0x41:
- return XKEY_F7
- case 0x42:
- return XKEY_F8
- case 0x43:
- return XKEY_F9
- case 0x44:
- return XKEY_F10
- /*
- case 0x45:
- return XKEY_F11
- case 0x46:
- return XKEY_F12
- */
- case 0x52:
- return XKEY_INSERT
- case 0x53:
- return XKEY_DELETE
- default:
- log.Printf("ERROR Doorway mode: 0x00 %x\n", c2)
- return XKEY_UNKNOWN
- }
- }
- if c == 0x1b {
- // Escape key?
- c2 = d.getkey_or_pushback()
- if c2 < 0 {
- // Just escape key
- return c
- }
- var extended []byte = make([]byte, 1)
- extended[0] = byte(c2) // string = string(byte(c2))
- c2 = d.getkey_or_pushback()
- for c2 > 0 {
- if c2 == 0x1b {
- d.Pushback.Push(c2)
- break
- }
- extended = append(extended, byte(c2)) // += string(byte(c2))
- var has bool
- c2, has = extendedKeys[string(extended)]
- if has {
- // break out here if \x1b[ + letter or @
- // break out if \x1b[ + digits + ~
- // break out if \x1bO + letter
- return c2
- }
- c2 = d.getkey_or_pushback()
- }
- if strings.HasPrefix(string(extended), "[M") && len(extended) == 5 {
- // log.Printf("MOUSE Extended %#v\n", extended)
- var mouse Mouse = Mouse{Button: int8(extended[2]) - ' ' + 1,
- X: int8(extended[3]) - '!' + 1,
- Y: int8(extended[4]) - '!' + 1}
- d.AddMouse(mouse)
- log.Printf("MOUSE %d (%d,%d)\n", mouse.Button, mouse.X, mouse.Y)
- return XKEY_MOUSE
- }
- log.Printf("ERROR Extended %#v\n", extended)
- return XKEY_UNKNOWN
- }
- return c
- }
- func (d *Door) Key() int {
- return d.WaitKey(Inactivity, 0)
- }
- // usecs = Microseconds
- func (d *Door) WaitKey(secs int64, usecs int64) int {
- if d.Disconnect() {
- return -2
- }
- if !d.Pushback.Empty() {
- return d.GetKey()
- }
- var timeout time.Duration = time.Duration(secs)*time.Second + time.Duration(usecs)*time.Microsecond
- select {
- case res, ok := <-d.readerChannel:
- if ok {
- d.Pushback.Push(int(res))
- return d.GetKey()
- } else {
- // Reader Closed
- d.Disconnected = true
- // If I wrap this with if !d.WriterClosed .. races ?
- // Why can't I do this? This isn't a go routine...
- if !d.WriterClosed {
- d.writerChannel <- ""
- }
- // d.closeChannel <- struct{}{}
- // atomic.StoreInt32(&d.Disconnected, 1)
- return -2
- }
- case <-time.After(timeout):
- return -1
- }
- }
- // Outputs spaces and backspaces
- // If you have set a background color, this shows the input area.
- func DisplayInput(max int) string {
- return strings.Repeat(" ", max) + strings.Repeat("\x08", max)
- }
- // Input a string of max length.
- // This displays the input area if a bg color was set.
- // This handles timeout, input, backspace, and enter.
- func (d *Door) Input(max int) string {
- var line string
- // draw input area
- d.Write(DisplayInput(max))
- var c int
- for {
- c = d.WaitKey(Inactivity, 0)
- if c < 0 {
- // timeout/hangup
- return ""
- }
- if c > 1000 {
- continue
- }
- if strconv.IsPrint(rune(c)) {
- if len(line) < max {
- d.Write(string(byte(c)))
- line += string(byte(c))
- } else {
- d.Write("\x07")
- }
- } else {
- // Non-print
- switch c {
- case 0x7f, 0x08:
- if len(line) > 0 {
- d.Write("\x08 \x08")
- line = line[:len(line)-1]
- }
- case 0x0d:
- return line
- }
- }
- }
- }
- func (d *Door) GetOneOf(possible string) int {
- var c int
- for {
- c = d.WaitKey(Inactivity, 0)
- if c < 0 {
- return c
- }
- r := unicode.ToUpper(rune(c))
- if strings.ContainsRune(possible, r) {
- // return upper case rune
- return int(r)
- }
- /*
- c = strings.IndexRune(possible, r)
- if c != -1 {
- return c
- }
- */
- }
- }
|