package draw import ( "bufio" "os" "unicode/utf8" ) // Keyboardctl delivers runes typed at the window. // // Opening it also turns the console raw. Without that, rio keeps its own // line editor on the window and echoes what you type into the text layer, // painting over whatever the program has drawn. Holding consctl open is // what keeps raw mode; closing it puts the window back as it was. type Keyboardctl struct { C <-chan rune ctl *os.File cons *os.File } func OpenKeyboard(dev string) (*Keyboardctl, error) { ctl, err := os.OpenFile(dev+"/consctl", os.O_WRONLY, 0) if err != nil { return nil, err } if _, err := ctl.WriteString("rawon"); err != nil { ctl.Close() return nil, err } cons, err := os.Open(dev + "/cons") if err != nil { ctl.Close() return nil, err } ch := make(chan rune, 32) k := &Keyboardctl{C: ch, ctl: ctl, cons: cons} go func() { defer close(ch) br := bufio.NewReader(cons) var buf []byte b := make([]byte, 1) for { n, err := br.Read(b) if n == 0 || err != nil { return } buf = append(buf, b[0]) // a rune may arrive a byte at a time if r, sz := utf8.DecodeRune(buf); r != utf8.RuneError || sz > 1 { ch <- r buf = buf[:0] } else if len(buf) >= utf8.UTFMax { buf = buf[:0] } } }() return k, nil } func (k *Keyboardctl) Close() error { k.cons.Close() // dropping consctl is what restores rio's own line editing return k.ctl.Close() }