input.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package door
  2. import (
  3. "fmt"
  4. "log"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "unicode"
  9. )
  10. // For debugging input reader routines.
  11. const DEBUG_INPUT = false
  12. var ErrInactivity error = fmt.Errorf("Inactivity")
  13. var ErrTimeout error = fmt.Errorf("Timeout")
  14. var ErrDisconnected error = fmt.Errorf("Disconnected")
  15. var DefaultTimeout time.Duration = time.Duration(60) * time.Second
  16. func (d *Door) WaitKey(timeout time.Duration) (rune, Extended, error) {
  17. /*
  18. d.readerMutex.Lock()
  19. if d.ReaderClosed {
  20. d.readerMutex.Unlock()
  21. return 0, NOP, ErrDisconnected
  22. }
  23. d.readerMutex.Unlock()
  24. */
  25. // Probably faster to just read from closed channel and get ok = false.
  26. select {
  27. case r, ok := <-d.readerChannel:
  28. if ok {
  29. if DEBUG_INPUT {
  30. log.Println("WaitKey:", r)
  31. }
  32. // return bio.GetKey()
  33. return r.R, r.Ex, r.Err
  34. } else {
  35. log.Println("WaitKey: Disconnected")
  36. // Reader has closed.
  37. // Disconnected = true
  38. return 0, NOP, ErrDisconnected
  39. }
  40. case <-time.After(timeout):
  41. return 0, NOP, ErrTimeout
  42. }
  43. }
  44. // Outputs spaces and backspaces
  45. // If you have set a background color, this shows the input area.
  46. func DisplayInput(max int) string {
  47. return strings.Repeat(" ", max) + strings.Repeat("\x08", max)
  48. }
  49. // Input a string of max length.
  50. // This displays the input area if a bg color was set.
  51. // This handles timeout, input, backspace, and enter.
  52. func (d *Door) Input(max int) string {
  53. var line []rune = make([]rune, 0, max)
  54. var length int
  55. // draw input area
  56. d.Write(DisplayInput(max))
  57. var r rune
  58. var ex Extended
  59. var err error
  60. for {
  61. r, ex, err = d.WaitKey(DefaultTimeout)
  62. if err != nil {
  63. // timeout/hangup
  64. return ""
  65. }
  66. if ex != NOP {
  67. continue
  68. }
  69. uw := UnicodeWidth(r)
  70. if strconv.IsPrint(r) {
  71. if length+uw <= max {
  72. d.Write(string(r))
  73. line = append(line, r)
  74. length += uw
  75. } else {
  76. d.Write("\x07")
  77. }
  78. } else {
  79. // Non-print
  80. switch r {
  81. case 0x7f, 0x08:
  82. if len(line) > 0 {
  83. d.Write("\x08 \x08")
  84. rlen := len(line)
  85. if UnicodeWidth(line[rlen-1]) == 2 {
  86. d.Write("\x08 \x08")
  87. length -= 2
  88. } else {
  89. length--
  90. }
  91. line = line[0 : rlen-1]
  92. }
  93. case 0x0d:
  94. return string(line)
  95. }
  96. }
  97. }
  98. }
  99. func (d *Door) GetOneOf(possible string) rune {
  100. var r rune
  101. var err error
  102. for {
  103. r, _, err = d.WaitKey(DefaultTimeout)
  104. if err != nil {
  105. return rune(0)
  106. }
  107. r := unicode.ToUpper(r)
  108. if strings.ContainsRune(possible, r) {
  109. // return upper case rune
  110. return r
  111. }
  112. }
  113. }