input.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. package door
  2. import (
  3. "log"
  4. "strconv"
  5. "strings"
  6. "time"
  7. )
  8. // This is the current list of Extended keys we support:
  9. const (
  10. XKEY_UP_ARROW = 0x1001
  11. XKEY_DOWN_ARROW = 0x1002
  12. XKEY_RIGHT_ARROW = 0x1003
  13. XKEY_LEFT_ARROW = 0x1004
  14. XKEY_HOME = 0x1010
  15. XKEY_END = 0x1011
  16. XKEY_PGUP = 0x1012
  17. XKEY_PGDN = 0x1023
  18. XKEY_INSERT = 0x1024
  19. XKEY_DELETE = 0x7f
  20. XKEY_F1 = 0x1021
  21. XKEY_F2 = 0x1022
  22. XKEY_F3 = 0x1023
  23. XKEY_F4 = 0x1024
  24. XKEY_F5 = 0x1025
  25. XKEY_F6 = 0x1026
  26. XKEY_F7 = 0x1027
  27. XKEY_F8 = 0x1028
  28. XKEY_F9 = 0x1029
  29. XKEY_F10 = 0x102a
  30. XKEY_F11 = 0x102b
  31. XKEY_F12 = 0x102c
  32. XKEY_UNKNOWN = 0x1111
  33. )
  34. // Low level read key function.
  35. // This gets the raw keys from the client, it doesn't handle extended keys,
  36. // functions, arrows.
  37. // Return key, or -1 (Timeout/No key available), -2 hangup
  38. func (d *Door) getch() int {
  39. select {
  40. case res, ok := <-d.readerChannel:
  41. if ok {
  42. return int(res)
  43. } else {
  44. d.Disconnected = true
  45. return -2
  46. }
  47. case <-time.After(time.Duration(100) * time.Millisecond):
  48. return -1
  49. }
  50. }
  51. func (d *Door) getkey_or_pushback() int {
  52. if !d.Pushback.Empty() {
  53. return d.Pushback.Pop()
  54. }
  55. if false {
  56. key := d.getch()
  57. log.Printf("%d / %X\n", key, key)
  58. return key
  59. } else {
  60. return d.getch()
  61. }
  62. }
  63. // Return key received, or XKEY_* values.
  64. // -1 timeout/no key
  65. // -2 hangup
  66. // -3 out of time
  67. func (d *Door) GetKey() int {
  68. var c, c2 int
  69. if d.Disconnected {
  70. return -2
  71. }
  72. if d.TimeLeft() < 0 {
  73. return -3
  74. }
  75. c = d.getkey_or_pushback()
  76. if c < 0 {
  77. return c
  78. }
  79. // We get 0x0d 0x00, or 0x0d 0x0a from syncterm.
  80. if c == 0x0d {
  81. c2 = d.getkey_or_pushback()
  82. if c2 > 0 {
  83. // wasn't an error
  84. if c2 != 0x00 && c2 != 0x0a {
  85. // wasn't 0x00 or 0x0a
  86. d.Pushback.Push(c2)
  87. // log.Printf("Push 0x0d trailer %d / %x\n", c2, c2)
  88. }
  89. }
  90. return c
  91. }
  92. if c == 0 {
  93. // possibly doorway mode
  94. tries := 0
  95. c2 = d.getkey_or_pushback()
  96. for c2 < 0 {
  97. if tries > 7 {
  98. return c
  99. }
  100. c2 = d.getkey_or_pushback()
  101. tries++
  102. }
  103. switch c2 {
  104. case 0x50:
  105. return XKEY_DOWN_ARROW
  106. case 0x48:
  107. return XKEY_UP_ARROW
  108. case 0x4b:
  109. return XKEY_LEFT_ARROW
  110. case 0x4d:
  111. return XKEY_RIGHT_ARROW
  112. case 0x47:
  113. return XKEY_HOME
  114. case 0x4f:
  115. return XKEY_END
  116. case 0x49:
  117. return XKEY_PGUP
  118. case 0x51:
  119. return XKEY_PGDN
  120. case 0x3b:
  121. return XKEY_F1
  122. case 0x3c:
  123. return XKEY_F2
  124. case 0x3d:
  125. return XKEY_F3
  126. case 0x3e:
  127. return XKEY_F4
  128. case 0x3f:
  129. return XKEY_F5
  130. case 0x40:
  131. return XKEY_F6
  132. case 0x41:
  133. return XKEY_F7
  134. case 0x42:
  135. return XKEY_F8
  136. case 0x43:
  137. return XKEY_F9
  138. case 0x44:
  139. return XKEY_F10
  140. /*
  141. case 0x45:
  142. return XKEY_F11
  143. case 0x46:
  144. return XKEY_F12
  145. */
  146. case 0x52:
  147. return XKEY_INSERT
  148. case 0x53:
  149. return XKEY_DELETE
  150. default:
  151. log.Printf("ERROR Doorway mode: 0x00 %x\n", c2)
  152. return XKEY_UNKNOWN
  153. }
  154. }
  155. if c == 0x1b {
  156. // Escape key?
  157. c2 = d.getkey_or_pushback()
  158. if c2 < 0 {
  159. // Just escape key
  160. return c
  161. }
  162. var extended string = string(byte(c2))
  163. c2 = d.getkey_or_pushback()
  164. for c2 > 0 {
  165. if c2 == 0x1b {
  166. d.Pushback.Push(c2)
  167. break
  168. }
  169. extended += string(byte(c2))
  170. c2 = d.getkey_or_pushback()
  171. }
  172. switch extended {
  173. case "[A":
  174. return XKEY_UP_ARROW
  175. case "[B":
  176. return XKEY_DOWN_ARROW
  177. case "[C":
  178. return XKEY_RIGHT_ARROW
  179. case "[D":
  180. return XKEY_LEFT_ARROW
  181. case "[H":
  182. return XKEY_HOME
  183. case "[F":
  184. return XKEY_END // terminal
  185. case "[K":
  186. return XKEY_END
  187. case "[V":
  188. return XKEY_PGUP
  189. case "[U":
  190. return XKEY_PGDN
  191. case "[@":
  192. return XKEY_INSERT
  193. case "[1":
  194. // Syncterm is lost, could be F1..F5?
  195. log.Printf("ERROR (Syncterm) Extended %#v\n", extended)
  196. return XKEY_UNKNOWN
  197. case "[2~":
  198. return XKEY_INSERT // terminal
  199. case "[3~":
  200. return XKEY_DELETE // terminal
  201. case "[5~":
  202. return XKEY_PGUP // terminal
  203. case "[6~":
  204. return XKEY_PGDN // terminal
  205. case "[15~":
  206. return XKEY_F5 // terminal
  207. case "[17~":
  208. return XKEY_F6 // terminal
  209. case "[18~":
  210. return XKEY_F7 // terminal
  211. case "[19~":
  212. return XKEY_F8 // terminal
  213. case "[20~":
  214. return XKEY_F9 // terminal
  215. case "[21~":
  216. return XKEY_F10 // terminal
  217. case "[23~":
  218. return XKEY_F11
  219. case "[24~":
  220. return XKEY_F12 // terminal
  221. case "OP":
  222. return XKEY_F1
  223. case "OQ":
  224. return XKEY_F2
  225. case "OR":
  226. return XKEY_F3
  227. case "OS":
  228. return XKEY_F4
  229. case "Ot":
  230. return XKEY_F5 // syncterm
  231. default:
  232. log.Printf("ERROR Extended %#v\n", extended)
  233. return XKEY_UNKNOWN
  234. }
  235. }
  236. return c
  237. }
  238. func (d *Door) Key() int {
  239. return d.WaitKey(Inactivity, 0)
  240. }
  241. // usecs = Microseconds
  242. func (d *Door) WaitKey(secs int64, usecs int64) int {
  243. if d.Disconnected {
  244. return -2
  245. }
  246. if !d.Pushback.Empty() {
  247. return d.GetKey()
  248. }
  249. timeout := time.Duration(secs)*time.Second + time.Duration(usecs)*time.Microsecond
  250. select {
  251. case res, ok := <-d.readerChannel:
  252. if ok {
  253. d.Pushback.Push(int(res))
  254. return d.GetKey()
  255. } else {
  256. d.Disconnected = true
  257. return -2
  258. }
  259. case <-time.After(timeout):
  260. return -1
  261. }
  262. }
  263. // Outputs spaces and backspaces
  264. // If you have set a background color, this shows the input area.
  265. func DisplayInput(max int) string {
  266. return strings.Repeat(" ", max) + strings.Repeat("\x08", max)
  267. }
  268. // Input a string of max length.
  269. // This displays the input area if a bg color was set.
  270. // This handles timeout, input, backspace, and enter.
  271. func (d *Door) Input(max int) string {
  272. var line string
  273. // draw input area
  274. d.Write(DisplayInput(max))
  275. var c int
  276. for {
  277. c = d.WaitKey(Inactivity, 0)
  278. if c < 0 {
  279. // timeout/hangup
  280. return ""
  281. }
  282. if c > 1000 {
  283. continue
  284. }
  285. if strconv.IsPrint(rune(c)) {
  286. if len(line) < max {
  287. d.Write(string(byte(c)))
  288. line += string(byte(c))
  289. } else {
  290. d.Write("\x07")
  291. }
  292. } else {
  293. // Non-print
  294. switch c {
  295. case 0x7f, 0x08:
  296. if len(line) > 0 {
  297. d.Write("\x08 \x08")
  298. line = line[:len(line)-1]
  299. }
  300. case 0x0d:
  301. return line
  302. }
  303. }
  304. }
  305. // this is never reached
  306. return line
  307. }