summaryrefslogtreecommitdiffstats
path: root/src/tui
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2017-09-02 03:19:50 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2017-09-02 03:19:50 +0900
commite3e7b3360cd83c20a109ccc550bd0508fc5b1cba (patch)
treec83144b22c76d7ad6871f0eecd76ee63b3a51c13 /src/tui
parent655dfb832891bbb3424774e7f32cf2cef5ee8729 (diff)
Delete ncurses implementation
Diffstat (limited to 'src/tui')
-rw-r--r--src/tui/ncurses.go505
1 files changed, 0 insertions, 505 deletions
diff --git a/src/tui/ncurses.go b/src/tui/ncurses.go
deleted file mode 100644
index 3263c397..00000000
--- a/src/tui/ncurses.go
+++ /dev/null
@@ -1,505 +0,0 @@
-// +build ncurses
-// +build !windows
-// +build !tcell
-
-package tui
-
-/*
-#include <ncurses.h>
-#include <locale.h>
-#cgo !static LDFLAGS: -lncurses
-#cgo static LDFLAGS: -l:libncursesw.a -l:libtinfo.a -l:libgpm.a -ldl
-#cgo android static LDFLAGS: -l:libncurses.a -fPIE -march=armv7-a -mfpu=neon -mhard-float -Wl,--no-warn-mismatch
-
-FILE* c_tty() {
- return fopen("/dev/tty", "r");
-}
-
-SCREEN* c_newterm(FILE* tty) {
- return newterm(NULL, stderr, tty);
-}
-
-int c_getcurx(WINDOW* win) {
- return getcurx(win);
-}
-*/
-import "C"
-
-import (
- "os"
- "strconv"
- "strings"
- "time"
- "unicode/utf8"
-)
-
-func HasFullscreenRenderer() bool {
- return true
-}
-
-type Attr C.uint
-
-type CursesWindow struct {
- impl *C.WINDOW
- top int
- left int
- width int
- height int
-}
-
-func (w *CursesWindow) Top() int {
- return w.top
-}
-
-func (w *CursesWindow) Left() int {
- return w.left
-}
-
-func (w *CursesWindow) Width() int {
- return w.width
-}
-
-func (w *CursesWindow) Height() int {
- return w.height
-}
-
-func (w *CursesWindow) Refresh() {
- C.wnoutrefresh(w.impl)
-}
-
-func (w *CursesWindow) FinishFill() {
- // NO-OP
-}
-
-const (
- Bold Attr = C.A_BOLD
- Dim = C.A_DIM
- Blink = C.A_BLINK
- Reverse = C.A_REVERSE
- Underline = C.A_UNDERLINE
-)
-
-var Italic Attr = C.A_VERTICAL << 1 // FIXME
-
-const (
- AttrRegular Attr = 0
-)
-
-var (
- _screen *C.SCREEN
- _colorMap map[int]int16
- _colorFn func(ColorPair, Attr) (C.short, C.int)
-)
-
-func init() {
- _colorMap = make(map[int]int16)
- if strings.HasPrefix(C.GoString(C.curses_version()), "ncurses 5") {
- Italic = C.A_NORMAL
- }
-}
-
-func (a Attr) Merge(b Attr) Attr {
- return a | b
-}
-
-func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
- if C.tigetnum(C.CString("colors")) >= 256 {
- return Dark256
- }
- return Default16
-}
-
-func (r *FullscreenRenderer) Init() {
- C.setlocale(C.LC_ALL, C.CString(""))
- tty := C.c_tty()
- if tty == nil {
- errorExit("Failed to open /dev/tty")
- }
- _screen = C.c_newterm(tty)
- if _screen == nil {
- errorExit("Invalid $TERM: " + os.Getenv("TERM"))
- }
- C.set_term(_screen)
- if r.mouse {
- C.mousemask(C.ALL_MOUSE_EVENTS, nil)
- C.mouseinterval(0)
- }
- C.noecho()
- C.raw() // stty dsusp undef
- C.nonl()
- C.keypad(C.stdscr, true)
-
- delay := 50
- delayEnv := os.Getenv("ESCDELAY")
- if len(delayEnv) > 0 {
- num, err := strconv.Atoi(delayEnv)
- if err == nil && num >= 0 {
- delay = num
- }
- }
- C.set_escdelay(C.int(delay))
-
- if r.theme != nil {
- C.start_color()
- initTheme(r.theme, r.defaultTheme(), r.forceBlack)
- initPairs(r.theme)
- C.bkgd(C.chtype(C.COLOR_PAIR(C.int(ColNormal.index()))))
- _colorFn = attrColored
- } else {
- initTheme(r.theme, nil, r.forceBlack)
- _colorFn = attrMono
- }
-
- C.nodelay(C.stdscr, true)
- ch := C.getch()
- if ch != C.ERR {
- C.ungetch(ch)
- }
- C.nodelay(C.stdscr, false)
-}
-
-func initPairs(theme *ColorTheme) {
- C.assume_default_colors(C.int(theme.Fg), C.int(theme.Bg))
- for _, pair := range []ColorPair{
- ColNormal,
- ColPrompt,
- ColMatch,
- ColCurrent,
- ColCurrentMatch,
- ColSpinner,
- ColInfo,
- ColCursor,
- ColSelected,
- ColHeader,
- ColBorder} {
- C.init_pair(C.short(pair.index()), C.short(pair.Fg()), C.short(pair.Bg()))
- }
-}
-
-func (r *FullscreenRenderer) Pause(bool) {
- C.endwin()
-}
-
-func (r *FullscreenRenderer) Resume(bool) {
-}
-
-func (r *FullscreenRenderer) Close() {
- C.endwin()
- C.delscreen(_screen)
-}
-
-func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
- win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
- if r.theme != nil {
- C.wbkgd(win, C.chtype(C.COLOR_PAIR(C.int(ColNormal.index()))))
- }
- // FIXME Does not implement BorderHorizontal
- if borderStyle != BorderNone {
- pair, attr := _colorFn(ColBorder, 0)
- C.wcolor_set(win, pair, nil)
- C.wattron(win, attr)
- C.box(win, 0, 0)
- C.wattroff(win, attr)
- C.wcolor_set(win, 0, nil)
- }
-
- return &CursesWindow{
- impl: win,
- top: top,
- left: left,
- width: width,
- height: height,
- }
-}
-
-func attrColored(color ColorPair, a Attr) (C.short, C.int) {
- return C.short(color.index()), C.int(a)
-}
-
-func attrMono(color ColorPair, a Attr) (C.short, C.int) {
- return 0, C.int(attrFor(color, a))
-}
-
-func (r *FullscreenRenderer) MaxX() int {
- return int(C.COLS)
-}
-
-func (r *FullscreenRenderer) MaxY() int {
- return int(C.LINES)
-}
-
-func (w *CursesWindow) Close() {
- C.delwin(w.impl)
-}
-
-func (w *CursesWindow) Enclose(y int, x int) bool {
- return bool(C.wenclose(w.impl, C.int(y), C.int(x)))
-}
-
-func (w *CursesWindow) Move(y int, x int) {
- C.wmove(w.impl, C.int(y), C.int(x))
-}
-
-func (w *CursesWindow) MoveAndClear(y int, x int) {
- w.Move(y, x)
- C.wclrtoeol(w.impl)
-}
-
-func (w *CursesWindow) Print(text string) {
- C.waddstr(w.impl, C.CString(strings.Map(func(r rune) rune {
- if r < 32 {
- return -1
- }
- return r
- }, text)))
-}
-
-func (w *CursesWindow) CPrint(color ColorPair, attr Attr, text string) {
- p, a := _colorFn(color, attr)
- C.wcolor_set(w.impl, p, nil)
- C.wattron(w.impl, a)
- w.Print(text)
- C.wattroff(w.impl, a)
- C.wcolor_set(w.impl, 0, nil)
-}
-
-func (r *FullscreenRenderer) Clear() {
- C.clear()
- C.endwin()
-}
-
-func (r *FullscreenRenderer) Refresh() {
- C.refresh()
-}
-
-func (w *CursesWindow) Erase() {
- C.werase(w.impl)
-}
-
-func (w *CursesWindow) X() int {
- return int(C.c_getcurx(w.impl))
-}
-
-func (r *FullscreenRenderer) DoesAutoWrap() bool {
- return true
-}
-
-func (r *FullscreenRenderer) IsOptimized() bool {
- return true
-}
-
-func (w *CursesWindow) Fill(str string) FillReturn {
- if C.waddstr(w.impl, C.CString(str)) == C.OK {
- return FillContinue
- }
- return FillSuspend
-}
-
-func (w *CursesWindow) CFill(fg Color, bg Color, attr Attr, str string) FillReturn {
- index := ColorPair{fg, bg, -1}.index()
- C.wcolor_set(w.impl, C.short(index), nil)
- C.wattron(w.impl, C.int(attr))
- ret := w.Fill(str)
- C.wattroff(w.impl, C.int(attr))
- C.wcolor_set(w.impl, 0, nil)
- return ret
-}
-
-func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
- for _, w := range windows {
- w.Refresh()
- }
- C.doupdate()
-}
-
-func (p ColorPair) index() int16 {
- if p.id >= 0 {
- return p.id
- }
-
- // ncurses does not support 24-bit colors
- if p.is24() {
- return ColDefault.index()
- }
-
- key := p.key()
- if found, prs := _colorMap[key]; prs {
- return found
- }
-
- id := int16(len(_colorMap)) + ColUser.id
- C.init_pair(C.short(id), C.short(p.Fg()), C.short(p.Bg()))
- _colorMap[key] = id
- return id
-}
-
-func consume(expects ...rune) bool {
- for _, r := range expects {
- if int(C.getch()) != int(r) {
- return false
- }
- }
- return true
-}
-
-func escSequence() Event {
- C.nodelay(C.stdscr, true)
- defer func() {
- C.nodelay(C.stdscr, false)
- }()
- c := C.getch()
- switch c {
- case C.ERR:
- return Event{ESC, 0, nil}
- case CtrlM:
- return Event{CtrlAltM, 0, nil}
- case '/':
- return Event{AltSlash, 0, nil}
- case ' ':
- return Event{AltSpace, 0, nil}
- case 127, C.KEY_BACKSPACE:
- return Event{AltBS, 0, nil}
- case '[':
- // Bracketed paste mode (printf "\e[?2004h")
- // \e[200~ TEXT \e[201~
- if consume('2', '0', '0', '~') {
- return Event{Invalid, 0, nil}
- }
- }
- if c >= 'a' && c <= 'z' {
- return Event{AltA + int(c) - 'a', 0, nil}
- }
-
- if c >= '0' && c <= '9' {
- return Event{Alt0 + int(c) - '0', 0, nil}
- }
-
- // Don't care. Ignore the rest.
- for ; c != C.ERR; c = C.getch() {
- }
- return Event{Invalid, 0, nil}
-}
-
-func (r *FullscreenRenderer) GetChar() Event {
- c := C.getch()
- switch c {
- case C.ERR:
- // Unexpected error from blocking read
- r.Close()
- errorExit("Failed to read /dev/tty")
- case C.KEY_UP:
- return Event{Up, 0, nil}
- case C.KEY_DOWN:
- return Event{Down, 0, nil}
- case C.KEY_LEFT:
- return Event{Left, 0, nil}
- case C.KEY_RIGHT:
- return Event{Right, 0, nil}
- case C.KEY_HOME:
- return Event{Home, 0, nil}
- case C.KEY_END:
- return Event{End, 0, nil}
- case C.KEY_BACKSPACE:
- return Event{BSpace, 0, nil}
- case C.KEY_F0 + 1:
- return Event{F1, 0, nil}
- case C.KEY_F0 + 2:
- return Event{F2, 0, nil}
- case C.KEY_F0 + 3:
- return Event{F3, 0, nil}
- case C.KEY_F0 + 4:
- return Event{F4, 0, nil}
- case C.KEY_F0 + 5:
- return Event{F5, 0, nil}
- case C.KEY_F0 + 6:
- return Event{F6, 0, nil}
- case C.KEY_F0 + 7:
- return Event{F7, 0, nil}
- case C.KEY_F0 + 8:
- return Event{F8, 0, nil}
- case C.KEY_F0 + 9:
- return Event{F9, 0, nil}
- case C.KEY_F0 + 10:
- return Event{F10, 0, nil}
- case C.KEY_F0 + 11:
- return Event{F11, 0, nil}
- case C.KEY_F0 + 12:
- return Event{F12, 0, nil}
- case C.KEY_DC:
- return Event{Del, 0, nil}
- case C.KEY_PPAGE:
- return Event{PgUp, 0, nil}
- case C.KEY_NPAGE:
- return Event{PgDn, 0, nil}
- case C.KEY_BTAB:
- return Event{BTab, 0, nil}
- case C.KEY_ENTER:
- return Event{CtrlM, 0, nil}
- case C.KEY_SLEFT:
- return Event{SLeft, 0, nil}
- case C.KEY_SRIGHT:
- return Event{SRight, 0, nil}
- case C.KEY_MOUSE:
- var me C.MEVENT
- if C.getmouse(&me) != C.ERR {
- mod := ((me.bstate & C.BUTTON_SHIFT) | (me.bstate & C.BUTTON_CTRL) | (me.bstate & C.BUTTON_ALT)) > 0
- x := int(me.x)
- y := int(me.y)
- /* Cannot use BUTTON1_DOUBLE_CLICKED due to mouseinterval(0) */
- if (me.bstate & C.BUTTON1_PRESSED) > 0 {
- now := time.Now()
- if now.Sub(r.prevDownTime) < doubleClickDuration {
- r.clickY = append(r.clickY, y)
- } else {
- r.clickY = []int{y}
- r.prevDownTime = now
- }
- return Event{Mouse, 0, &MouseEvent{y, x, 0, true, false, mod}}
- } else if (me.bstate & C.BUTTON1_RELEASED) > 0 {
- double := false
- if len(r.clickY) > 1 && r.clickY[0] == r.clickY[1] &&
- time.Now().Sub(r.prevDownTime) < doubleClickDuration {
- double = true
- }
- return Event{Mouse, 0, &MouseEvent{y, x, 0, false, double, mod}}
- } else if (me.bstate&0x8000000) > 0 || (me.bstate&0x80) > 0 {
- return Event{Mouse, 0, &MouseEvent{y, x, -1, false, false, mod}}
- } else if (me.bstate & C.BUTTON4_PRESSED) > 0 {
- return Event{Mouse, 0, &MouseEvent{y, x, 1, false, false, mod}}
- }
- }
- return Event{Invalid, 0, nil}
- case C.KEY_RESIZE:
- return Event{Resize, 0, nil}
- case ESC:
- return escSequence()
- case 127:
- return Event{BSpace, 0, nil}
- case 0:
- return Event{CtrlSpace, 0, nil}
- }
- // CTRL-A ~ CTRL-Z
- if c >= CtrlA && c <= CtrlZ {
- return Event{int(c), 0, nil}
- }
-
- // Multi-byte character
- buffer := []byte{byte(c)}
- for {
- r, _ := utf8.DecodeRune(buffer)
- if r != utf8.RuneError {
- return Event{Rune, r, nil}
- }
-
- c := C.getch()
- if c == C.ERR {
- break
- }
- if c >= C.KEY_CODE_YES {
- C.ungetch(c)
- break
- }
- buffer = append(buffer, byte(c))
- }
- return Event{Invalid, 0, nil}
-}