Browse Source

Added .Input, moved input funcs to input.go.

Steve Thielemann 3 years ago
parent
commit
72c1b080b4
3 changed files with 396 additions and 448 deletions
  1. 11 445
      door/door.go
  2. 375 0
      door/input.go
  3. 10 3
      testdoor/testdoor.go

+ 11 - 445
door/door.go

@@ -12,7 +12,13 @@ import (
 	"time"
 )
 
-const CRNL = "\r\n"
+const CRNL = "\r\n"         // BBS Line Ending
+var Reset string = Color(0) // ANSI Color Reset
+var Unicode bool            // Unicode support detected
+var CP437 bool              // CP437 support detected
+var Full_CP437 bool         // Full CP437 support detected (handles control codes properly)
+var Height int              // Screen height detected
+var Width int               // Screen width detected
 
 /*
 door32.sys:
@@ -30,8 +36,6 @@ g00r00                       Line 7 : User's handle/alias
 1                            Line 11: Current node number
 */
 
-var Reset string = Color(0)
-
 type DropfileConfig struct {
 	comm_type      int
 	comm_handle    int
@@ -99,37 +103,12 @@ func (d *Door) ReadDropfile(filename string) {
 	d.TimeOut = time.Now().Add(time.Duration(d.config.time_left) * time.Minute)
 }
 
-func (d *Door) HasKey() bool {
-	var fdsetRead = syscall.FdSet{}
-	clearAll(&fdsetRead)
-	set(&fdsetRead, d.READFD)
-	timeout := syscall.Timeval{Sec: 0, Usec: 1}
-	v, _ := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
-	if v == -1 {
-		return false
-	}
-	if v == 0 {
-		return false
-	}
-	return true
-}
-
-var Unicode bool    // Unicode support detected
-var CP437 bool      // CP437 support detected
-var Full_CP437 bool // Full CP437 support detected (handles control codes properly)
-var Height int      // Screen height detected
-var Width int       // Screen width detected
-
 // Detect client terminal capabilities, Unicode, CP437, Full_CP437,
 // screen Height and Width.
 func (d *Door) detect() {
-	// if d.config.comm_handle == 0 {
-	// d.Write("\377\375\042\377\373\001") // fix telnet client
-	// }
-	d.Write("\x1b[0;30;40m\x1b[2J\x1b[H") // black on black, clrscr, go home
-	d.Write("\x03\x04\x1b[6n")            // hearts and diamonds does CP437 work?
-
-	d.Write(CRNL + "\u2615\x1b[6n")
+	d.Write("\x1b[0;30;40m\x1b[2J\x1b[H")                          // black on black, clrscr, go home
+	d.Write("\x03\x04\x1b[6n")                                     // hearts and diamonds does CP437 work?
+	d.Write(CRNL + "\u2615\x1b[6n")                                // hot beverage
 	d.Write("\x1b[999C\x1b[999B\x1b[6n" + Reset + "\x1b[2J\x1b[H") // goto end of screen + cursor pos
 	// time.Sleep(50 * time.Millisecond)
 	time.Sleep(250 * time.Millisecond)
@@ -137,7 +116,7 @@ func (d *Door) detect() {
 	// telnet term isn't in RAW mode, so keys are buffer until <CR>
 	var results string
 
-	if true { // d.HasKey() {
+	if d.HasKey() {
 		buffer := make([]byte, 100)
 		r, err := syscall.Read(d.READFD, buffer)
 		results = string(buffer[:r])
@@ -174,7 +153,6 @@ func (d *Door) detect() {
 			pos = strings.Index(results, ";")
 			if pos != -1 {
 				height := results[:pos]
-				// Height, err = strconv.Atoi(results)
 				Height, err = strconv.Atoi(height)
 				pos++
 				results = results[pos:]
@@ -227,415 +205,3 @@ func (d *Door) Write(output string) {
 		fmt.Printf("Write fail: %d != %d\n", len(buffer), n)
 	}
 }
-
-/*
-func write(output string, config *DropfileConfig) {
-	buffer := []byte(output)
-	n, err := syscall.Write(config.comm_handle, buffer)
-	if err != nil {
-		fmt.Println("Write error/HANGUP?", n)
-	}
-}
-*/
-
-// from: https://github.com/yubo/dea_ng
-// https://github.com/yubo/dea_ng/blob/master/go/src/directoryserver/streaming.go
-
-func set(fdSetPtr *syscall.FdSet, fd int) {
-	(*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64)
-}
-
-func isSet(fdSetPtr *syscall.FdSet, fd int) bool {
-	return ((*fdSetPtr).Bits[fd/64] & (1 << uint64(fd%64))) != 0
-}
-
-func clearAll(fdSetPtr *syscall.FdSet) {
-	for index, _ := range (*fdSetPtr).Bits {
-		(*fdSetPtr).Bits[index] = 0
-	}
-}
-
-func (d *Door) SleepKey(sleep int64) int {
-	// var fdsetRead, fdsetWrite, fdsete syscall.FdSet
-	var fdsetRead syscall.FdSet
-	// fdsetWrite := syscall.FdSet
-	clearAll(&fdsetRead)
-	// clearAll(&fdsetWrite)
-	// clearAll(&fdsete)
-	set(&fdsetRead, d.READFD)
-	// timeout := syscall.Timeval{Sec: 0, Usec: 100}
-	timeout := syscall.Timeval{Sec: sleep, Usec: 0}
-	v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
-	if v == -1 {
-		fmt.Println("-1 : ", err)
-		// hangup ?!
-		return -2
-	}
-	if v == 0 {
-		// timeout
-		return -1
-	}
-	// var buffer []byte   -- 0 byte buffer.  doh!
-	buffer := make([]byte, 1)
-	r, err := syscall.Read(d.READFD, buffer)
-	if r != 1 {
-		fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
-		// hangup
-		return -2
-	}
-	return int(buffer[0])
-}
-
-// Low level read key function.
-// This gets the raw keys from the client, it doesn't handle extended keys,
-// functions, arrows.
-// Return key, or -1 (Timeout/No key available), -2 hangup
-func (d *Door) getch() int {
-	var fdsetRead syscall.FdSet
-	clearAll(&fdsetRead)
-	set(&fdsetRead, d.READFD)
-
-	// 100 Usec seems like a good value, works with QModem in dosbox.
-	timeout := syscall.Timeval{Sec: 0, Usec: 100}
-	v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
-	if v == -1 {
-		// hangup
-		return -2
-	}
-	if v == 0 {
-		// timeout
-		return -1
-	}
-
-	buffer := make([]byte, 1)
-	r, err := syscall.Read(d.READFD, buffer)
-	if r != 1 {
-		fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
-		// hangup
-		return -2
-	}
-	return int(buffer[0])
-}
-
-func (d *Door) getkey_or_pushback() int {
-	if d.pushback.Len() != 0 {
-		e := d.pushback.Front()
-		d.pushback.Remove(e)
-		return e.Value.(int)
-	}
-	return d.getch()
-}
-
-const (
-	XKEY_UP_ARROW    = 0x1001
-	XKEY_DOWN_ARROW  = 0x1002
-	XKEY_RIGHT_ARROW = 0x1003
-	XKEY_LEFT_ARROW  = 0x1004
-	XKEY_HOME        = 0x1010
-	XKEY_END         = 0x1011
-	XKEY_PGUP        = 0x1012
-	XKEY_PGDN        = 0x1023
-	XKEY_INSERT      = 0x1024
-	XKEY_DELETE      = 0x7f
-	XKEY_F1          = 0x1021
-	XKEY_F2          = 0x1022
-	XKEY_F3          = 0x1023
-	XKEY_F4          = 0x1024
-	XKEY_F5          = 0x1025
-	XKEY_F6          = 0x1026
-	XKEY_F7          = 0x1027
-	XKEY_F8          = 0x1028
-	XKEY_F9          = 0x1029
-	XKEY_F10         = 0x102a
-	XKEY_F11         = 0x102b
-	XKEY_F12         = 0x102c
-	XKEY_UNKNOWN     = 0x1111
-)
-
-// Return key received, or XKEY_* values.
-// -1 timeout/no key
-// -2 hangup
-// -3 out of time
-func (d *Door) Getkey() int {
-	var c, c2 int
-
-	if d.TimeLeft() < 0 {
-		return -3
-	}
-
-	c = d.getkey_or_pushback()
-
-	if c < 0 {
-		return c
-	}
-
-	// We get 0x0d 0x00, or 0x0d 0x0a from syncterm.
-	if c == 0x0d {
-		c2 = d.getkey_or_pushback()
-		if c2 > 0 {
-			// wasn't an error
-			if c2 != 0x00 && c2 != 0x0a {
-				// wasn't 0x00 or 0x0a
-				d.pushback.PushFront(c2)
-			}
-		}
-		return c
-	}
-
-	if c == 0 {
-		// possibly doorway mode
-		tries := 0
-		c2 = d.getkey_or_pushback()
-		for c2 < 0 {
-			if tries > 7 {
-				return c
-			}
-			c2 = d.getkey_or_pushback()
-			tries++
-		}
-
-		switch c2 {
-		case 0x50:
-			return XKEY_DOWN_ARROW
-		case 0x48:
-			return XKEY_UP_ARROW
-		case 0x4b:
-			return XKEY_LEFT_ARROW
-		case 0x4d:
-			return XKEY_RIGHT_ARROW
-		case 0x47:
-			return XKEY_HOME
-		case 0x4f:
-			return XKEY_END
-		case 0x49:
-			return XKEY_PGUP
-		case 0x51:
-			return XKEY_PGDN
-		case 0x3b:
-			return XKEY_F1
-		case 0x3c:
-			return XKEY_F2
-		case 0x3d:
-			return XKEY_F3
-		case 0x3e:
-			return XKEY_F4
-		case 0x3f:
-			return XKEY_F5
-		case 0x40:
-			return XKEY_F6
-		case 0x41:
-			return XKEY_F7
-		case 0x42:
-			return XKEY_F8
-		case 0x43:
-			return XKEY_F9
-		case 0x44:
-			return XKEY_F10
-		/*
-			case 0x45:
-				return XKEY_F11
-			case 0x46:
-				return XKEY_F12
-		*/
-		case 0x52:
-			return XKEY_INSERT
-		case 0x53:
-			return XKEY_DELETE
-		default:
-			fmt.Printf("ERROR Doorway mode: 0x00 %x\n", c2)
-			return XKEY_UNKNOWN
-		}
-	}
-
-	if c == 0x1b {
-		// Escape key?
-		c2 = d.getkey_or_pushback()
-		if c2 < 0 {
-			// Just escape key
-			return c
-		}
-		var extended string = string(c2)
-
-		c2 = d.getkey_or_pushback()
-		for c2 > 0 {
-			if c2 == 0x1b {
-				d.pushback.PushBack(c2)
-				break
-			}
-			extended += string(c2)
-			c2 = d.getkey_or_pushback()
-		}
-
-		switch extended {
-		case "[A":
-			return XKEY_UP_ARROW
-		case "[B":
-			return XKEY_DOWN_ARROW
-		case "[C":
-			return XKEY_RIGHT_ARROW
-		case "[D":
-			return XKEY_LEFT_ARROW
-		case "[H":
-			return XKEY_HOME
-		case "[F":
-			return XKEY_END // terminal
-		case "[K":
-			return XKEY_END
-		case "[V":
-			return XKEY_PGUP
-		case "[U":
-			return XKEY_PGDN
-		case "[@":
-			return XKEY_INSERT
-		case "[1":
-			// Syncterm is lost, could be F1..F5?
-			fmt.Printf("ERROR (Syncterm) Extended %#v\n", extended)
-			return XKEY_UNKNOWN
-		case "[2~":
-			return XKEY_INSERT // terminal
-		case "[3~":
-			return XKEY_DELETE // terminal
-		case "[5~":
-			return XKEY_PGUP // terminal
-		case "[6~":
-			return XKEY_PGDN // terminal
-		case "[15~":
-			return XKEY_F5 // terminal
-		case "[17~":
-			return XKEY_F6 // terminal
-		case "[18~":
-			return XKEY_F7 // terminal
-		case "[19~":
-			return XKEY_F8 // terminal
-		case "[20~":
-			return XKEY_F9 // terminal
-		case "[21~":
-			return XKEY_F10 // terminal
-		case "[23~":
-			return XKEY_F11
-		case "[24~":
-			return XKEY_F12 // terminal
-		case "OP":
-			return XKEY_F1
-		case "OQ":
-			return XKEY_F2
-		case "OR":
-			return XKEY_F3
-		case "OS":
-			return XKEY_F4
-		case "Ot":
-			return XKEY_F5 // syncterm
-		default:
-			fmt.Printf("ERROR Extended %#v\n", extended)
-			return XKEY_UNKNOWN
-		}
-
-	}
-
-	return c
-}
-
-func (d *Door) WaitKey(secs int64) int {
-	// var fdsetRead, fdsetWrite, fdsete syscall.FdSet
-	var fdsetRead syscall.FdSet
-	// fdsetWrite := syscall.FdSet
-	clearAll(&fdsetRead)
-	// clearAll(&fdsetWrite)
-	// clearAll(&fdsete)
-	set(&fdsetRead, d.READFD)
-	// timeout := syscall.Timeval{Sec: 0, Usec: 100}
-	timeout := syscall.Timeval{Sec: secs, Usec: 0}
-	v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
-	if v == -1 {
-		fmt.Println("-1 : ", err)
-		// hangup ?!
-		return -2
-	}
-	if v == 0 {
-		// timeout
-		return -1
-	}
-
-	return d.Getkey()
-
-	// var buffer []byte   -- 0 byte buffer.  doh!
-	buffer := make([]byte, 1)
-	r, err := syscall.Read(d.READFD, buffer)
-	if r != 1 {
-		fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
-		// hangup
-		return -2
-	}
-	return int(buffer[0])
-}
-
-/*
-func sleep_key(config *DropfileConfig, secs int) int {
-	// var fdsetRead, fdsetWrite, fdsete syscall.FdSet
-	var fdsetRead = syscall.FdSet{}
-	// fdsetWrite := syscall.FdSet
-	clearAll(&fdsetRead)
-	// clearAll(&fdsetWrite)
-	// clearAll(&fdsete)
-	set(&fdsetRead, config.comm_handle)
-	timeout := syscall.Timeval{Sec: int64(secs), Usec: 0}
-	// v, err := syscall.Select(config.comm_handle+1, &fdsetRead, &fdsetWrite, &fdsete, &timeout)
-	v, err := syscall.Select(config.comm_handle+1, &fdsetRead, nil, nil, &timeout)
-	fmt.Println("v:", v, "err:", err)
-	if v == -1 {
-		fmt.Println("-1 : ", err)
-		// hangup ?!
-		return -2
-	}
-	if v == 0 {
-		// timeout
-		return -1
-	}
-	// var buffer []byte
-	buffer := make([]byte, 1)
-	// var buffer [1]byte
-
-	r, err := syscall.Read(config.comm_handle, buffer)
-	if r != 1 {
-		fmt.Printf("Read said ready, but didn't read a character %d %v ?\n", r, err)
-		// hangup
-		return -2
-	}
-	return int(buffer[0])
-}
-*/
-
-/*
-func main() {
-	fmt.Println("doorgo")
-	var dropfile string
-
-	flag.StringVar(&dropfile, "dropfile", "", "Dropfile to use")
-	flag.Parse()
-
-	if len(dropfile) == 0 {
-		flag.PrintDefaults()
-		os.Exit(2)
-	}
-	fmt.Printf("Loading: %s\n", dropfile)
-
-	var config DropfileConfig
-	read_dropfile(dropfile, &config)
-
-	fmt.Printf("BBS %s, User %s / Handle %s\n", config.BBSID, config.real_name, config.handle)
-	message := "Welcome BBS User!\n\r"
-	// buffer := []byte(message)
-	// n, err := syscall.Write(config.comm_handle, buffer)
-	write(message, &config)
-
-	write("Press a key...", &config)
-	key := sleep_key(&config, 20)
-	write("\n\r", &config)
-	message = fmt.Sprintf("Key %d / %x\n\r", key, key)
-	write(message, &config)
-
-	write("\n\rReturning to BBS.\n\r", &config)
-	fmt.Println("Done.")
-	// fmt.Println(n, err)
-}
-*/

+ 375 - 0
door/input.go

@@ -0,0 +1,375 @@
+package door
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+	"syscall"
+)
+
+// from: https://github.com/yubo/dea_ng
+// https://github.com/yubo/dea_ng/blob/master/go/src/directoryserver/streaming.go
+
+func set(fdSetPtr *syscall.FdSet, fd int) {
+	(*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64)
+}
+
+func isSet(fdSetPtr *syscall.FdSet, fd int) bool {
+	return ((*fdSetPtr).Bits[fd/64] & (1 << uint64(fd%64))) != 0
+}
+
+func clearAll(fdSetPtr *syscall.FdSet) {
+	for index, _ := range (*fdSetPtr).Bits {
+		(*fdSetPtr).Bits[index] = 0
+	}
+}
+
+// Is there a key waiting?
+// Returns true on key, or if hungup/time out
+func (d *Door) HasKey() bool {
+	if d.TimeLeft() < 0 {
+		return true
+	}
+
+	var fdsetRead = syscall.FdSet{}
+	clearAll(&fdsetRead)
+	set(&fdsetRead, d.READFD)
+	timeout := syscall.Timeval{Sec: 0, Usec: 1}
+	v, _ := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
+	if v == -1 {
+		return false
+	}
+	if v == 0 {
+		return false
+	}
+	return true
+}
+
+// Low level read key function.
+// This gets the raw keys from the client, it doesn't handle extended keys,
+// functions, arrows.
+// Return key, or -1 (Timeout/No key available), -2 hangup
+func (d *Door) getch() int {
+	var fdsetRead syscall.FdSet
+	clearAll(&fdsetRead)
+	set(&fdsetRead, d.READFD)
+
+	// 100 Usec seems like a good value, works with QModem in dosbox.
+	timeout := syscall.Timeval{Sec: 0, Usec: 100}
+	v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
+	if v == -1 {
+		// hangup
+		return -2
+	}
+	if v == 0 {
+		// timeout
+		return -1
+	}
+
+	buffer := make([]byte, 1)
+	r, err := syscall.Read(d.READFD, buffer)
+	if r != 1 {
+		fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
+		// hangup
+		return -2
+	}
+	return int(buffer[0])
+}
+
+func (d *Door) getkey_or_pushback() int {
+	if d.pushback.Len() != 0 {
+		e := d.pushback.Front()
+		d.pushback.Remove(e)
+		return e.Value.(int)
+	}
+	return d.getch()
+}
+
+const (
+	XKEY_UP_ARROW    = 0x1001
+	XKEY_DOWN_ARROW  = 0x1002
+	XKEY_RIGHT_ARROW = 0x1003
+	XKEY_LEFT_ARROW  = 0x1004
+	XKEY_HOME        = 0x1010
+	XKEY_END         = 0x1011
+	XKEY_PGUP        = 0x1012
+	XKEY_PGDN        = 0x1023
+	XKEY_INSERT      = 0x1024
+	XKEY_DELETE      = 0x7f
+	XKEY_F1          = 0x1021
+	XKEY_F2          = 0x1022
+	XKEY_F3          = 0x1023
+	XKEY_F4          = 0x1024
+	XKEY_F5          = 0x1025
+	XKEY_F6          = 0x1026
+	XKEY_F7          = 0x1027
+	XKEY_F8          = 0x1028
+	XKEY_F9          = 0x1029
+	XKEY_F10         = 0x102a
+	XKEY_F11         = 0x102b
+	XKEY_F12         = 0x102c
+	XKEY_UNKNOWN     = 0x1111
+)
+
+// Return key received, or XKEY_* values.
+// -1 timeout/no key
+// -2 hangup
+// -3 out of time
+func (d *Door) GetKey() int {
+	var c, c2 int
+
+	if d.TimeLeft() < 0 {
+		return -3
+	}
+
+	c = d.getkey_or_pushback()
+
+	if c < 0 {
+		return c
+	}
+
+	// We get 0x0d 0x00, or 0x0d 0x0a from syncterm.
+	if c == 0x0d {
+		c2 = d.getkey_or_pushback()
+		if c2 > 0 {
+			// wasn't an error
+			if c2 != 0x00 && c2 != 0x0a {
+				// wasn't 0x00 or 0x0a
+				d.pushback.PushFront(c2)
+			}
+		}
+		return c
+	}
+
+	if c == 0 {
+		// possibly doorway mode
+		tries := 0
+		c2 = d.getkey_or_pushback()
+		for c2 < 0 {
+			if tries > 7 {
+				return c
+			}
+			c2 = d.getkey_or_pushback()
+			tries++
+		}
+
+		switch c2 {
+		case 0x50:
+			return XKEY_DOWN_ARROW
+		case 0x48:
+			return XKEY_UP_ARROW
+		case 0x4b:
+			return XKEY_LEFT_ARROW
+		case 0x4d:
+			return XKEY_RIGHT_ARROW
+		case 0x47:
+			return XKEY_HOME
+		case 0x4f:
+			return XKEY_END
+		case 0x49:
+			return XKEY_PGUP
+		case 0x51:
+			return XKEY_PGDN
+		case 0x3b:
+			return XKEY_F1
+		case 0x3c:
+			return XKEY_F2
+		case 0x3d:
+			return XKEY_F3
+		case 0x3e:
+			return XKEY_F4
+		case 0x3f:
+			return XKEY_F5
+		case 0x40:
+			return XKEY_F6
+		case 0x41:
+			return XKEY_F7
+		case 0x42:
+			return XKEY_F8
+		case 0x43:
+			return XKEY_F9
+		case 0x44:
+			return XKEY_F10
+		/*
+			case 0x45:
+				return XKEY_F11
+			case 0x46:
+				return XKEY_F12
+		*/
+		case 0x52:
+			return XKEY_INSERT
+		case 0x53:
+			return XKEY_DELETE
+		default:
+			fmt.Printf("ERROR Doorway mode: 0x00 %x\n", c2)
+			return XKEY_UNKNOWN
+		}
+	}
+
+	if c == 0x1b {
+		// Escape key?
+		c2 = d.getkey_or_pushback()
+		if c2 < 0 {
+			// Just escape key
+			return c
+		}
+		var extended string = string(c2)
+
+		c2 = d.getkey_or_pushback()
+		for c2 > 0 {
+			if c2 == 0x1b {
+				d.pushback.PushBack(c2)
+				break
+			}
+			extended += string(c2)
+			c2 = d.getkey_or_pushback()
+		}
+
+		switch extended {
+		case "[A":
+			return XKEY_UP_ARROW
+		case "[B":
+			return XKEY_DOWN_ARROW
+		case "[C":
+			return XKEY_RIGHT_ARROW
+		case "[D":
+			return XKEY_LEFT_ARROW
+		case "[H":
+			return XKEY_HOME
+		case "[F":
+			return XKEY_END // terminal
+		case "[K":
+			return XKEY_END
+		case "[V":
+			return XKEY_PGUP
+		case "[U":
+			return XKEY_PGDN
+		case "[@":
+			return XKEY_INSERT
+		case "[1":
+			// Syncterm is lost, could be F1..F5?
+			fmt.Printf("ERROR (Syncterm) Extended %#v\n", extended)
+			return XKEY_UNKNOWN
+		case "[2~":
+			return XKEY_INSERT // terminal
+		case "[3~":
+			return XKEY_DELETE // terminal
+		case "[5~":
+			return XKEY_PGUP // terminal
+		case "[6~":
+			return XKEY_PGDN // terminal
+		case "[15~":
+			return XKEY_F5 // terminal
+		case "[17~":
+			return XKEY_F6 // terminal
+		case "[18~":
+			return XKEY_F7 // terminal
+		case "[19~":
+			return XKEY_F8 // terminal
+		case "[20~":
+			return XKEY_F9 // terminal
+		case "[21~":
+			return XKEY_F10 // terminal
+		case "[23~":
+			return XKEY_F11
+		case "[24~":
+			return XKEY_F12 // terminal
+		case "OP":
+			return XKEY_F1
+		case "OQ":
+			return XKEY_F2
+		case "OR":
+			return XKEY_F3
+		case "OS":
+			return XKEY_F4
+		case "Ot":
+			return XKEY_F5 // syncterm
+		default:
+			fmt.Printf("ERROR Extended %#v\n", extended)
+			return XKEY_UNKNOWN
+		}
+
+	}
+
+	return c
+}
+
+func (d *Door) WaitKey(secs int64, usecs int64) int {
+	// var fdsetRead, fdsetWrite, fdsete syscall.FdSet
+	var fdsetRead syscall.FdSet
+	// fdsetWrite := syscall.FdSet
+	clearAll(&fdsetRead)
+	// clearAll(&fdsetWrite)
+	// clearAll(&fdsete)
+	set(&fdsetRead, d.READFD)
+	// timeout := syscall.Timeval{Sec: 0, Usec: 100}
+	timeout := syscall.Timeval{Sec: secs, Usec: usecs}
+	v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
+	if v == -1 {
+		fmt.Println("-1 : ", err)
+		// hangup ?!
+		return -2
+	}
+	if v == 0 {
+		// timeout
+		return -1
+	}
+
+	return d.GetKey()
+
+	// var buffer []byte   -- 0 byte buffer.  doh!
+	buffer := make([]byte, 1)
+	r, err := syscall.Read(d.READFD, buffer)
+	if r != 1 {
+		fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
+		// hangup
+		return -2
+	}
+	return int(buffer[0])
+}
+
+// Input a string of max length.
+// This displays the input area if a bg color was set.
+// This handles timeout, input, backspace, and enter.
+func (d *Door) Input(max int) string {
+	var line string
+
+	// draw input area
+	d.Write(strings.Repeat(" ", max) + strings.Repeat("\x08", max))
+
+	var c int
+
+	for true {
+		c = d.WaitKey(120, 0)
+		if c < 0 {
+			// timeout/hangup
+			return ""
+		}
+
+		if c > 1000 {
+			continue
+		}
+
+		if strconv.IsPrint(rune(c)) {
+			if len(line) < max {
+				d.Write(string(c))
+				line += string(c)
+			} else {
+				d.Write("\x07")
+			}
+		} else {
+			// Non-print
+			switch c {
+			case 0x7f, 0x08:
+				if len(line) > 0 {
+					d.Write("\x08 \x08")
+					line = line[:len(line)-1]
+				}
+			case 0x0d:
+				return line
+			}
+		}
+	}
+	// this is never reached
+	return line
+}

+ 10 - 3
testdoor/testdoor.go

@@ -14,7 +14,7 @@ func main() {
 	bold := door.Color(1, 37, 40)
 	bolder := door.ColorText("BLI BOLD YEL ON BLUE")
 	d.Write("Welcome to " + bolder + "door32.sys" + reset + door.CRNL + "..." + door.CRNL)
-	key := d.WaitKey(120)
+	key := d.WaitKey(120, 0)
 	message := fmt.Sprintf("Key %s%d / %x%s"+door.CRNL, bold, key, key, reset)
 	d.Write(message)
 	b := door.Box{20, 1}
@@ -32,8 +32,15 @@ func main() {
 	message = fmt.Sprintf("You have %0.2f minutes / %0.2f seconds remaining..."+door.CRNL, left.Minutes(), left.Seconds())
 	d.Write(message)
 
-	d.Write("Returning you to the BBS..." + door.CRNL)
-	d.WaitKey(3)
+	inputColor := door.ColorText("BRI WHI ON BLUE")
+	d.Write("What is your name: " + inputColor)
+	name := d.Input(25)
+	d.Write(door.Reset + door.CRNL)
+
+	message = fmt.Sprintf("Returning %s to the BBS..."+door.CRNL, name)
+	d.Write(message)
+
+	d.WaitKey(3, 0)
 
 	left = d.TimeLeft()