From 459557b90308d1d58ca4ad84793022e643d553d9 Mon Sep 17 00:00:00 2001 From: niten94 Date: Tue, 30 Sep 2025 21:10:00 +0800 Subject: [PATCH 1/4] Merge tscreen_windows.go with tscreen_stub.go --- tscreen_stub.go | 7 +++++-- tscreen_windows.go | 44 -------------------------------------------- 2 files changed, 5 insertions(+), 46 deletions(-) delete mode 100644 tscreen_windows.go diff --git a/tscreen_stub.go b/tscreen_stub.go index 0c7d0122..e599a610 100644 --- a/tscreen_stub.go +++ b/tscreen_stub.go @@ -1,4 +1,4 @@ -// +build nacl plan9 +// +build nacl plan9 windows // Copyright 2015 The TCell Authors // @@ -16,7 +16,10 @@ package tcell -// This stub file is for systems that have no termios. +// On Windows we don't have support for termios. We probably could, and +// may should, in a cygwin type environment. Its not clear how to make +// this all work nicely with both cygwin and Windows console, so we +// decline to do so here. type termiosPrivate struct{} diff --git a/tscreen_windows.go b/tscreen_windows.go deleted file mode 100644 index e0ae2eeb..00000000 --- a/tscreen_windows.go +++ /dev/null @@ -1,44 +0,0 @@ -// +build windows - -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use file except in compliance with the License. -// You may obtain a copy of the license at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tcell - -// On Windows we don't have support for termios. We probably could, and -// may should, in a cygwin type environment. Its not clear how to make -// this all work nicely with both cygwin and Windows console, so we -// decline to do so here. - -func (t *tScreen) termioInit() error { - return ErrNoScreen -} - -func (t *tScreen) termioFini() { - return -} - -func (t *tScreen) getWinSize() (int, int, error) { - return 0, 0, ErrNoScreen -} - -func (t *tScreen) getCharset() string { - return "UTF-16LE" -} - -func (t *tScreen) Beep() error { - return ErrNoScreen -} - -type termiosPrivate struct{} From 1c71155ce3b9137cfe6adc08e55c01b35805ca76 Mon Sep 17 00:00:00 2001 From: niten94 Date: Tue, 30 Sep 2025 22:20:29 +0800 Subject: [PATCH 2/4] Set and restore raw mode using x/term on Linux On Linux, set the terminal to raw mode and restore it using MakeRaw() and Restore() in x/term respectively, which is used upstream in place of x/sys/unix.IoctlSetTermios() or syscall.Syscall6() in other platforms. MakeRaw() performs the same changes with certain termios fields, but copies the whole struct prior instead of those fields only. --- go.mod | 3 ++- go.sum | 4 ++++ tscreen.go | 2 ++ tscreen_linux.go | 44 +++++++------------------------------------- 4 files changed, 15 insertions(+), 38 deletions(-) diff --git a/go.mod b/go.mod index 0b551637..7ed2618e 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,8 @@ require ( github.com/mattn/go-runewidth v0.0.7 github.com/xo/terminfo v0.0.0-20200218205459-454e5b68f9e8 github.com/zyedidia/poller v1.0.1 - golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 + golang.org/x/sys v0.30.0 + golang.org/x/term v0.29.0 golang.org/x/text v0.3.0 ) diff --git a/go.sum b/go.sum index bdc40ef7..e98db952 100644 --- a/go.sum +++ b/go.sum @@ -10,5 +10,9 @@ github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/tscreen.go b/tscreen.go index 1bb0e5ad..29b1bf34 100644 --- a/tscreen.go +++ b/tscreen.go @@ -28,6 +28,7 @@ import ( "time" "unicode/utf8" + "golang.org/x/term" "golang.org/x/text/transform" "github.com/micro-editor/tcell/v2/terminfo" @@ -140,6 +141,7 @@ type tScreen struct { buttondn bool rawseq []string finiOnce sync.Once + saved *term.State sync.Mutex } diff --git a/tscreen_linux.go b/tscreen_linux.go index 63499a7c..7956ec20 100644 --- a/tscreen_linux.go +++ b/tscreen_linux.go @@ -23,16 +23,14 @@ import ( "syscall" "golang.org/x/sys/unix" + "golang.org/x/term" ) -type termiosPrivate struct { - tio *unix.Termios -} +type termiosPrivate struct{} func (t *tScreen) termioInit() error { var e error - var raw *unix.Termios - var tio *unix.Termios + var state *term.State if t.in, e = os.OpenFile("/dev/tty", os.O_RDONLY, 0); e != nil { goto failed @@ -41,40 +39,12 @@ func (t *tScreen) termioInit() error { goto failed } - tio, e = unix.IoctlGetTermios(int(t.out.(*os.File).Fd()), unix.TCGETS) + state, e = term.MakeRaw(int(t.out.(*os.File).Fd())) if e != nil { goto failed } - t.tiosp = &termiosPrivate{tio: tio} - - // make a local copy, to make it raw - raw = &unix.Termios{ - Cflag: tio.Cflag, - Oflag: tio.Oflag, - Iflag: tio.Iflag, - Lflag: tio.Lflag, - Cc: tio.Cc, - } - raw.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | - unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) - raw.Oflag &^= unix.OPOST - raw.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | - unix.IEXTEN) - raw.Cflag &^= (unix.CSIZE | unix.PARENB) - raw.Cflag |= unix.CS8 - - // This is setup for blocking reads. In the past we attempted to - // use non-blocking reads, but now a separate input loop and timer - // copes with the problems we had on some systems (BSD/Darwin) - // where close hung forever. - raw.Cc[unix.VMIN] = 1 - raw.Cc[unix.VTIME] = 0 - - e = unix.IoctlSetTermios(int(t.out.(*os.File).Fd()), unix.TCSETS, raw) - if e != nil { - goto failed - } + t.saved = state signal.Notify(t.sigwinch, syscall.SIGWINCH) @@ -100,8 +70,8 @@ func (t *tScreen) termioFini() { <-t.indoneq - if t.out != nil && t.tiosp != nil { - unix.IoctlSetTermios(int(t.out.(*os.File).Fd()), unix.TCSETSF, t.tiosp.tio) + if t.out != nil && t.saved != nil { + term.Restore(int(t.out.(*os.File).Fd()), t.saved) t.out.(*os.File).Close() } From 06d8d0acc15ab01838a95ec1742bc0a448bb7430 Mon Sep 17 00:00:00 2001 From: niten94 Date: Wed, 1 Oct 2025 20:27:09 +0800 Subject: [PATCH 3/4] Use Linux screen implementation on most Unix systems Switch to Linux screen implementation on all Unix platforms excluding MacOS, and add Unix platforms that are supported upstream only to the build constraint. The modifications on certain termios fields only differ on BSD systems where VMIN and VTIME were not modified. --- tscreen_bsd.go | 121 --------------------------- tscreen_solaris.go | 122 ---------------------------- tscreen_linux.go => tscreen_unix.go | 2 +- 3 files changed, 1 insertion(+), 244 deletions(-) delete mode 100644 tscreen_bsd.go delete mode 100644 tscreen_solaris.go rename tscreen_linux.go => tscreen_unix.go (97%) diff --git a/tscreen_bsd.go b/tscreen_bsd.go deleted file mode 100644 index b10e2224..00000000 --- a/tscreen_bsd.go +++ /dev/null @@ -1,121 +0,0 @@ -// +build freebsd netbsd openbsd dragonfly - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use file except in compliance with the License. -// You may obtain a copy of the license at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tcell - -import ( - "os" - "os/signal" - "syscall" - "unsafe" -) - -type termiosPrivate syscall.Termios - -func (t *tScreen) termioInit() error { - var e error - var newtios termiosPrivate - var fd uintptr - var tios uintptr - 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 { - goto failed - } - - tios = uintptr(unsafe.Pointer(t.tiosp)) - ioc = uintptr(syscall.TIOCGETA) - fd = uintptr(t.out.(*os.File).Fd()) - if _, _, e1 := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0); e1 != 0 { - e = e1 - goto failed - } - - newtios = *t.tiosp - newtios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | - syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | - syscall.ICRNL | syscall.IXON - newtios.Oflag &^= syscall.OPOST - newtios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | - syscall.ISIG | syscall.IEXTEN - newtios.Cflag &^= syscall.CSIZE | syscall.PARENB - newtios.Cflag |= syscall.CS8 - - tios = uintptr(unsafe.Pointer(&newtios)) - - ioc = uintptr(syscall.TIOCSETA) - if _, _, e1 := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0); e1 != 0 { - e = e1 - 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) - } - - return nil - -failed: - if t.in != nil { - t.in.(*os.File).Close() - } - if t.out != nil { - t.out.(*os.File).Close() - } - return e -} - -func (t *tScreen) termioFini() { - - signal.Stop(t.sigwinch) - - <-t.indoneq - - if t.out != nil { - fd := uintptr(t.out.(*os.File).Fd()) - ioc := uintptr(syscall.TIOCSETAF) - tios := uintptr(unsafe.Pointer(t.tiosp)) - syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0) - t.out.(*os.File).Close() - } - if t.in != nil { - t.in.(*os.File).Close() - } -} - -func (t *tScreen) getWinSize() (int, int, error) { - - fd := uintptr(t.out.(*os.File).Fd()) - dim := [4]uint16{} - dimp := uintptr(unsafe.Pointer(&dim)) - ioc := uintptr(syscall.TIOCGWINSZ) - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, - fd, ioc, dimp, 0, 0, 0); err != 0 { - return -1, -1, err - } - return int(dim[1]), int(dim[0]), nil -} - -func (t *tScreen) Beep() error { - t.writeString(string(byte(7))) - return nil -} diff --git a/tscreen_solaris.go b/tscreen_solaris.go deleted file mode 100644 index b0c7272f..00000000 --- a/tscreen_solaris.go +++ /dev/null @@ -1,122 +0,0 @@ -// +build solaris illumos - -// Copyright 2020 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use file except in compliance with the License. -// You may obtain a copy of the license at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tcell - -import ( - "os" - "os/signal" - "syscall" - - "golang.org/x/sys/unix" -) - -type termiosPrivate struct { - tio *unix.Termios -} - -func (t *tScreen) termioInit() error { - var e error - var raw *unix.Termios - var tio *unix.Termios - - 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 { - goto failed - } - - tio, e = unix.IoctlGetTermios(int(t.out.(*os.File).Fd()), unix.TCGETS) - if e != nil { - goto failed - } - - t.tiosp = &termiosPrivate{tio: tio} - - // make a local copy, to make it raw - raw = &unix.Termios{ - Cflag: tio.Cflag, - Oflag: tio.Oflag, - Iflag: tio.Iflag, - Lflag: tio.Lflag, - Cc: tio.Cc, - } - - raw.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.INLCR | - unix.IGNCR | unix.ICRNL | unix.IXON) - raw.Oflag &^= unix.OPOST - raw.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) - raw.Cflag &^= (unix.CSIZE | unix.PARENB) - raw.Cflag |= unix.CS8 - - // This is setup for blocking reads. In the past we attempted to - // use non-blocking reads, but now a separate input loop and timer - // copes with the problems we had on some systems (BSD/Darwin) - // where close hung forever. - raw.Cc[unix.VMIN] = 1 - raw.Cc[unix.VTIME] = 0 - - e = unix.IoctlSetTermios(int(t.out.(*os.File).Fd()), unix.TCSETS, raw) - if e != nil { - 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) - } - - return nil - -failed: - if t.in != nil { - t.in.(*os.File).Close() - } - if t.out != nil { - t.out.(*os.File).Close() - } - return e -} - -func (t *tScreen) termioFini() { - - signal.Stop(t.sigwinch) - - <-t.indoneq - - if t.out != nil && t.tiosp != nil { - unix.IoctlSetTermios(int(t.out.(*os.File).Fd()), unix.TCSETSF, t.tiosp.tio) - t.out.(*os.File).Close() - } - if t.in != nil { - t.in.(*os.File).Close() - } -} - -func (t *tScreen) getWinSize() (int, int, error) { - wsz, err := unix.IoctlGetWinsize(int(t.out.(*os.File).Fd()), unix.TIOCGWINSZ) - if err != nil { - return -1, -1, err - } - return int(wsz.Col), int(wsz.Row), nil -} - -func (t *tScreen) Beep() error { - t.writeString(string(byte(7))) - return nil -} diff --git a/tscreen_linux.go b/tscreen_unix.go similarity index 97% rename from tscreen_linux.go rename to tscreen_unix.go index 7956ec20..8d18594a 100644 --- a/tscreen_linux.go +++ b/tscreen_unix.go @@ -1,4 +1,4 @@ -// +build linux +// +build aix dragonfly freebsd linux netbsd openbsd solaris zos // Copyright 2019 The TCell Authors // From 46e2256e17e93206cee88b56412af29f7d4aacaa Mon Sep 17 00:00:00 2001 From: niten94 Date: Mon, 13 Oct 2025 23:14:44 +0800 Subject: [PATCH 4/4] Use generic Unix screen implementation on MacOS The differences with the original implementation are the same with BSD systems. --- tscreen.go | 1 - tscreen_darwin.go | 142 ----------------------------------------- tscreen_unix.go | 23 +++---- tscreen_unix_darwin.go | 46 +++++++++++++ tscreen_unix_other.go | 26 ++++++++ 5 files changed, 81 insertions(+), 157 deletions(-) delete mode 100644 tscreen_darwin.go create mode 100644 tscreen_unix_darwin.go create mode 100644 tscreen_unix_other.go diff --git a/tscreen.go b/tscreen.go index 29b1bf34..f5849f2e 100644 --- a/tscreen.go +++ b/tscreen.go @@ -127,7 +127,6 @@ type tScreen struct { clear bool cursorx int cursory int - tiosp *termiosPrivate wasbtn bool acs map[rune]string charset string diff --git a/tscreen_darwin.go b/tscreen_darwin.go deleted file mode 100644 index d05479d3..00000000 --- a/tscreen_darwin.go +++ /dev/null @@ -1,142 +0,0 @@ -// +build darwin - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use file except in compliance with the License. -// You may obtain a copy of the license at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tcell - -// The Darwin system is *almost* a real BSD system, but it suffers from -// a brain damaged TTY driver. This TTY driver does not actually -// wake up in poll() or similar calls, which means that we cannot reliably -// shut down the terminal without resorting to obscene custom C code -// and a dedicated poller thread. -// -// So instead, we do a best effort, and simply try to do the close in the -// background. Probably this will cause a leak of two goroutines and -// maybe also the file descriptor, meaning that applications on Darwin -// can't reinitialize the screen, but that's probably a very rare behavior, -// and accepting that is the best of some very poor alternative options. -// -// Maybe someday Apple will fix there tty driver, but its been broken for -// a long time (probably forever) so holding one's breath is contraindicated. -// -// NOTE: In this fork of tcell, we fix this issue by using the library -// zyedidia/poller to properly interface with the tty such that when we -// close it, it actually closes - -import ( - "os/signal" - "syscall" - "unsafe" - - "github.com/zyedidia/poller" -) - -type termiosPrivate syscall.Termios - -func (t *tScreen) termioInit() error { - var e error - var newtios termiosPrivate - var fd uintptr - var tios uintptr - var ioc uintptr - t.tiosp = &termiosPrivate{} - - if t.in, e = poller.Open("/dev/tty", poller.O_RO); e != nil { - goto failed - } - if t.out, e = poller.Open("/dev/tty", poller.O_WO); e != nil { - goto failed - } - - tios = uintptr(unsafe.Pointer(t.tiosp)) - ioc = uintptr(syscall.TIOCGETA) - fd = uintptr(t.out.(*poller.FD).Sysfd()) - if _, _, e1 := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0); e1 != 0 { - e = e1 - goto failed - } - - newtios = *t.tiosp - newtios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | - syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | - syscall.ICRNL | syscall.IXON - newtios.Oflag &^= syscall.OPOST - newtios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | - syscall.ISIG | syscall.IEXTEN - newtios.Cflag &^= syscall.CSIZE | syscall.PARENB - newtios.Cflag |= syscall.CS8 - - tios = uintptr(unsafe.Pointer(&newtios)) - - ioc = uintptr(syscall.TIOCSETA) - if _, _, e1 := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0); e1 != 0 { - e = e1 - 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) - } - - return nil - -failed: - if t.in != nil { - t.in.(*poller.FD).Close() - } - if t.out != nil { - t.out.(*poller.FD).Close() - } - return e -} - -func (t *tScreen) termioFini() { - - signal.Stop(t.sigwinch) - - <-t.indoneq - - if t.out != nil { - fd := uintptr(t.out.(*poller.FD).Sysfd()) - ioc := uintptr(syscall.TIOCSETAF) - tios := uintptr(unsafe.Pointer(t.tiosp)) - syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0) - t.out.(*poller.FD).Close() - } - - if t.in != nil { - t.in.(*poller.FD).Close() - } -} - -func (t *tScreen) getWinSize() (int, int, error) { - - fd := uintptr(t.out.(*poller.FD).Sysfd()) - dim := [4]uint16{} - dimp := uintptr(unsafe.Pointer(&dim)) - ioc := uintptr(syscall.TIOCGWINSZ) - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, - fd, ioc, dimp, 0, 0, 0); err != 0 { - return -1, -1, err - } - return int(dim[1]), int(dim[0]), nil -} - -func (t *tScreen) Beep() error { - t.writeString(string(byte(7))) - return nil -} diff --git a/tscreen_unix.go b/tscreen_unix.go index 8d18594a..55e63c33 100644 --- a/tscreen_unix.go +++ b/tscreen_unix.go @@ -1,4 +1,4 @@ -// +build aix dragonfly freebsd linux netbsd openbsd solaris zos +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos // Copyright 2019 The TCell Authors // @@ -26,20 +26,15 @@ import ( "golang.org/x/term" ) -type termiosPrivate struct{} - func (t *tScreen) termioInit() error { var e error var state *term.State - 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 = openTty(); e != nil { goto failed } - state, e = term.MakeRaw(int(t.out.(*os.File).Fd())) + state, e = term.MakeRaw(t.fd()) if e != nil { goto failed } @@ -56,10 +51,10 @@ func (t *tScreen) termioInit() error { failed: if t.in != nil { - t.in.(*os.File).Close() + closeTty(t.in) } if t.out != nil { - t.out.(*os.File).Close() + closeTty(t.out) } return e } @@ -71,18 +66,18 @@ func (t *tScreen) termioFini() { <-t.indoneq if t.out != nil && t.saved != nil { - term.Restore(int(t.out.(*os.File).Fd()), t.saved) - t.out.(*os.File).Close() + term.Restore(t.fd(), t.saved) + closeTty(t.out) } if t.in != nil { - t.in.(*os.File).Close() + closeTty(t.in) } } func (t *tScreen) getWinSize() (int, int, error) { - wsz, err := unix.IoctlGetWinsize(int(t.out.(*os.File).Fd()), unix.TIOCGWINSZ) + wsz, err := unix.IoctlGetWinsize(t.fd(), unix.TIOCGWINSZ) if err != nil { return -1, -1, err } diff --git a/tscreen_unix_darwin.go b/tscreen_unix_darwin.go new file mode 100644 index 00000000..72dbf829 --- /dev/null +++ b/tscreen_unix_darwin.go @@ -0,0 +1,46 @@ +// +build darwin + +package tcell + +// The Darwin system is *almost* a real BSD system, but it suffers from +// a brain damaged TTY driver. This TTY driver does not actually +// wake up in poll() or similar calls, which means that we cannot reliably +// shut down the terminal without resorting to obscene custom C code +// and a dedicated poller thread. +// +// So instead, we do a best effort, and simply try to do the close in the +// background. Probably this will cause a leak of two goroutines and +// maybe also the file descriptor, meaning that applications on Darwin +// can't reinitialize the screen, but that's probably a very rare behavior, +// and accepting that is the best of some very poor alternative options. +// +// Maybe someday Apple will fix there tty driver, but its been broken for +// a long time (probably forever) so holding one's breath is contraindicated. +// +// NOTE: In this fork of tcell, we fix this issue by using the library +// zyedidia/poller to properly interface with the tty such that when we +// close it, it actually closes + +import ( + "io" + + "github.com/zyedidia/poller" +) + +func openTty() (io.Reader, io.Writer, error) { + var out io.Writer + in, err := poller.Open("/dev/tty", poller.O_RO) + if err == nil { + out, err = poller.Open("/dev/tty", poller.O_WO) + } + + return in, out, err +} + +func closeTty(f interface{}) { + f.(*poller.FD).Close() +} + +func (t *tScreen) fd() int { + return t.out.(*poller.FD).Sysfd() +} diff --git a/tscreen_unix_other.go b/tscreen_unix_other.go new file mode 100644 index 00000000..aaaa5d19 --- /dev/null +++ b/tscreen_unix_other.go @@ -0,0 +1,26 @@ +// +build aix dragonfly freebsd linux netbsd openbsd solaris zos + +package tcell + +import ( + "io" + "os" +) + +func openTty() (io.Reader, io.Writer, error) { + var out io.Writer + in, err := os.OpenFile("/dev/tty", os.O_RDONLY, 0) + if err == nil { + out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0) + } + + return in, out, err +} + +func closeTty(f interface{}) { + f.(*os.File).Close() +} + +func (t *tScreen) fd() int { + return int(t.out.(*os.File).Fd()) +}