From 646fb36ddfaa30c0707517beb27c3d65dd27af87 Mon Sep 17 00:00:00 2001 From: Jeff Lindsay Date: Tue, 4 Sep 2018 19:37:29 -0500 Subject: [PATCH] add driver for setting up tty file descriptors, allowing use with SSH servers Signed-off-by: Jeff Lindsay --- driver.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++ tscreen.go | 9 ++++++++ tscreen_bsd.go | 15 ++++-------- tscreen_darwin.go | 15 ++++-------- tscreen_linux.go | 12 ++++------ tscreen_posix.go | 11 ++++----- 6 files changed, 88 insertions(+), 33 deletions(-) create mode 100644 driver.go diff --git a/driver.go b/driver.go new file mode 100644 index 00000000..e5c12960 --- /dev/null +++ b/driver.go @@ -0,0 +1,59 @@ +package tcell + +import ( + "errors" + "os" + "os/signal" + "syscall" +) + +// ErrWinSizeUnused is for TermDrivers to signal to use the default platform +// window size lookup method +var ErrWinSizeUnused = errors.New("driver does not provide WinSize") + +// TermDriver allows you to customize the TTY used by Screen, +// most notably to support a PTY pair that can be used with SSH servers. +type TermDriver interface { + // Init sets up two file TTY/PTY file descriptors, which may be the same + // in some cases. It also takes a chan that is used to notify the Screen + // refresh the window size. + Init(winch chan os.Signal) (in *os.File, out *os.File, err error) + + // Fini is called before the cleanup of Screen's Fini. It's typically used + // to unsubscribe window change signals. + Fini() + + // WinSize returns the current window width and height. It can also return + // ErrWinSizeUnused to tell Screen to use platform syscalls to get the + // window size from the out file descriptor. + WinSize() (width int, height int, err error) +} + +// defaultTermDriver is what's used when you don't specify a custom TermDriver +type defaultTermDriver struct { + winch chan os.Signal + out *os.File +} + +func (d *defaultTermDriver) Init(winch chan os.Signal) (in *os.File, out *os.File, err error) { + in, err = os.OpenFile("/dev/tty", os.O_RDONLY, 0) + if err != nil { + return + } + out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0) + if err != nil { + return + } + signal.Notify(winch, syscall.SIGWINCH) + d.winch = winch + d.out = out + return +} + +func (d *defaultTermDriver) Fini() { + signal.Stop(d.winch) +} + +func (d *defaultTermDriver) WinSize() (int, int, error) { + return 0, 0, ErrWinSizeUnused +} diff --git a/tscreen.go b/tscreen.go index 4c64e833..5267adbb 100644 --- a/tscreen.go +++ b/tscreen.go @@ -42,6 +42,7 @@ func NewTerminfoScreen() (Screen, error) { return nil, e } t := &tScreen{ti: ti} + t.driver = &defaultTermDriver{} t.keyexist = make(map[Key]bool) t.keycodes = make(map[string]*tKeyCode) @@ -68,6 +69,7 @@ type tKeyCode struct { // tScreen represents a screen backed by a terminfo implementation. type tScreen struct { ti *terminfo.Terminfo + driver TermDriver h int w int fini bool @@ -1386,3 +1388,10 @@ func (t *tScreen) HasKey(k Key) bool { } func (t *tScreen) Resize(int, int, int, int) {} + +// SetDriver is used to replace the default TermDriver. +// When using this package, you'll want to make an interface +// and type assert your Screen to get this method. +func (t *tScreen) SetDriver(driver TermDriver) { + t.driver = driver +} diff --git a/tscreen_bsd.go b/tscreen_bsd.go index 86d749b7..67dbcabc 100644 --- a/tscreen_bsd.go +++ b/tscreen_bsd.go @@ -17,8 +17,6 @@ package tcell import ( - "os" - "os/signal" "syscall" "unsafe" ) @@ -33,10 +31,7 @@ func (t *tScreen) termioInit() error { var ioc uintptr t.tiosp = &termiosPrivate{} - if t.in, e = os.OpenFile("/dev/tty", os.O_RDONLY, 0); e != nil { - goto failed - } - if t.out, e = os.OpenFile("/dev/tty", os.O_WRONLY, 0); e != nil { + if t.in, t.out, e = t.driver.Init(t.sigwinch); e != nil { goto failed } @@ -69,8 +64,6 @@ func (t *tScreen) termioInit() error { goto failed } - signal.Notify(t.sigwinch, syscall.SIGWINCH) - if w, h, e := t.getWinSize(); e == nil && w != 0 && h != 0 { t.cells.Resize(w, h) } @@ -89,7 +82,7 @@ failed: func (t *tScreen) termioFini() { - signal.Stop(t.sigwinch) + t.driver.Fini() <-t.indoneq @@ -106,7 +99,9 @@ func (t *tScreen) termioFini() { } func (t *tScreen) getWinSize() (int, int, error) { - + if w, h, err := t.driver.WinSize(); err != ErrWinSizeUnused { + return w, h, err + } fd := uintptr(t.out.Fd()) dim := [4]uint16{} dimp := uintptr(unsafe.Pointer(&dim)) diff --git a/tscreen_darwin.go b/tscreen_darwin.go index df51cb5f..117c648c 100644 --- a/tscreen_darwin.go +++ b/tscreen_darwin.go @@ -32,8 +32,6 @@ package tcell // a long time (probably forever) so holding one's breath is contraindicated. import ( - "os" - "os/signal" "syscall" "unsafe" ) @@ -48,10 +46,7 @@ func (t *tScreen) termioInit() error { var ioc uintptr t.tiosp = &termiosPrivate{} - if t.in, e = os.OpenFile("/dev/tty", os.O_RDONLY, 0); e != nil { - goto failed - } - if t.out, e = os.OpenFile("/dev/tty", os.O_WRONLY, 0); e != nil { + if t.in, t.out, e = t.driver.Init(t.sigwinch); e != nil { goto failed } @@ -84,8 +79,6 @@ func (t *tScreen) termioInit() error { goto failed } - signal.Notify(t.sigwinch, syscall.SIGWINCH) - if w, h, e := t.getWinSize(); e == nil && w != 0 && h != 0 { t.cells.Resize(w, h) } @@ -104,7 +97,7 @@ failed: func (t *tScreen) termioFini() { - signal.Stop(t.sigwinch) + t.driver.Fini() <-t.indoneq @@ -127,7 +120,9 @@ func (t *tScreen) termioFini() { } func (t *tScreen) getWinSize() (int, int, error) { - + if w, h, err := t.driver.WinSize(); err != ErrWinSizeUnused { + return w, h, err + } fd := uintptr(t.out.Fd()) dim := [4]uint16{} dimp := uintptr(unsafe.Pointer(&dim)) diff --git a/tscreen_linux.go b/tscreen_linux.go index 79602f4c..a2360060 100644 --- a/tscreen_linux.go +++ b/tscreen_linux.go @@ -17,7 +17,6 @@ package tcell import ( - "os" "os/signal" "syscall" "unsafe" @@ -33,10 +32,7 @@ func (t *tScreen) termioInit() error { var ioc uintptr t.tiosp = &termiosPrivate{} - if t.in, e = os.OpenFile("/dev/tty", os.O_RDONLY, 0); e != nil { - goto failed - } - if t.out, e = os.OpenFile("/dev/tty", os.O_WRONLY, 0); e != nil { + if t.in, t.out, e = t.driver.Init(t.sigwinch); e != nil { goto failed } @@ -98,7 +94,7 @@ failed: func (t *tScreen) termioFini() { - signal.Stop(t.sigwinch) + t.driver.Fini() <-t.indoneq @@ -116,7 +112,9 @@ func (t *tScreen) termioFini() { } func (t *tScreen) getWinSize() (int, int, error) { - + if w, h, err := t.driver.WinSize(); err != ErrWinSizeUnused { + return w, h, err + } fd := uintptr(t.out.Fd()) dim := [4]uint16{} dimp := uintptr(unsafe.Pointer(&dim)) diff --git a/tscreen_posix.go b/tscreen_posix.go index 66fbe04b..fff159f9 100644 --- a/tscreen_posix.go +++ b/tscreen_posix.go @@ -17,7 +17,6 @@ package tcell import ( - "os" "os/signal" "syscall" ) @@ -128,10 +127,7 @@ func (t *tScreen) termioInit() error { var newtios C.struct_termios var fd C.int - if t.in, e = os.OpenFile("/dev/tty", os.O_RDONLY, 0); e != nil { - goto failed - } - if t.out, e = os.OpenFile("/dev/tty", os.O_WRONLY, 0); e != nil { + if t.in, t.out, e = t.driver.Init(t.sigwinch); e != nil { goto failed } @@ -183,7 +179,7 @@ failed: func (t *tScreen) termioFini() { - signal.Stop(t.sigwinch) + t.driver.Fini() <-t.indoneq @@ -198,6 +194,9 @@ func (t *tScreen) termioFini() { } func (t *tScreen) getWinSize() (int, int, error) { + if w, h, err := t.driver.WinSize(); err != ErrWinSizeUnused { + return w, h, err + } var cx, cy C.int if r, e := C.getwinsize(C.int(t.out.Fd()), &cx, &cy); r != 0 { return 0, 0, e