Browse Source

Replace calls to `stty` with syscalls

- Move termios-related functionality to its own package
- Reimplement stty calls using ioctl calls
- Add per-platform constants for linux and non-linux because the ioctl
  enums are different. The enums in Darwin seem to come from BSD and so
  I'm assuming they might also work for other operating systems. If not,
  we'll need to add other build-constrained const files.

This code has been tested on Darwin and will be tested on Linux before
merging.
pull/158/head
Hannu Hartikainen 11 months ago
parent
commit
e3e2afc4fc
  1. 20
      client.go
  2. 37
      cui/cui.go
  3. 10
      termios/consts_linux.go
  4. 10
      termios/consts_nonlinux.go
  5. 60
      termios/termios.go

20
client.go

@ -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

37
cui/cui.go

@ -5,6 +5,8 @@ import (
"fmt"
"os"
"os/exec"
"tildegit.org/sloum/bombadillo/termios"
)
var Shapes = map[string]string{
@ -55,16 +57,17 @@ func Exit(exitCode int, msg string) {
// InitTerm sets the terminal modes appropriate for Bombadillo
func InitTerm() {
SetCharMode()
Tput("smcup") // use alternate screen
Tput("rmam") // turn off line wrapping
termios.SetCharMode()
Tput("smcup") // use alternate screen
Tput("rmam") // turn off line wrapping
fmt.Print("\033[?25l") // hide cursor
}
// CleanupTerm reverts changs to terminal mode made by InitTerm
func CleanupTerm() {
moveCursorToward("down", 500)
moveCursorToward("right", 500)
SetLineMode()
termios.SetLineMode()
fmt.Print("\n")
fmt.Print("\033[?25h") // reenables cursor blinking
@ -98,7 +101,7 @@ func Getch() rune {
}
func GetLine(prefix string) (string, error) {
SetLineMode()
termios.SetLineMode()
reader := bufio.NewReader(os.Stdin)
fmt.Print(prefix)
@ -107,32 +110,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

10
termios/consts_linux.go

@ -0,0 +1,10 @@
// +build linux
package termios
import "syscall"
const (
getTermiosIoctl = syscall.TCGETS
setTermiosIoctl = syscall.TCSETS
)

10
termios/consts_nonlinux.go

@ -0,0 +1,10 @@
// +build !linux
package termios
import "syscall"
const (
getTermiosIoctl = syscall.TIOCGETA
setTermiosIoctl = syscall.TIOCSETAF
)

60
termios/termios.go

@ -0,0 +1,60 @@
package termios
import (
"os"
"runtime"
"syscall"
"unsafe"
)
type winsize struct {
Row uint16
Col uint16
Xpixel uint16
Ypixel uint16
}
var fd = os.Stdin.Fd()
func ioctl(fd, request, argp uintptr) error {
if _, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, request, argp); e != 0 {
return e
}
return nil
}
func GetWindowSize() (int, int) {
var value winsize
ioctl(fd, syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&value)))
return int(value.Col), int(value.Row)
}
func getTermios() syscall.Termios {
var value syscall.Termios
err := ioctl(fd, getTermiosIoctl, uintptr(unsafe.Pointer(&value)))
if err != nil {
panic(err)
}
return value
}
func setTermios(termios syscall.Termios) {
err := ioctl(fd, setTermiosIoctl, uintptr(unsafe.Pointer(&termios)))
if err != nil {
panic(err)
}
runtime.KeepAlive(termios)
}
func SetCharMode() {
t := getTermios()
t.Lflag = t.Lflag ^ syscall.ICANON
t.Lflag = t.Lflag ^ syscall.ECHO
setTermios(t)
}
func SetLineMode() {
var t = getTermios()
t.Lflag = t.Lflag | (syscall.ICANON | syscall.ECHO)
setTermios(t)
}
Loading…
Cancel
Save