input.go 6.4 KB

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