input_linux.go 7.8 KB

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