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" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv" "strconv"
@ -19,6 +18,7 @@ import (
"tildegit.org/sloum/bombadillo/http" "tildegit.org/sloum/bombadillo/http"
"tildegit.org/sloum/bombadillo/local" "tildegit.org/sloum/bombadillo/local"
"tildegit.org/sloum/bombadillo/telnet" "tildegit.org/sloum/bombadillo/telnet"
"tildegit.org/sloum/bombadillo/termios"
) )
//------------------------------------------------\\ //------------------------------------------------\\
@ -43,14 +43,7 @@ type client struct {
//--------------------------------------------------\\ //--------------------------------------------------\\
func (c *client) GetSizeOnce() { func (c *client) GetSizeOnce() {
cmd := exec.Command("stty", "size") var w, h = termios.GetWindowSize()
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)
c.Height = h c.Height = h
c.Width = w c.Width = w
} }
@ -61,14 +54,7 @@ func (c *client) GetSize() {
c.Draw() c.Draw()
for { for {
cmd := exec.Command("stty", "size") var w, h = termios.GetWindowSize()
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)
if h != c.Height || w != c.Width { if h != c.Height || w != c.Width {
c.Height = h c.Height = h
c.Width = w c.Width = w

View File

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