123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- // Copyright 2010 Jonas mg
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- // +build !plan9,!windows
- package term
- import (
- "io"
- "os"
- "syscall"
- "github.com/kless/term/sys"
- )
- // Default values for input and output.
- var (
- InputFD int = syscall.Stdin
- Input io.Reader = os.Stdin
- Output io.Writer = os.Stdout
- )
- // A Terminal represents a general terminal interface.
- type Terminal struct {
- mode modeType
- // Contain the state of a terminal, enabling to restore the original settings
- oldState, lastState sys.Termios
- // Window size
- size sys.Winsize
- fd int // File descriptor
- }
- // New creates a new terminal interface in the file descriptor InputFD.
- func New() (*Terminal, error) {
- var t Terminal
- // Get the actual state
- if err := sys.Getattr(InputFD, &t.lastState); err != nil {
- return nil, os.NewSyscallError("sys.Getattr", err)
- }
- t.oldState = t.lastState // the actual state is copied to another one
- t.fd = InputFD
- return &t, nil
- }
- // == Restore
- //
- type State struct {
- wrap sys.Termios
- }
- // OriginalState returns the terminal's original state.
- func (t *Terminal) OriginalState() State {
- return State{t.oldState}
- }
- // Restore restores the original settings for the term.
- func (t *Terminal) Restore() error {
- if t.mode != 0 {
- if err := sys.Setattr(t.fd, sys.TCSANOW, &t.oldState); err != nil {
- return os.NewSyscallError("sys.Setattr", err)
- }
- t.lastState = t.oldState
- t.mode = 0
- }
- return nil
- }
- // Restore restores the settings from State.
- func Restore(fd int, st State) error {
- if err := sys.Setattr(fd, sys.TCSANOW, &st.wrap); err != nil {
- return os.NewSyscallError("sys.Setattr", err)
- }
- return nil
- }
- // == Modes
- //
- // RawMode sets the terminal to something like the "raw" mode. Input is available
- // character by character, echoing is disabled, and all special processing of
- // terminal input and output characters is disabled.
- //
- // NOTE: in tty "raw mode", CR+LF is used for output and CR is used for input.
- func (t *Terminal) RawMode() error {
- // Input modes - no break, no CR to NL, no NL to CR, no carriage return,
- // no strip char, no start/stop output control, no parity check.
- t.lastState.Iflag &^= (sys.BRKINT | sys.IGNBRK | sys.ICRNL | sys.INLCR |
- sys.IGNCR | sys.ISTRIP | sys.IXON | sys.PARMRK)
- // Output modes - disable post processing.
- t.lastState.Oflag &^= sys.OPOST
- // Local modes - echoing off, canonical off, no extended functions,
- // no signal chars (^Z,^C).
- t.lastState.Lflag &^= (sys.ECHO | sys.ECHONL | sys.ICANON | sys.IEXTEN | sys.ISIG)
- // Control modes - set 8 bit chars.
- t.lastState.Cflag &^= (sys.CSIZE | sys.PARENB)
- t.lastState.Cflag |= sys.CS8
- // Control chars - set return condition: min number of bytes and timer.
- // We want read to return every single byte, without timeout.
- t.lastState.Cc[sys.VMIN] = 1 // Read returns when one char is available.
- t.lastState.Cc[sys.VTIME] = 0
- // Put the terminal in raw mode after flushing
- if err := sys.Setattr(t.fd, sys.TCSAFLUSH, &t.lastState); err != nil {
- return os.NewSyscallError("sys.Setattr", err)
- }
- t.mode |= RawMode
- return nil
- }
- // EchoMode turns the echo mode.
- func (t *Terminal) EchoMode(echo bool) error {
- if !echo {
- //t.lastState.Lflag &^= (sys.ECHO | sys.ECHOE | sys.ECHOK | sys.ECHONL)
- t.lastState.Lflag &^= sys.ECHO
- } else {
- //t.lastState.Lflag |= (sys.ECHO | sys.ECHOE | sys.ECHOK | sys.ECHONL)
- t.lastState.Lflag |= sys.ECHO
- }
- if err := sys.Setattr(t.fd, sys.TCSANOW, &t.lastState); err != nil {
- return os.NewSyscallError("sys.Setattr", err)
- }
- if echo {
- t.mode |= EchoMode
- } else {
- t.mode &^= EchoMode
- }
- return nil
- }
- // CharMode sets the terminal to single-character mode.
- func (t *Terminal) CharMode() error {
- // Disable canonical mode, and set buffer size to 1 byte.
- t.lastState.Lflag &^= sys.ICANON
- t.lastState.Cc[sys.VTIME] = 0
- t.lastState.Cc[sys.VMIN] = 1
- if err := sys.Setattr(t.fd, sys.TCSANOW, &t.lastState); err != nil {
- return os.NewSyscallError("sys.Setattr", err)
- }
- t.mode |= CharMode
- return nil
- }
- // SetMode sets the terminal attributes given by state.
- // Warning: The use of this function is not cross-system.
- func (t *Terminal) SetMode(state sys.Termios) error {
- if err := sys.Setattr(t.fd, sys.TCSANOW, &state); err != nil {
- return os.NewSyscallError("sys.Setattr", err)
- }
- t.lastState = state
- t.mode |= OtherMode
- return nil
- }
- // == Utility
- //
- // Fd returns the file descriptor referencing the term.
- func (t *Terminal) Fd() int {
- return t.fd
- }
- // GetSize returns the size of the term.
- func (t *Terminal) GetSize() (row, column int, err error) {
- if err = sys.GetWinsize(syscall.Stdout, &t.size); err != nil {
- return
- }
- return int(t.size.Row), int(t.size.Col), nil
- }
|