// NewDefaultLine returns a line type using the prompt by default, and setting // the terminal to raw mode. // If the history is nil then it is not used. func NewDefaultLine(hist *history) (*Line, error) { ter, err := term.New() if err != nil { return nil, err } if err = ter.RawMode(); err != nil { return nil, err } _, col, err := ter.GetSize() if err != nil { return nil, err } buf := newBuffer(len(PS1), col) buf.insertRunes([]rune(PS1)) return &Line{ ter: ter, buf: buf, hist: hist, ps1: PS1, ps2: PS2, lenPS1: len(PS1), useHistory: hasHistory(hist), }, nil }
func (t *Tty) raw() (err error) { if t.term, err = term.New(); err != nil { return err } if err := t.term.RawMode(); err != nil { return err } return nil }
func New() (*Cursor, error) { col, line, err := GetCursorPosition() if err != nil { return &Cursor{}, err } c := &Cursor{} c.Position.X, c.StartingPosition.X = col, col c.Position.Y, c.StartingPosition.Y = line, line c.terminal, err = term.New() return c, err }
// Dimensions returns terminal dimensions func (t *Tty) Dimensions() (height, width int, err error) { if t.term == nil { t.term, err = term.New() if err != nil { return 25, 80, err } } height, width, err = t.term.GetSize() if height == 0 { height = 25 } if width == 0 { width = 25 } return height, width, err }
func TestEchoMode() { fmt.Print("\n=== RUN TestEchoMode\n") ter, _ := term.New() defer func() { if err := ter.Restore(); err != nil { log.Print(err) } }() if err := ter.EchoMode(false); err != nil { log.Print("expected to set echo mode:", err) return } fmt.Print(" + Mode to echo off\n") buf := bufio.NewReader(term.Input) if !*IsInteractive { go func() { time.Sleep(time.Duration(*Time) * time.Second) fmt.Fprint(pw, "Karma\n") }() } fmt.Print(" Write (enter to finish): ") line, err := buf.ReadString('\n') if err != nil { log.Print(err) return } fmt.Printf("\n entered: %q\n", line) ter.EchoMode(true) fmt.Print("\n + Mode to echo on\n") if !*IsInteractive { go func() { time.Sleep(time.Duration(*Time) * time.Second) fmt.Fprint(pw, "hotel\n") }() } fmt.Print(" Write (enter to finish): ") line, _ = buf.ReadString('\n') if !*IsInteractive { fmt.Println() } fmt.Printf(" entered: %q\n", line) }
// Lookup prints the decimal code at pressing a key. func Lookup() { ter, err := term.New() if err != nil { log.Print(err) return } defer func() { if err = ter.Restore(); err != nil { log.Print(err) } }() if err = ter.RawMode(); err != nil { log.Print(err) return } else { buf := bufio.NewReader(term.Input) runes := make([]int32, 0) chars := make([]string, 0) fmt.Print("[Press Enter to exit]\r\n") fmt.Print("> ") L: for { rune_, _, err := buf.ReadRune() if err != nil { log.Print(err) continue } switch rune_ { default: fmt.Print(rune_, " ") runes = append(runes, rune_) char := strconv.QuoteRune(rune_) chars = append(chars, char[1:len(char)-1]) continue case 13: fmt.Printf("\r\n\r\n%v\r\n\"%s\"\r\n", runes, strings.Join(chars, " ")) break L } } } }
// New returns a Question with the given arguments. // // prefix is the text placed before of the prompt, errPrefix is placed before of // show any error. // // trueString and falseString are the strings to be showed when the question // needs a boolean like answer and it is being used a default value. // It is already handled the next strings like boolean values (from validate.Atob): // 1, t, T, TRUE, true, True, y, Y, yes, YES, Yes // 0, f, F, FALSE, false, False, n, N, no, NO, No func New(prefix, errPrefix, trueString, falseString string) *Question { ter, err := term.New() if err != nil { panic(err) } extraBool := make(map[string]bool) val := validate.New(validate.Bool, validate.None) // Add strings of boolean values if there are not validated like boolean. if _, err = val.Atob(trueString); err != nil { extraBool = map[string]bool{ strings.ToLower(trueString): true, strings.ToUpper(trueString): true, strings.Title(trueString): true, } } if _, err = val.Atob(falseString); err != nil { extraBool[strings.ToLower(falseString)] = false extraBool[strings.ToUpper(falseString)] = false extraBool[strings.Title(falseString)] = false } return &Question{ new(validate.Validate), ter, extraBool, trueString, falseString, prefix, errPrefix, "", "", nil, false, validate.None, } }
// Open will put the terminal in Raw mode and clean the scree func (t *Tty) Open() (err error) { t.saveScreen() t.disableCursor() if t.ManualFlush { t.Flush() } if t.term == nil { t.term, err = term.New() if err != nil { return err } } err = t.term.RawMode() if err != nil { t.restoreScreen() return err } return nil }
// TestCharMode tests terminal set to single character mode. func TestCharMode() { fmt.Print("\n=== RUN TestCharMode\n") ter, _ := term.New() defer func() { if err := ter.Restore(); err != nil { log.Print(err) } }() if err := ter.CharMode(); err != nil { log.Print("expected to set character mode:", err) return } buf := bufio.NewReaderSize(term.Input, 4) reply := []string{"a", "€", "~"} if !*IsInteractive { go func() { for _, r := range reply { time.Sleep(time.Duration(*Time) * time.Second) fmt.Fprint(pw, r) } }() } for i := 1; ; i++ { fmt.Print(" Press key: ") rune, _, err := buf.ReadRune() if err != nil { log.Print(err) return } fmt.Printf("\n pressed: %q\n", string(rune)) if *IsInteractive || i == len(reply) { break } } }
func TestDetectSize() { fmt.Print("\n=== RUN TestDetectSize\n") ter, _ := term.New() defer func() { if err := ter.Restore(); err != nil { log.Print(err) } }() row, col, err := ter.GetSize() if err != nil { panic(err) } winSize := term.DetectWinSize() fmt.Println("[Change the size of the terminal]") // I want to finish the test. go func() { time.Sleep(10 * time.Second) winSize.Change <- true }() <-winSize.Change winSize.Close() row2, col2, err := ter.GetSize() if err != nil { panic(err) } if row == row2 && col == col2 { log.Print("the terminal size got the same value") return } }
func GetCursorPosition() (col int, line int, err error) { // set terminal to raw mode and back t, err := term.New() if err != nil { fallback_SetRawMode() defer fallback_SetCookedMode() } else { t.RawMode() defer t.Restore() } // same as $ echo -e "\033[6n" // by printing the output, we are triggering input fmt.Printf(fmt.Sprintf("\r%c[6n", ESC)) // capture keyboard output from print command reader := bufio.NewReader(os.Stdin) // capture the triggered stdin from the print text, _ := reader.ReadSlice('R') // check for the desired output re := regexp.MustCompile(`\d+;\d+`) res := re.FindString(string(text)) // make sure that cooked mode gets set if res != "" { parts := strings.Split(res, ";") line, _ = strconv.Atoi(parts[0]) col, _ = strconv.Atoi(parts[1]) return col, line, nil } else { return 0, 0, errors.New("unable to read cursor position") } }
// Init sets up our terminal for use. // It must be run before anything else. func Init() { var err error if tty, err = term.New(); err != nil { log.Fatalf("%q fd: %d", err, term.InputFD) } }