Преглед на файлове

Attempting windows, failing tests.

Steve Thielemann преди 2 години
родител
ревизия
ab59df8ba6
променени са 5 файла, в които са добавени 614 реда и са изтрити 414 реда
  1. 338 0
      door/input.go
  2. 3 369
      door/input_linux.go
  3. 1 1
      door/input_test.go
  4. 228 23
      door/input_windows.go
  5. 44 21
      door/write_windows.go

+ 338 - 0
door/input.go

@@ -17,6 +17,344 @@ var ErrDisconnected error = fmt.Errorf("Disconnected")
 
 var DefaultTimeout time.Duration = time.Duration(60) * time.Second
 
+var ReaderInterval = time.Duration(200) * time.Millisecond
+
+// Output a nice string representation of the rune buffer.
+func extended_output(buffer []rune) string {
+	var output string = string(buffer)
+	output = strings.Replace(output, "\x1b", "^[", -1)
+	return output
+}
+
+func RuneToInt8(r rune) int8 {
+	return int8(r)
+}
+
+// Translate these extended codes into these Extended values.
+var extendedKeys map[string]Extended = map[string]Extended{
+	"[A":   UP_ARROW,
+	"[B":   DOWN_ARROW,
+	"[C":   RIGHT_ARROW,
+	"[D":   LEFT_ARROW,
+	"[H":   HOME,
+	"[F":   END, // terminal
+	"[K":   END,
+	"[V":   PAGE_UP,
+	"[U":   PAGE_DOWN,
+	"[@":   INSERT,
+	"[2~":  INSERT,    // terminal
+	"[3~":  DELETE,    // terminal
+	"[5~":  PAGE_UP,   // terminal
+	"[6~":  PAGE_DOWN, // terminal
+	"[15~": F5,        // terminal
+	"[17~": F6,        // terminal
+	"[18~": F7,        // terminal
+	"[19~": F8,        // terminal
+	"[20~": F9,        // terminal
+	"[21~": F10,       // terminal
+	"[23~": F11,
+	"[24~": F12, // terminal
+	"OP":   F1,
+	"OQ":   F2,
+	"OR":   F3,
+	"OS":   F4, // syncterm "[1" = F1,F2,F3, and F4)
+	"Ot":   F5, // syncterm
+}
+
+func process(d *Door, newRune bool) {
+	// This is like GetKey
+	var rlen int
+	var r, r2 rune
+	var has2 bool
+	var done bool = false
+
+	for !done {
+		rlen = len(readerBuffer)
+		if rlen == 0 {
+			// Nothing to do here
+			return
+		}
+
+		log.Println("rlen:", rlen, "readerBuffer:", readerBuffer, "newRune:", newRune)
+
+		r = readerBuffer[0]
+		if rlen >= 2 {
+			r2 = readerBuffer[1]
+			has2 = true
+		} else {
+			r2 = unicode.ReplacementChar
+			has2 = false
+		}
+
+		// if !has2 and !newRune, then we're "done" (received everything,
+		// and there's nothing else coming...)
+
+		// fyneterm CR
+		if r == '\x0a' {
+			if !has2 && !newRune {
+				ArrayPop(&readerBuffer, 1)
+				d.readerChannel <- ReaderData{R: '\x0d', Ex: NOP, Err: nil}
+				return
+			}
+
+			if has2 {
+				ArrayPop(&readerBuffer, 1)
+				if r2 == '\x00' || r2 == '\x0a' {
+					ArrayPop(&readerBuffer, 1)
+				}
+				d.readerChannel <- ReaderData{R: '\x0d', Ex: NOP, Err: nil}
+			} else {
+				// We don't have a 2nd rune, and we haven't timed out.
+				return
+			}
+
+			continue
+		}
+
+		// We get 0x0d, 0x00, or 0x0d 0x0a from syncterm
+		if r == '\x0d' {
+			if !has2 && !newRune {
+				ArrayPop(&readerBuffer, 1)
+				d.readerChannel <- ReaderData{R: r, Ex: NOP, Err: nil}
+				return
+			}
+
+			if has2 {
+				ArrayPop(&readerBuffer, 1)
+				if r2 == '\x00' || r2 == '\x0a' {
+					ArrayPop(&readerBuffer, 1)
+				}
+				d.readerChannel <- ReaderData{R: r, Ex: NOP, Err: nil}
+			} else {
+				return
+			}
+
+			continue
+		}
+
+		if r == '\x00' {
+			// Possibly doorway mode - deprecated?
+			// syncterm does support this.
+
+			if !has2 && !newRune {
+				// timeout
+				ArrayPop(&readerBuffer, 1)
+				d.readerChannel <- ReaderData{R: r, Ex: NOP, Err: nil}
+				return
+			}
+
+			if has2 {
+				ArrayPop(&readerBuffer, 2)
+				switch r2 {
+				case '\x50':
+					d.readerChannel <- ReaderData{0, DOWN_ARROW, nil}
+				case '\x48':
+					d.readerChannel <- ReaderData{0, UP_ARROW, nil}
+				case '\x4b':
+					d.readerChannel <- ReaderData{0, LEFT_ARROW, nil}
+				case 0x4d:
+					d.readerChannel <- ReaderData{0, RIGHT_ARROW, nil}
+				case 0x47:
+					d.readerChannel <- ReaderData{0, HOME, nil}
+				case 0x4f:
+					d.readerChannel <- ReaderData{0, END, nil}
+				case 0x49:
+					d.readerChannel <- ReaderData{0, PAGE_UP, nil}
+				case 0x51:
+					d.readerChannel <- ReaderData{0, PAGE_DOWN, nil}
+				case 0x3b:
+					d.readerChannel <- ReaderData{0, F1, nil}
+				case 0x3c:
+					d.readerChannel <- ReaderData{0, F2, nil}
+				case 0x3d:
+					d.readerChannel <- ReaderData{0, F3, nil}
+				case 0x3e:
+					d.readerChannel <- ReaderData{0, F4, nil}
+				case 0x3f:
+					d.readerChannel <- ReaderData{0, F5, nil}
+				case 0x40:
+					d.readerChannel <- ReaderData{0, F6, nil}
+				case 0x41:
+					d.readerChannel <- ReaderData{0, F7, nil}
+				case 0x42:
+					d.readerChannel <- ReaderData{0, F8, nil}
+				case 0x43:
+					d.readerChannel <- ReaderData{0, F9, nil}
+				case 0x44:
+					d.readerChannel <- ReaderData{0, F10, nil}
+
+				case 0x45:
+					d.readerChannel <- ReaderData{0, F11, nil}
+				case 0x46:
+					d.readerChannel <- ReaderData{0, F12, nil}
+
+				case 0x52:
+					d.readerChannel <- ReaderData{0, INSERT, nil}
+				case 0x53:
+					d.readerChannel <- ReaderData{0, DELETE, nil}
+				default:
+					log.Printf("ERROR Doorway mode: 0x00 %x\n", r2)
+					d.readerChannel <- ReaderData{0, UNKNOWN, nil}
+
+				}
+			} else {
+				return
+			}
+			continue
+		} // end doorway mode
+
+		if r == '\x1b' {
+			// Escape key, or ?
+			// This is a little harder, since we don't know how many we need.
+			if !has2 && !newRune {
+				ArrayPop(&readerBuffer, 1)
+				d.readerChannel <- ReaderData{r, NOP, nil}
+				return
+			}
+
+			if has2 {
+				// We at least have the 2nd one...
+				if r2 == '\x1b' {
+					ArrayPop(&readerBuffer, 1)
+					d.readerChannel <- ReaderData{r, NOP, nil}
+					continue
+				}
+
+				// Can't distinguish between \x1bO and \x1bOP (F1)
+				/*
+					if unicode.IsLetter(r2) {
+						// ALT-KEY
+						if unicode.IsLower(r2) {
+							ArrayPop(&readerBuffer, 2)
+							ex := Extended(int(ALT_a) + int(r2-'a'))
+							d.readerChannel <- ReaderData{0, ex, nil}
+						} else {
+							// Must be upper
+							ArrayPop(&readerBuffer, 2)
+							ex := Extended(int(ALT_A) + int(r2-'A'))
+							d.readerChannel <- ReaderData{0, ex, nil}
+						}
+						continue
+					}
+				*/
+
+				var extended []rune = make([]rune, 0, 10)
+				extended = append(extended, r2)
+				var extlen = 2 // Length of codes to remove on successful match.
+				var pos = 2    // Where we get the next rune from
+				var isMouse bool = false
+				var found bool = false
+
+				log.Println(pos, readerBuffer)
+
+				for pos < rlen {
+					r2 = readerBuffer[pos]
+
+					pos++
+					extlen++
+
+					extended = append(extended, r2)
+
+					log.Println("0x1b LOOP pos:", pos, "extlen:", extlen, "extended:", string(extended))
+
+					ext, has := extendedKeys[string(extended)]
+					if has {
+						// Found it!
+						log.Println("Found Extended Match:", ext.String())
+						ArrayPop(&readerBuffer, extlen)
+						d.readerChannel <- ReaderData{0, ext, nil}
+						found = true
+						break
+					}
+
+					// Mouse codes can also contain letters.
+					if !isMouse && unicode.IsLetter(r2) {
+						// end of extended code, unless mouse!
+						log.Println("not mouse, is letter...")
+						if string(extended) == "[M" {
+							isMouse = true
+						} else {
+							break
+						}
+					}
+
+					if isMouse && len(extended) == 5 {
+						break
+					}
+				}
+
+				if found {
+					continue
+				}
+
+				log.Println("POS:", pos, "EXLEN:", extlen, "Extended:", extended, "readerBuffer:", readerBuffer)
+
+				log.Printf("(possible) Extended Code: [%s]", extended_output(extended))
+
+				var exString string = string(extended)
+				if strings.HasPrefix(exString, "[M") && len(extended) == 5 {
+					// Yes, "valid"
+					ArrayPop(&readerBuffer, extlen)
+					// Mouse Extended - input zero based (I add +1 to X, Y, and button)
+					var mouse Mouse = Mouse{Button: RuneToInt8(extended[2]) - ' ' + 1,
+						X: RuneToInt8(extended[3]) - '!' + 1,
+						Y: RuneToInt8(extended[4]) - '!' + 1}
+					d.mcMutex.Lock()
+					d.LastMouse = append(d.LastMouse, mouse)
+					d.mcMutex.Unlock()
+					log.Println("Mouse:", mouse)
+					d.readerChannel <- ReaderData{0, MOUSE, nil}
+					continue
+				}
+
+				if strings.HasSuffix(exString, "R") {
+					// yes, "valid"
+					ArrayPop(&readerBuffer, extlen)
+					// Cursor Position information (or Shift-F3)
+					var cursor CursorPos
+					// ^[[1;1R^[[2;3r^[[41;173R
+					// Y;X
+					exString = exString[1 : len(exString)-1] // Remove [ and R
+					pos := SplitToInt(exString, ";")
+					if len(pos) == 2 {
+						cursor.X = pos[1]
+						cursor.Y = pos[0]
+						d.mcMutex.Lock()
+						d.LastCursor = append(d.LastCursor, cursor)
+						d.mcMutex.Unlock()
+						log.Println("Cursor Pos:", cursor)
+						d.readerChannel <- ReaderData{0, CURSOR, nil}
+						continue
+					} else {
+						log.Println("ERROR Cursor Pos:", extended)
+						d.readerChannel <- ReaderData{0, UNKNOWN, nil}
+						continue
+					}
+				}
+
+				// Ok, this LOOKS like something invalid...
+				if !newRune {
+					// Yes, this is invalid.
+					ArrayPop(&readerBuffer, extlen)
+					log.Println("ERROR Extended:", extended)
+					d.readerChannel <- ReaderData{0, UNKNOWN, nil}
+				} else {
+					log.Println("(Possibly) invalid extended:", extended)
+					return
+				}
+
+			} else {
+				return
+			}
+			continue
+		}
+
+		ArrayPop(&readerBuffer, 1)
+		d.readerChannel <- ReaderData{r, NOP, nil}
+		continue
+	}
+}
+
 func (d *Door) WaitKey(timeout time.Duration) (rune, Extended, error) {
 	/*
 		d.readerMutex.Lock()

+ 3 - 369
door/input_linux.go

@@ -5,7 +5,6 @@ import (
 	"bytes"
 	"io"
 	"log"
-	"strings"
 	"syscall"
 	"time"
 	"unicode"
@@ -14,13 +13,6 @@ import (
 var ReaderInterval = time.Duration(200) * time.Millisecond
 var ReaderTimeval syscall.Timeval = syscall.Timeval{Sec: 0, Usec: 200}
 
-// Output a nice string representation of the rune buffer.
-func extended_output(buffer []rune) string {
-	var output string = string(buffer)
-	output = strings.Replace(output, "\x1b", "^[", -1)
-	return output
-}
-
 // syscall.FdSet clear all
 func clearAll(fdSetPtr *syscall.FdSet) {
 	for index := range (*fdSetPtr).Bits {
@@ -33,339 +25,9 @@ func set(fdSetPtr *syscall.FdSet, fd int) {
 	(*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64)
 }
 
-func RuneToInt8(r rune) int8 {
-	return int8(r)
-}
-
 const READ_SIZE = 16 // Size of read buffer
-
 var readerBuffer []rune
 
-// Translate these extended codes into these Extended values.
-var extendedKeys map[string]Extended = map[string]Extended{
-	"[A":   UP_ARROW,
-	"[B":   DOWN_ARROW,
-	"[C":   RIGHT_ARROW,
-	"[D":   LEFT_ARROW,
-	"[H":   HOME,
-	"[F":   END, // terminal
-	"[K":   END,
-	"[V":   PAGE_UP,
-	"[U":   PAGE_DOWN,
-	"[@":   INSERT,
-	"[2~":  INSERT,    // terminal
-	"[3~":  DELETE,    // terminal
-	"[5~":  PAGE_UP,   // terminal
-	"[6~":  PAGE_DOWN, // terminal
-	"[15~": F5,        // terminal
-	"[17~": F6,        // terminal
-	"[18~": F7,        // terminal
-	"[19~": F8,        // terminal
-	"[20~": F9,        // terminal
-	"[21~": F10,       // terminal
-	"[23~": F11,
-	"[24~": F12, // terminal
-	"OP":   F1,
-	"OQ":   F2,
-	"OR":   F3,
-	"OS":   F4, // syncterm "[1" = F1,F2,F3, and F4)
-	"Ot":   F5, // syncterm
-}
-
-func process(d *Door, newRune bool) {
-	// This is like GetKey
-	var rlen int
-	var r, r2 rune
-	var has2 bool
-	var done bool = false
-
-	for !done {
-		rlen = len(readerBuffer)
-		if rlen == 0 {
-			// Nothing to do here
-			return
-		}
-
-		log.Println("rlen:", rlen, "readerBuffer:", readerBuffer, "newRune:", newRune)
-
-		r = readerBuffer[0]
-		if rlen >= 2 {
-			r2 = readerBuffer[1]
-			has2 = true
-		} else {
-			r2 = unicode.ReplacementChar
-			has2 = false
-		}
-
-		// if !has2 and !newRune, then we're "done" (received everything,
-		// and there's nothing else coming...)
-
-		// fyneterm CR
-		if r == '\x0a' {
-			if !has2 && !newRune {
-				ArrayPop(&readerBuffer, 1)
-				d.readerChannel <- ReaderData{R: '\x0d', Ex: NOP, Err: nil}
-				return
-			}
-
-			if has2 {
-				ArrayPop(&readerBuffer, 1)
-				if r2 == '\x00' || r2 == '\x0a' {
-					ArrayPop(&readerBuffer, 1)
-				}
-				d.readerChannel <- ReaderData{R: '\x0d', Ex: NOP, Err: nil}
-			} else {
-				// We don't have a 2nd rune, and we haven't timed out.
-				return
-			}
-
-			continue
-		}
-
-		// We get 0x0d, 0x00, or 0x0d 0x0a from syncterm
-		if r == '\x0d' {
-			if !has2 && !newRune {
-				ArrayPop(&readerBuffer, 1)
-				d.readerChannel <- ReaderData{R: r, Ex: NOP, Err: nil}
-				return
-			}
-
-			if has2 {
-				ArrayPop(&readerBuffer, 1)
-				if r2 == '\x00' || r2 == '\x0a' {
-					ArrayPop(&readerBuffer, 1)
-				}
-				d.readerChannel <- ReaderData{R: r, Ex: NOP, Err: nil}
-			} else {
-				return
-			}
-
-			continue
-		}
-
-		if r == '\x00' {
-			// Possibly doorway mode - deprecated?
-			// syncterm does support this.
-
-			if !has2 && !newRune {
-				// timeout
-				ArrayPop(&readerBuffer, 1)
-				d.readerChannel <- ReaderData{R: r, Ex: NOP, Err: nil}
-				return
-			}
-
-			if has2 {
-				ArrayPop(&readerBuffer, 2)
-				switch r2 {
-				case '\x50':
-					d.readerChannel <- ReaderData{0, DOWN_ARROW, nil}
-				case '\x48':
-					d.readerChannel <- ReaderData{0, UP_ARROW, nil}
-				case '\x4b':
-					d.readerChannel <- ReaderData{0, LEFT_ARROW, nil}
-				case 0x4d:
-					d.readerChannel <- ReaderData{0, RIGHT_ARROW, nil}
-				case 0x47:
-					d.readerChannel <- ReaderData{0, HOME, nil}
-				case 0x4f:
-					d.readerChannel <- ReaderData{0, END, nil}
-				case 0x49:
-					d.readerChannel <- ReaderData{0, PAGE_UP, nil}
-				case 0x51:
-					d.readerChannel <- ReaderData{0, PAGE_DOWN, nil}
-				case 0x3b:
-					d.readerChannel <- ReaderData{0, F1, nil}
-				case 0x3c:
-					d.readerChannel <- ReaderData{0, F2, nil}
-				case 0x3d:
-					d.readerChannel <- ReaderData{0, F3, nil}
-				case 0x3e:
-					d.readerChannel <- ReaderData{0, F4, nil}
-				case 0x3f:
-					d.readerChannel <- ReaderData{0, F5, nil}
-				case 0x40:
-					d.readerChannel <- ReaderData{0, F6, nil}
-				case 0x41:
-					d.readerChannel <- ReaderData{0, F7, nil}
-				case 0x42:
-					d.readerChannel <- ReaderData{0, F8, nil}
-				case 0x43:
-					d.readerChannel <- ReaderData{0, F9, nil}
-				case 0x44:
-					d.readerChannel <- ReaderData{0, F10, nil}
-
-				case 0x45:
-					d.readerChannel <- ReaderData{0, F11, nil}
-				case 0x46:
-					d.readerChannel <- ReaderData{0, F12, nil}
-
-				case 0x52:
-					d.readerChannel <- ReaderData{0, INSERT, nil}
-				case 0x53:
-					d.readerChannel <- ReaderData{0, DELETE, nil}
-				default:
-					log.Printf("ERROR Doorway mode: 0x00 %x\n", r2)
-					d.readerChannel <- ReaderData{0, UNKNOWN, nil}
-
-				}
-			} else {
-				return
-			}
-			continue
-		} // end doorway mode
-
-		if r == '\x1b' {
-			// Escape key, or ?
-			// This is a little harder, since we don't know how many we need.
-			if !has2 && !newRune {
-				ArrayPop(&readerBuffer, 1)
-				d.readerChannel <- ReaderData{r, NOP, nil}
-				return
-			}
-
-			if has2 {
-				// We at least have the 2nd one...
-				if r2 == '\x1b' {
-					ArrayPop(&readerBuffer, 1)
-					d.readerChannel <- ReaderData{r, NOP, nil}
-					continue
-				}
-
-				// Can't distinguish between \x1bO and \x1bOP (F1)
-				/*
-					if unicode.IsLetter(r2) {
-						// ALT-KEY
-						if unicode.IsLower(r2) {
-							ArrayPop(&readerBuffer, 2)
-							ex := Extended(int(ALT_a) + int(r2-'a'))
-							d.readerChannel <- ReaderData{0, ex, nil}
-						} else {
-							// Must be upper
-							ArrayPop(&readerBuffer, 2)
-							ex := Extended(int(ALT_A) + int(r2-'A'))
-							d.readerChannel <- ReaderData{0, ex, nil}
-						}
-						continue
-					}
-				*/
-
-				var extended []rune = make([]rune, 0, 10)
-				extended = append(extended, r2)
-				var extlen = 2 // Length of codes to remove on successful match.
-				var pos = 2    // Where we get the next rune from
-				var isMouse bool = false
-				var found bool = false
-
-				log.Println(pos, readerBuffer)
-
-				for pos < rlen {
-					r2 = readerBuffer[pos]
-
-					pos++
-					extlen++
-
-					extended = append(extended, r2)
-
-					log.Println("0x1b LOOP pos:", pos, "extlen:", extlen, "extended:", string(extended))
-
-					ext, has := extendedKeys[string(extended)]
-					if has {
-						// Found it!
-						log.Println("Found Extended Match:", ext.String())
-						ArrayPop(&readerBuffer, extlen)
-						d.readerChannel <- ReaderData{0, ext, nil}
-						found = true
-						break
-					}
-
-					// Mouse codes can also contain letters.
-					if !isMouse && unicode.IsLetter(r2) {
-						// end of extended code, unless mouse!
-						log.Println("not mouse, is letter...")
-						if string(extended) == "[M" {
-							isMouse = true
-						} else {
-							break
-						}
-					}
-
-					if isMouse && len(extended) == 5 {
-						break
-					}
-				}
-
-				if found {
-					continue
-				}
-
-				log.Println("POS:", pos, "EXLEN:", extlen, "Extended:", extended, "readerBuffer:", readerBuffer)
-
-				log.Printf("(possible) Extended Code: [%s]", extended_output(extended))
-
-				var exString string = string(extended)
-				if strings.HasPrefix(exString, "[M") && len(extended) == 5 {
-					// Yes, "valid"
-					ArrayPop(&readerBuffer, extlen)
-					// Mouse Extended - input zero based (I add +1 to X, Y, and button)
-					var mouse Mouse = Mouse{Button: RuneToInt8(extended[2]) - ' ' + 1,
-						X: RuneToInt8(extended[3]) - '!' + 1,
-						Y: RuneToInt8(extended[4]) - '!' + 1}
-					d.mcMutex.Lock()
-					d.LastMouse = append(d.LastMouse, mouse)
-					d.mcMutex.Unlock()
-					log.Println("Mouse:", mouse)
-					d.readerChannel <- ReaderData{0, MOUSE, nil}
-					continue
-				}
-
-				if strings.HasSuffix(exString, "R") {
-					// yes, "valid"
-					ArrayPop(&readerBuffer, extlen)
-					// Cursor Position information (or Shift-F3)
-					var cursor CursorPos
-					// ^[[1;1R^[[2;3r^[[41;173R
-					// Y;X
-					exString = exString[1 : len(exString)-1] // Remove [ and R
-					pos := SplitToInt(exString, ";")
-					if len(pos) == 2 {
-						cursor.X = pos[1]
-						cursor.Y = pos[0]
-						d.mcMutex.Lock()
-						d.LastCursor = append(d.LastCursor, cursor)
-						d.mcMutex.Unlock()
-						log.Println("Cursor Pos:", cursor)
-						d.readerChannel <- ReaderData{0, CURSOR, nil}
-						continue
-					} else {
-						log.Println("ERROR Cursor Pos:", extended)
-						d.readerChannel <- ReaderData{0, UNKNOWN, nil}
-						continue
-					}
-				}
-
-				// Ok, this LOOKS like something invalid...
-				if !newRune {
-					// Yes, this is invalid.
-					ArrayPop(&readerBuffer, extlen)
-					log.Println("ERROR Extended:", extended)
-					d.readerChannel <- ReaderData{0, UNKNOWN, nil}
-				} else {
-					log.Println("(Possibly) invalid extended:", extended)
-					return
-				}
-
-			} else {
-				return
-			}
-			continue
-		}
-
-		ArrayPop(&readerBuffer, 1)
-		d.readerChannel <- ReaderData{r, NOP, nil}
-		continue
-	}
-}
-
 // go routine Reader for input
 // This "times out" every ReaderTimeval
 func Reader(d *Door) {
@@ -401,23 +63,18 @@ func Reader(d *Door) {
 		}
 		// log.Printf("Select: %#v / %#v\n", v, err)
 
-		if v == -1 {
+		if err != nil {
 			log.Printf("Reader ERR: %#v\n", err)
 			d.readerMutex.Lock()
-
+			defer d.readerMutex.Unlock()
 			if !d.ReaderClosed {
 				d.ReaderClosed = true
+				d.Disconnected = true
 				close(d.readerChannel)
 			}
 			return
 		}
 
-		if v == 0 {
-			// timeout
-			process(d, false)
-			continue
-		}
-
 		// The buffer used here must have len & cap to size you want to read.
 		// use [BUFF_SIZE]byte for readone
 		r, err := syscall.Read(d.READFD, readbuffer[:])
@@ -487,28 +144,5 @@ func Reader(d *Door) {
 		} // goto RuneRead
 
 		process(d, true)
-
-		// buffer = append(buffer, readone[0])
-
-		/*
-			Take2:
-				input, size = utf8.DecodeRune(buffer)
-				if input != utf8.RuneError {
-					d.readerChannel <- input
-					for size > 0 {
-						ArrayDelete(&buffer, 0)
-						size--
-					}
-					if len(buffer) > 0 {
-						goto Take2
-					}
-					timeoutCount = 0
-				} else {
-					// Not a valid rune
-					continue
-				}
-		*/
-		// d.readerChannel <- rune(buffer[0])
-
 	}
 }

+ 1 - 1
door/input_test.go

@@ -379,7 +379,7 @@ func TestDoorInputConnection(t *testing.T) {
 		var result string = string(buffer[:r])
 	*/
 
-	expected := "     \x08\x08\x08\x08\x0812345\x07\x07\x07\x07"
+	expected := "     \x08\x08\x08\x08\x0812345\x07\x07\x07\x07\x07"
 	if result != expected {
 		t.Errorf("Buffer Input(5): Expected %#v, got %#v\n", expected, result)
 	}

+ 228 - 23
door/input_windows.go

@@ -1,14 +1,155 @@
 package door
 
 import (
+	"bufio"
+	"bytes"
+	"io"
 	"log"
-	"sync/atomic"
+
 	"syscall"
+	"unicode"
+
+	"github.com/creack/goselect"
 )
 
+const READ_SIZE = 16 // Size of read buffer
+
+var readerBuffer []rune
+
+/* // linux Reader
+// go routine Reader for input
+// This "times out" every ReaderTimeval
+func Reader(d *Door) {
+	// I need non-blocking reads here.
+
+	readerBuffer = make([]rune, 0, READ_SIZE*2)
+
+	defer func() {
+		log.Printf("~Reader\n")
+		if d.ReaderCanClose {
+			d.wg.Done()
+		}
+	}()
+	defer func() {
+		if err := recover(); err != nil {
+			log.Printf("Reader: %#v\n", err)
+		}
+	}()
+
+	var fdset syscall.FdSet
+	var readbuffer [READ_SIZE]byte
+	var runebuffer bytes.Buffer
+	var runeread = bufio.NewReaderSize(&runebuffer, 1)
+
+	for {
+		clearAll(&fdset)
+		set(&fdset, d.READFD)
+
+		v, err := syscall.Select(d.READFD+1, &fdset, nil, nil, &ReaderTimeval)
+
+		if err == syscall.EINTR {
+			continue
+		}
+		// log.Printf("Select: %#v / %#v\n", v, err)
+
+		if v == -1 {
+			log.Printf("Reader ERR: %#v\n", err)
+			d.readerMutex.Lock()
+
+			if !d.ReaderClosed {
+				d.ReaderClosed = true
+				close(d.readerChannel)
+			}
+			return
+		}
+
+		if v == 0 {
+			// timeout
+			process(d, false)
+			continue
+		}
+
+		// The buffer used here must have len & cap to size you want to read.
+		// use [BUFF_SIZE]byte for readone
+		r, err := syscall.Read(d.READFD, readbuffer[:])
+		if r == -1 {
+			log.Println("syscall.Read -1 (closed)")
+			d.readerMutex.Lock()
+			defer d.readerMutex.Unlock()
+			if !d.ReaderClosed {
+				d.ReaderClosed = true
+				d.Disconnected = true
+				close(d.readerChannel)
+			}
+			return
+		}
+		if r == 0 {
+			log.Printf("Select said ready, but: %#v %#v\n", r, err)
+			d.readerMutex.Lock()
+			defer d.readerMutex.Unlock()
+			if !d.ReaderClosed {
+				d.ReaderClosed = true
+				d.Disconnected = true
+				close(d.readerChannel)
+			}
+			return
+		}
+
+		if DEBUG_INPUT {
+			log.Printf("Reader << %d, %#v\n", r, readbuffer[:r])
+		}
+
+		runebuffer.Write(readbuffer[:r])
+		var input rune
+
+		// Is this unicode?
+
+		err = nil
+		for err == nil {
+			//RuneRead:
+
+			input, _, err = runeread.ReadRune()
+
+			if err == io.EOF {
+				break // continue
+			}
+			if err != nil {
+				log.Printf("ReadRune: %#v\n", err)
+				// errors EOF
+				break // continue // break for loop
+			}
+			if input == unicode.ReplacementChar {
+				runeread.UnreadRune()
+				b, _ := runeread.ReadByte()
+				if DEBUG_INPUT {
+					log.Printf("Reader (byte) >> %x\n", b)
+				}
+				readerBuffer = append(readerBuffer, rune(b))
+				// d.readerChannel <- rune(b)
+			} else {
+				if DEBUG_INPUT {
+					log.Printf("Reader >> %x\n", input)
+				}
+				readerBuffer = append(readerBuffer, input)
+				// d.readerChannel <- input
+			}
+			// process(d, true)
+
+		} // goto RuneRead
+
+		process(d, true)
+
+		// buffer = append(buffer, readone[0])
+
+		// d.readerChannel <- rune(buffer[0])
+
+	}
+}
+*/
+
 func Reader(d *Door) {
 	// handle syscall.Handle, readerChannel *chan byte
-	var handle syscall.Handle = syscall.Handle(d.Config.Comm_handle)
+	var handle syscall.Handle = syscall.Handle(d.READFD) // d.Config.Comm_handle)
 
 	defer func() {
 		log.Printf("~Reader")
@@ -23,37 +164,101 @@ func Reader(d *Door) {
 		}
 	}()
 
-	var buffer []byte = make([]byte, 1)
-	WSA_Buffer := syscall.WSABuf{Len: 1, Buf: &buffer[0]}
-	read := uint32(0)
-	flags := uint32(0)
+	var fdset *goselect.FDSet = &goselect.FDSet{}
+	var readbuffer [READ_SIZE]byte
+	var runebuffer bytes.Buffer
+	var runeread = bufio.NewReaderSize(&runebuffer, 1)
+	WSA_Buffer := syscall.WSABuf{Len: READ_SIZE, Buf: &readbuffer[0]}
 
 	for {
-		err := syscall.WSARecv(handle, &WSA_Buffer, 1, &read, &flags, nil, nil)
+		fdset.Zero()
+		fdset.Set(uintptr(d.READFD))
+
+		err := goselect.Select(d.READFD+1, fdset, nil, nil, ReaderInterval)
+
+		if err == syscall.EINTR {
+			continue
+		}
+
+		if err != nil {
+			log.Printf("Reader Err: %#v\n", err)
+			d.readerMutex.Lock()
+			defer d.readerMutex.Unlock()
+			if !d.ReaderClosed {
+				d.ReaderClosed = true
+				close(d.readerChannel)
+			}
+			return
+
+		}
+
+		if !fdset.IsSet(uintptr(d.READFD)) {
+			// I believe this means timeout
+			process(d, false)
+			continue
+		}
+
+		read := uint32(0)
+		flags := uint32(0)
+
+		err = syscall.WSARecv(handle, &WSA_Buffer, 1, &read, &flags, nil, nil)
 		if err != nil {
 			log.Printf("Reader ERR: %#v\n", err)
-			close(d.readerChannel)
-			if !d.Disconnect() {
-				log.Println("Reader close writerChannel")
-				atomic.StoreInt32(&d.Disconnected, 1)
-				d.closeChannel <- struct{}{}
-				return
+			d.readerMutex.Lock()
+			defer d.readerMutex.Unlock()
+			if !d.ReaderClosed {
+				d.ReaderClosed = true
+				close(d.readerChannel)
 			}
-			break
+			return
 		}
 
-		if read == 1 {
-			d.readerChannel <- buffer[0]
-		} else {
+		if read <= 0 {
 			log.Printf("READ FAILED %d\n", read)
-			close(d.readerChannel)
-			if !d.Disconnect() {
-				log.Println("Reader close writerChannel")
-				atomic.StoreInt32(&d.Disconnected, 1)
-				d.closeChannel <- struct{}{}
+			d.readerMutex.Lock()
+			defer d.readerMutex.Unlock()
+			if !d.ReaderClosed {
+				d.ReaderClosed = true
+				d.Disconnected = true
+				close(d.readerChannel)
 				return
 			}
-			return
 		}
+
+		if DEBUG_INPUT {
+			log.Printf("Reader << %d, %#v\n", read, readbuffer[:read])
+		}
+
+		runebuffer.Write(readbuffer[:read])
+		var input rune
+		err = nil
+		for err == nil {
+			input, _, err = runeread.ReadRune()
+
+			if err == io.EOF {
+				break // continue
+			}
+			if err != nil {
+				log.Printf("ReadRune: %#v\n", err)
+				// errors EOF
+				break // continue // break for loop
+			}
+			if input == unicode.ReplacementChar {
+				runeread.UnreadRune()
+				b, _ := runeread.ReadByte()
+				if DEBUG_INPUT {
+					log.Printf("Reader (byte) >> %x\n", b)
+				}
+				readerBuffer = append(readerBuffer, rune(b))
+				// d.readerChannel <- rune(b)
+			} else {
+				if DEBUG_INPUT {
+					log.Printf("Reader >> %x\n", input)
+				}
+				readerBuffer = append(readerBuffer, input)
+			}
+		}
+
+		process(d, true)
 	}
 }

+ 44 - 21
door/write_windows.go

@@ -4,39 +4,54 @@ import (
 	"fmt"
 	"log"
 	"strings"
-	"sync/atomic"
 	"syscall"
 )
 
 func Writer(d *Door) {
-	var handle syscall.Handle = syscall.Handle(d.Config.Comm_handle)
+	var handle syscall.Handle = syscall.Handle(d.WRITEFD) // Config.Comm_handle)
 	// handle syscall.Handle, writerChannel *chan string
 	log.Println("Writer")
 	defer d.wg.Done()
+	var Closed bool = false
 
 	for {
+		/*
+			select {
+			case <-d.closeChannel:
+				close(d.writerChannel)
+				log.Println("~Writer")
+				return
+			default:
+			}
+		*/
 		select {
-		case <-d.closeChannel:
-			close(d.writerChannel)
-			log.Println("~Writer")
-			return
-		default:
-		}
-
-		select {
-		case <-d.closeChannel:
-			close(d.writerChannel)
-			log.Println("~Writer")
-			return
+		/*
+			case <-d.closeChannel:
+				close(d.writerChannel)
+				log.Println("~Writer")
+				return
+		*/
 		case output, ok := <-d.writerChannel:
 			if !ok {
 				log.Println("closeChannel")
-				if !d.Disconnect() {
-					atomic.StoreInt32(&d.Disconnected, 1)
+				d.writerMutex.Lock()
+				if !d.WriterClosed {
+					d.WriterClosed = true
 				}
+				d.writerMutex.Unlock()
 				log.Println("~Writer")
 				return
 			} else {
+				if output == "" {
+					Closed = true
+					continue
+				}
+
+				if Closed {
+					log.Println("Ignoring write output.")
+					continue
+				}
+
 				if strings.HasSuffix(output, RestorePos) {
 					output += Color(d.LastColor...)
 				} else {
@@ -57,12 +72,20 @@ func Writer(d *Door) {
 
 				if (err != nil) || (l != DataWrite) {
 					log.Println("CloseChannel")
-					if !d.Disconnect() {
-						atomic.StoreInt32(&d.Disconnected, 1)
-						close(d.writerChannel)
+					Closed = true
+					d.writerMutex.Lock()
+					if !d.WriterClosed {
+						d.WriterClosed = true
 					}
-					log.Println("~Writer")
-					return
+					d.writerMutex.Unlock()
+					/*
+						if !d.Disconnect() {
+							atomic.StoreInt32(&d.Disconnected, 1)
+							close(d.writerChannel)
+						}
+						log.Println("~Writer")
+						return
+					*/
 				}
 			}
 		}