diff --git a/client.go b/client.go index 9c13b45..8640e31 100644 --- a/client.go +++ b/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 diff --git a/cui/cui.go b/cui/cui.go index 676fcc6..c3ee14a 100644 --- a/cui/cui.go +++ b/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 diff --git a/termios/consts_linux.go b/termios/consts_linux.go new file mode 100644 index 0000000..ae6e076 --- /dev/null +++ b/termios/consts_linux.go @@ -0,0 +1,10 @@ +// +build linux + +package termios + +import "syscall" + +const ( + getTermiosIoctl = syscall.TCGETS + setTermiosIoctl = syscall.TCSETS +) diff --git a/termios/consts_nonlinux.go b/termios/consts_nonlinux.go new file mode 100644 index 0000000..ca0daf7 --- /dev/null +++ b/termios/consts_nonlinux.go @@ -0,0 +1,10 @@ +// +build !linux + +package termios + +import "syscall" + +const ( + getTermiosIoctl = syscall.TIOCGETA + setTermiosIoctl = syscall.TIOCSETAF +) diff --git a/termios/termios.go b/termios/termios.go new file mode 100644 index 0000000..9e0a5e7 --- /dev/null +++ b/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) +}