diff --git a/term/term.go b/term/term.go index 8503fab3..8f9d2818 100644 --- a/term/term.go +++ b/term/term.go @@ -1,5 +1,23 @@ package term +// Flag represents a terminal state flag. +type Flag interface { + flag() +} + +// These represents the state flag types. +type ( + I uint64 // Input flags + O uint64 // Output flags + C uint64 // Control flags, not supported on Windows + L uint64 // Local flags, not supported on Windows +) + +func (I) flag() {} +func (O) flag() {} +func (C) flag() {} +func (L) flag() {} + // State contains platform-specific state of a terminal. type State struct { state @@ -42,3 +60,19 @@ func GetSize(fd int) (width, height int, err error) { func ReadPassword(fd int) ([]byte, error) { return readPassword(fd) } + +// EnableMode enables terminal modes. +func EnableMode(fd int, flags ...Flag) (*State, error) { + if len(flags) == 0 { + return GetState(fd) + } + return enableMode(fd, flags...) +} + +// DisableMode disables terminal modes. +func DisableMode(fd int, flags ...Flag) (*State, error) { + if len(flags) == 0 { + return GetState(fd) + } + return disableMode(fd, flags...) +} diff --git a/term/term_other.go b/term/term_other.go index aae14549..8a303fde 100644 --- a/term/term_other.go +++ b/term/term_other.go @@ -33,3 +33,11 @@ func getSize(fd int) (width, height int, err error) { func readPassword(fd int) ([]byte, error) { return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) } + +func enableMode(fd int, flags ...Flag) (*State, error) { + return nil, fmt.Errorf("terminal: EnableMode not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +func disableMode(fd int, flags ...Flag) (*State, error) { + return nil, fmt.Errorf("terminal: DisableMode not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} diff --git a/term/term_unix.go b/term/term_unix.go index b0ac6785..5537b522 100644 --- a/term/term_unix.go +++ b/term/term_unix.go @@ -86,3 +86,51 @@ func readPassword(fd int) ([]byte, error) { return readPasswordLine(passwordReader(fd)) } + +func change(fd int, enable bool, flags ...Flag) (*State, error) { + termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + if err != nil { + return nil, err + } + + var iflags, oflags, lflags, cflags uint64 + for _, f := range flags { + switch f := f.(type) { + case I: + iflags |= uint64(f) + case O: + oflags |= uint64(f) + case L: + lflags |= uint64(f) + case C: + cflags |= uint64(f) + } + } + + // TODO: support termios.Cc and termios.[I|o]speed + if enable { + termios.Iflag |= iflags + termios.Oflag |= oflags + termios.Lflag |= lflags + termios.Cflag |= cflags + } else { + termios.Iflag &^= iflags + termios.Oflag &^= oflags + termios.Lflag &^= lflags + termios.Cflag &^= cflags + } + + if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil { + return nil, err + } + + return &State{state{Termios: *termios}}, nil +} + +func enableMode(fd int, flags ...Flag) (*State, error) { + return change(fd, true, flags...) +} + +func disableMode(fd int, flags ...Flag) (*State, error) { + return change(fd, false, flags...) +}