Replace calls to `stty` with <termios.h> cgo

This removes the dependency on `stty` and is the standard POSIX way of
modifying terminal state. It requires cgo, so cross-compiling might not
be trivial. Otherwise there are no additional dependencies brought in,
and this already supports all POSIXish platforms.

Fixes #154.
This commit is contained in:
Hannu Hartikainen 2020-05-23 11:49:30 +03:00
parent 36ae4a228f
commit 60c1c6cff4
3 changed files with 51 additions and 43 deletions

View File

@ -4,7 +4,6 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
@ -19,6 +18,7 @@ import (
"tildegit.org/sloum/bombadillo/http"
"tildegit.org/sloum/bombadillo/local"
"tildegit.org/sloum/bombadillo/telnet"
"tildegit.org/sloum/bombadillo/termios"
)
//------------------------------------------------\\
@ -43,14 +43,7 @@ type client struct {
//--------------------------------------------------\\
func (c *client) GetSizeOnce() {
cmd := exec.Command("stty", "size")
cmd.Stdin = os.Stdin
out, err := cmd.Output()
if err != nil {
cui.Exit(5, "Fatal error: Unable to retrieve terminal size")
}
var h, w int
_, _ = fmt.Sscan(string(out), &h, &w)
var w, h = termios.GetWindowSize()
c.Height = h
c.Width = w
}
@ -61,14 +54,7 @@ func (c *client) GetSize() {
c.Draw()
for {
cmd := exec.Command("stty", "size")
cmd.Stdin = os.Stdin
out, err := cmd.Output()
if err != nil {
cui.Exit(5, "Fatal error: Unable to retrieve terminal size")
}
var h, w int
_, _ = fmt.Sscan(string(out), &h, &w)
var w, h = termios.GetWindowSize()
if h != c.Height || w != c.Width {
c.Height = h
c.Width = w

View File

@ -5,6 +5,8 @@ import (
"fmt"
"os"
"os/exec"
"tildegit.org/sloum/bombadillo/termios"
)
var Shapes = map[string]string{
@ -55,7 +57,7 @@ func Exit(exitCode int, msg string) {
// InitTerm sets the terminal modes appropriate for Bombadillo
func InitTerm() {
SetCharMode()
termios.SetCharMode()
Tput("smcup") // use alternate screen
Tput("rmam") // turn off line wrapping
}
@ -64,7 +66,7 @@ func InitTerm() {
func CleanupTerm() {
moveCursorToward("down", 500)
moveCursorToward("right", 500)
SetLineMode()
termios.SetLineMode()
fmt.Print("\n")
fmt.Print("\033[?25h") // reenables cursor blinking
@ -98,7 +100,7 @@ func Getch() rune {
}
func GetLine(prefix string) (string, error) {
SetLineMode()
termios.SetLineMode()
reader := bufio.NewReader(os.Stdin)
fmt.Print(prefix)
@ -107,32 +109,10 @@ func GetLine(prefix string) (string, error) {
return "", err
}
SetCharMode()
termios.SetCharMode()
return text[:len(text)-1], nil
}
func SetCharMode() {
cmd := exec.Command("stty", "cbreak", "-echo")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
panic(err)
}
fmt.Print("\033[?25l")
}
func SetLineMode() {
cmd := exec.Command("stty", "-cbreak", "echo")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
panic(err)
}
}
func Tput(opt string) {
cmd := exec.Command("tput", opt)
cmd.Stdin = os.Stdin

42
termios/termios.go Normal file
View File

@ -0,0 +1,42 @@
// +build cgo
package termios
/*
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
// NOTE: ioctl() is variadic so cgo requires a wrapper
int getWinsize(struct winsize* ws) {
return ioctl(STDOUT_FILENO, TIOCGWINSZ, ws);
}
*/
import "C"
func GetWindowSize() (int, int) {
var value C.struct_winsize
C.getWinsize(&value)
return int(value.ws_col), int(value.ws_row)
}
func getTermios() C.struct_termios {
var termios C.struct_termios
C.tcgetattr(C.STDIN_FILENO, &termios)
return termios
}
func setTermios(termios C.struct_termios) {
C.tcsetattr(C.STDIN_FILENO, C.TCSAFLUSH, &termios)
}
func SetCharMode() {
var t = getTermios()
t.c_lflag = t.c_lflag &^ (C.ICANON | C.ECHO)
setTermios(t)
}
func SetLineMode() {
var t = getTermios()
t.c_lflag = t.c_lflag | (C.ICANON | C.ECHO)
setTermios(t)
}