input.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. package door
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. "syscall"
  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. // from: https://github.com/yubo/dea_ng
  35. // https://github.com/yubo/dea_ng/blob/master/go/src/directoryserver/streaming.go
  36. func set(fdSetPtr *syscall.FdSet, fd int) {
  37. (*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64)
  38. }
  39. func isSet(fdSetPtr *syscall.FdSet, fd int) bool {
  40. return ((*fdSetPtr).Bits[fd/64] & (1 << uint64(fd%64))) != 0
  41. }
  42. func clearAll(fdSetPtr *syscall.FdSet) {
  43. for index, _ := range (*fdSetPtr).Bits {
  44. (*fdSetPtr).Bits[index] = 0
  45. }
  46. }
  47. // Is there a key waiting?
  48. // Returns true on key, or if hungup/time out
  49. func (d *Door) HasKey() bool {
  50. if d.TimeLeft() < 0 {
  51. return true
  52. }
  53. var fdsetRead = syscall.FdSet{}
  54. clearAll(&fdsetRead)
  55. set(&fdsetRead, d.READFD)
  56. timeout := syscall.Timeval{Sec: 0, Usec: 1}
  57. v, _ := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  58. if v == -1 {
  59. return false
  60. }
  61. if v == 0 {
  62. return false
  63. }
  64. return true
  65. }
  66. // Low level read key function.
  67. // This gets the raw keys from the client, it doesn't handle extended keys,
  68. // functions, arrows.
  69. // Return key, or -1 (Timeout/No key available), -2 hangup
  70. func (d *Door) getch() int {
  71. var fdsetRead syscall.FdSet
  72. clearAll(&fdsetRead)
  73. set(&fdsetRead, d.READFD)
  74. // 100 Usec seems like a good value, works with QModem in dosbox.
  75. timeout := syscall.Timeval{Sec: 0, Usec: 100}
  76. v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  77. if v == -1 {
  78. // hangup
  79. return -2
  80. }
  81. if v == 0 {
  82. // timeout
  83. return -1
  84. }
  85. buffer := make([]byte, 1)
  86. r, err := syscall.Read(d.READFD, buffer)
  87. if r != 1 {
  88. fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
  89. // hangup
  90. return -2
  91. }
  92. return int(buffer[0])
  93. }
  94. func (d *Door) getkey_or_pushback() int {
  95. if d.pushback.Len() != 0 {
  96. e := d.pushback.Front()
  97. d.pushback.Remove(e)
  98. return e.Value.(int)
  99. }
  100. return d.getch()
  101. }
  102. // Return key received, or XKEY_* values.
  103. // -1 timeout/no key
  104. // -2 hangup
  105. // -3 out of time
  106. func (d *Door) GetKey() int {
  107. var c, c2 int
  108. if d.TimeLeft() < 0 {
  109. return -3
  110. }
  111. c = d.getkey_or_pushback()
  112. if c < 0 {
  113. return c
  114. }
  115. // We get 0x0d 0x00, or 0x0d 0x0a from syncterm.
  116. if c == 0x0d {
  117. c2 = d.getkey_or_pushback()
  118. if c2 > 0 {
  119. // wasn't an error
  120. if c2 != 0x00 && c2 != 0x0a {
  121. // wasn't 0x00 or 0x0a
  122. d.pushback.PushFront(c2)
  123. }
  124. }
  125. return c
  126. }
  127. if c == 0 {
  128. // possibly doorway mode
  129. tries := 0
  130. c2 = d.getkey_or_pushback()
  131. for c2 < 0 {
  132. if tries > 7 {
  133. return c
  134. }
  135. c2 = d.getkey_or_pushback()
  136. tries++
  137. }
  138. switch c2 {
  139. case 0x50:
  140. return XKEY_DOWN_ARROW
  141. case 0x48:
  142. return XKEY_UP_ARROW
  143. case 0x4b:
  144. return XKEY_LEFT_ARROW
  145. case 0x4d:
  146. return XKEY_RIGHT_ARROW
  147. case 0x47:
  148. return XKEY_HOME
  149. case 0x4f:
  150. return XKEY_END
  151. case 0x49:
  152. return XKEY_PGUP
  153. case 0x51:
  154. return XKEY_PGDN
  155. case 0x3b:
  156. return XKEY_F1
  157. case 0x3c:
  158. return XKEY_F2
  159. case 0x3d:
  160. return XKEY_F3
  161. case 0x3e:
  162. return XKEY_F4
  163. case 0x3f:
  164. return XKEY_F5
  165. case 0x40:
  166. return XKEY_F6
  167. case 0x41:
  168. return XKEY_F7
  169. case 0x42:
  170. return XKEY_F8
  171. case 0x43:
  172. return XKEY_F9
  173. case 0x44:
  174. return XKEY_F10
  175. /*
  176. case 0x45:
  177. return XKEY_F11
  178. case 0x46:
  179. return XKEY_F12
  180. */
  181. case 0x52:
  182. return XKEY_INSERT
  183. case 0x53:
  184. return XKEY_DELETE
  185. default:
  186. fmt.Printf("ERROR Doorway mode: 0x00 %x\n", c2)
  187. return XKEY_UNKNOWN
  188. }
  189. }
  190. if c == 0x1b {
  191. // Escape key?
  192. c2 = d.getkey_or_pushback()
  193. if c2 < 0 {
  194. // Just escape key
  195. return c
  196. }
  197. var extended string = string(c2)
  198. c2 = d.getkey_or_pushback()
  199. for c2 > 0 {
  200. if c2 == 0x1b {
  201. d.pushback.PushBack(c2)
  202. break
  203. }
  204. extended += string(c2)
  205. c2 = d.getkey_or_pushback()
  206. }
  207. switch extended {
  208. case "[A":
  209. return XKEY_UP_ARROW
  210. case "[B":
  211. return XKEY_DOWN_ARROW
  212. case "[C":
  213. return XKEY_RIGHT_ARROW
  214. case "[D":
  215. return XKEY_LEFT_ARROW
  216. case "[H":
  217. return XKEY_HOME
  218. case "[F":
  219. return XKEY_END // terminal
  220. case "[K":
  221. return XKEY_END
  222. case "[V":
  223. return XKEY_PGUP
  224. case "[U":
  225. return XKEY_PGDN
  226. case "[@":
  227. return XKEY_INSERT
  228. case "[1":
  229. // Syncterm is lost, could be F1..F5?
  230. fmt.Printf("ERROR (Syncterm) Extended %#v\n", extended)
  231. return XKEY_UNKNOWN
  232. case "[2~":
  233. return XKEY_INSERT // terminal
  234. case "[3~":
  235. return XKEY_DELETE // terminal
  236. case "[5~":
  237. return XKEY_PGUP // terminal
  238. case "[6~":
  239. return XKEY_PGDN // terminal
  240. case "[15~":
  241. return XKEY_F5 // terminal
  242. case "[17~":
  243. return XKEY_F6 // terminal
  244. case "[18~":
  245. return XKEY_F7 // terminal
  246. case "[19~":
  247. return XKEY_F8 // terminal
  248. case "[20~":
  249. return XKEY_F9 // terminal
  250. case "[21~":
  251. return XKEY_F10 // terminal
  252. case "[23~":
  253. return XKEY_F11
  254. case "[24~":
  255. return XKEY_F12 // terminal
  256. case "OP":
  257. return XKEY_F1
  258. case "OQ":
  259. return XKEY_F2
  260. case "OR":
  261. return XKEY_F3
  262. case "OS":
  263. return XKEY_F4
  264. case "Ot":
  265. return XKEY_F5 // syncterm
  266. default:
  267. fmt.Printf("ERROR Extended %#v\n", extended)
  268. return XKEY_UNKNOWN
  269. }
  270. }
  271. return c
  272. }
  273. func (d *Door) WaitKey(secs int64, usecs int64) int {
  274. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  275. var fdsetRead syscall.FdSet
  276. // fdsetWrite := syscall.FdSet
  277. clearAll(&fdsetRead)
  278. // clearAll(&fdsetWrite)
  279. // clearAll(&fdsete)
  280. set(&fdsetRead, d.READFD)
  281. // timeout := syscall.Timeval{Sec: 0, Usec: 100}
  282. timeout := syscall.Timeval{Sec: secs, Usec: usecs}
  283. v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  284. if v == -1 {
  285. fmt.Println("-1 : ", err)
  286. // hangup ?!
  287. return -2
  288. }
  289. if v == 0 {
  290. // timeout
  291. return -1
  292. }
  293. return d.GetKey()
  294. // var buffer []byte -- 0 byte buffer. doh!
  295. buffer := make([]byte, 1)
  296. r, err := syscall.Read(d.READFD, buffer)
  297. if r != 1 {
  298. fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
  299. // hangup
  300. return -2
  301. }
  302. return int(buffer[0])
  303. }
  304. // Input a string of max length.
  305. // This displays the input area if a bg color was set.
  306. // This handles timeout, input, backspace, and enter.
  307. func (d *Door) Input(max int) string {
  308. var line string
  309. // draw input area
  310. d.Write(strings.Repeat(" ", max) + strings.Repeat("\x08", max))
  311. var c int
  312. for true {
  313. c = d.WaitKey(120, 0)
  314. if c < 0 {
  315. // timeout/hangup
  316. return ""
  317. }
  318. if c > 1000 {
  319. continue
  320. }
  321. if strconv.IsPrint(rune(c)) {
  322. if len(line) < max {
  323. d.Write(string(c))
  324. line += string(c)
  325. } else {
  326. d.Write("\x07")
  327. }
  328. } else {
  329. // Non-print
  330. switch c {
  331. case 0x7f, 0x08:
  332. if len(line) > 0 {
  333. d.Write("\x08 \x08")
  334. line = line[:len(line)-1]
  335. }
  336. case 0x0d:
  337. return line
  338. }
  339. }
  340. }
  341. // this is never reached
  342. return line
  343. }