input.go 7.1 KB

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