|
@@ -37,6 +37,58 @@ const (
|
|
|
F12
|
|
|
MOUSE
|
|
|
CURSOR
|
|
|
+ ALT_a
|
|
|
+ ALT_b
|
|
|
+ ALT_c
|
|
|
+ ALT_d
|
|
|
+ ALT_e
|
|
|
+ ALT_f
|
|
|
+ ALT_g
|
|
|
+ ALT_h
|
|
|
+ ALT_i
|
|
|
+ ALT_j
|
|
|
+ ALT_k
|
|
|
+ ALT_l
|
|
|
+ ALT_m
|
|
|
+ ALT_n
|
|
|
+ ALT_o
|
|
|
+ ALT_p
|
|
|
+ ALT_q
|
|
|
+ ALT_r
|
|
|
+ ALT_s
|
|
|
+ ALT_t
|
|
|
+ ALT_u
|
|
|
+ ALT_v
|
|
|
+ ALT_w
|
|
|
+ ALT_x
|
|
|
+ ALT_y
|
|
|
+ ALT_z
|
|
|
+ ALT_A
|
|
|
+ ALT_B
|
|
|
+ ALT_C
|
|
|
+ ALT_D
|
|
|
+ ALT_E
|
|
|
+ ALT_F
|
|
|
+ ALT_G
|
|
|
+ ALT_H
|
|
|
+ ALT_I
|
|
|
+ ALT_J
|
|
|
+ ALT_K
|
|
|
+ ALT_L
|
|
|
+ ALT_M
|
|
|
+ ALT_N
|
|
|
+ ALT_O
|
|
|
+ ALT_P
|
|
|
+ ALT_Q
|
|
|
+ ALT_R
|
|
|
+ ALT_S
|
|
|
+ ALT_T
|
|
|
+ ALT_U
|
|
|
+ ALT_V
|
|
|
+ ALT_W
|
|
|
+ ALT_X
|
|
|
+ ALT_Y
|
|
|
+ ALT_Z
|
|
|
UNKNOWN
|
|
|
)
|
|
|
|
|
@@ -44,6 +96,12 @@ const (
|
|
|
|
|
|
const DEBUG_INPUT = true
|
|
|
|
|
|
+func extended_output(buffer []rune) string {
|
|
|
+ var output string = string(buffer)
|
|
|
+ output = strings.Replace(output, "\x1b", "^[", -1)
|
|
|
+ return output
|
|
|
+}
|
|
|
+
|
|
|
var ErrInactivity error = fmt.Errorf("Inactivity")
|
|
|
var ErrTimeout error = fmt.Errorf("Timeout")
|
|
|
var ErrDisconnected error = fmt.Errorf("Disconnected")
|
|
@@ -243,8 +301,24 @@ func (d *Door) GetKey() (rune, Extended, error) {
|
|
|
return r, ex, nil
|
|
|
}
|
|
|
|
|
|
- var extended []rune = make([]rune, 1)
|
|
|
- extended[0] = r2
|
|
|
+ if unicode.IsLetter(r2) {
|
|
|
+ // ALT-KEY
|
|
|
+ if unicode.IsLower(r2) {
|
|
|
+ // Lower case
|
|
|
+ ex = Extended(int(ALT_a) + int(r2-'a'))
|
|
|
+ return r, ex, nil
|
|
|
+ } else {
|
|
|
+ // Upper case
|
|
|
+ ex = Extended(int(ALT_A) + int(r2-'A'))
|
|
|
+ return r, ex, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var extended []rune = make([]rune, 0, 10)
|
|
|
+ extended = append(extended, r2)
|
|
|
+ // extended[0] = r2
|
|
|
+
|
|
|
+ var IsMouse bool = false
|
|
|
|
|
|
r2, err2 = d.ReadRunePushback()
|
|
|
for err2 == nil {
|
|
@@ -257,16 +331,30 @@ func (d *Door) GetKey() (rune, Extended, error) {
|
|
|
|
|
|
ext, has := extendedKeys[string(extended)]
|
|
|
if has {
|
|
|
+ // Found it! Return extended code.
|
|
|
return rune(0), ext, nil
|
|
|
}
|
|
|
|
|
|
- if unicode.IsLetter(r2) {
|
|
|
+ // Mouse codes can also contain letters.
|
|
|
+ if !IsMouse && unicode.IsLetter(r2) {
|
|
|
// The end of the extended code
|
|
|
+ // Unless this is Mouse!
|
|
|
+ if string(extended) == "[M" {
|
|
|
+ IsMouse = true
|
|
|
+ } else {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if IsMouse && len(extended) == 5 {
|
|
|
break
|
|
|
}
|
|
|
+
|
|
|
r2, err2 = d.ReadRunePushback()
|
|
|
}
|
|
|
|
|
|
+ log.Printf("Extended Code: [%s]", extended_output(extended))
|
|
|
+
|
|
|
var exString string = string(extended)
|
|
|
if strings.HasPrefix(exString, "[M") && len(extended) == 5 {
|
|
|
// Mouse Extended - input zero based (I add +1 to X, Y, and button)
|
|
@@ -338,223 +426,6 @@ func (d *Door) GetKey() (rune, Extended, error) {
|
|
|
|
|
|
// "[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 DOWN_ARROW
|
|
|
- case 0x48:
|
|
|
- return UP_ARROW
|
|
|
- case 0x4b:
|
|
|
- return LEFT_ARROW
|
|
|
- case 0x4d:
|
|
|
- return RIGHT_ARROW
|
|
|
- case 0x47:
|
|
|
- return HOME
|
|
|
- case 0x4f:
|
|
|
- return END
|
|
|
- case 0x49:
|
|
|
- return PAGE_UP
|
|
|
- case 0x51:
|
|
|
- return PAGE_DOWN
|
|
|
- case 0x3b:
|
|
|
- return F1
|
|
|
- case 0x3c:
|
|
|
- return F2
|
|
|
- case 0x3d:
|
|
|
- return F3
|
|
|
- case 0x3e:
|
|
|
- return F4
|
|
|
- case 0x3f:
|
|
|
- return F5
|
|
|
- case 0x40:
|
|
|
- return F6
|
|
|
- case 0x41:
|
|
|
- return F7
|
|
|
- case 0x42:
|
|
|
- return F8
|
|
|
- case 0x43:
|
|
|
- return F9
|
|
|
- case 0x44:
|
|
|
- return F10
|
|
|
- case 0x52:
|
|
|
- return INSERT
|
|
|
- case 0x53:
|
|
|
- return 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
|
|
|
- }
|
|
|
-}
|
|
|
-*/
|
|
|
-
|
|
|
// port over WaitKey
|
|
|
func (d *Door) WaitKey(timeout time.Duration) (rune, Extended, error) {
|
|
|
// disconnected test
|
|
@@ -612,7 +483,8 @@ func DisplayInput(max int) string {
|
|
|
// 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
|
|
|
+ var line []rune = make([]rune, 0, max)
|
|
|
+ var length int
|
|
|
|
|
|
// draw input area
|
|
|
d.Write(DisplayInput(max))
|
|
@@ -632,10 +504,13 @@ func (d *Door) Input(max int) string {
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
+ uw := UnicodeWidth(r)
|
|
|
+
|
|
|
if strconv.IsPrint(r) {
|
|
|
- if len(line) < max {
|
|
|
+ if length+uw <= max {
|
|
|
d.Write(string(r))
|
|
|
- line += string(r)
|
|
|
+ line = append(line, r)
|
|
|
+ length += uw
|
|
|
} else {
|
|
|
d.Write("\x07")
|
|
|
}
|
|
@@ -645,10 +520,17 @@ func (d *Door) Input(max int) string {
|
|
|
case 0x7f, 0x08:
|
|
|
if len(line) > 0 {
|
|
|
d.Write("\x08 \x08")
|
|
|
- line = line[:len(line)-1]
|
|
|
+ rlen := len(line)
|
|
|
+ if UnicodeWidth(line[rlen-1]) == 2 {
|
|
|
+ d.Write("\x08 \x08")
|
|
|
+ length -= 2
|
|
|
+ } else {
|
|
|
+ length--
|
|
|
+ }
|
|
|
+ line = line[0 : rlen-1]
|
|
|
}
|
|
|
case 0x0d:
|
|
|
- return line
|
|
|
+ return string(line)
|
|
|
}
|
|
|
}
|
|
|
}
|