summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2016-10-24 09:44:56 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2016-11-07 02:32:14 +0900
commit0c573b3dffe806253e1df2447754a5f3939a11f0 (patch)
tree1fba66a85b6ce48ff3afca18f234a0e0b6ff0822 /src
parent2cff00dce24df2a7e5add001423c6d8200b57c87 (diff)
downloadfzf-0c573b3dffe806253e1df2447754a5f3939a11f0.tar.gz
Prepare for termbox/windows build
`TAGS=termbox make` (or `go build -tags termbox`)
Diffstat (limited to 'src')
-rw-r--r--src/Makefile2
-rw-r--r--src/README.md6
-rw-r--r--src/ansi.go28
-rw-r--r--src/ansi_test.go8
-rw-r--r--src/constants.go1
-rw-r--r--src/constants_unix.go8
-rw-r--r--src/constants_windows.go8
-rw-r--r--src/options.go88
-rw-r--r--src/options_test.go172
-rw-r--r--src/result.go20
-rw-r--r--src/result_test.go24
-rw-r--r--src/terminal.go189
-rw-r--r--src/terminal_unix.go13
-rw-r--r--src/terminal_windows.go11
-rw-r--r--src/tui/ncurses.go (renamed from src/curses/curses.go)543
-rw-r--r--src/tui/termbox.go151
-rw-r--r--src/tui/tui.go250
-rw-r--r--src/tui/tui_test.go (renamed from src/curses/curses_test.go)2
-rw-r--r--src/util/util.go17
-rw-r--r--src/util/util_unix.go17
-rw-r--r--src/util/util_windows.go17
21 files changed, 920 insertions, 655 deletions
diff --git a/src/Makefile b/src/Makefile
index f7aa0b2d..edc77faf 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -43,7 +43,7 @@ $(SRCDIR):
ln -s $(ROOTDIR) $(SRCDIR)
deps: $(SRCDIR) $(SOURCES)
- cd $(SRCDIR) && go get
+ cd $(SRCDIR) && go get -tags "$(TAGS)"
android-build: $(SRCDIR)
cd $(SRCDIR) && GOARCH=arm GOARM=7 CGO_ENABLED=1 go get
diff --git a/src/README.md b/src/README.md
index 272c7554..e8b8debc 100644
--- a/src/README.md
+++ b/src/README.md
@@ -83,9 +83,11 @@ Third-party libraries used
- [ncurses][ncurses]
- [mattn/go-runewidth](https://github.com/mattn/go-runewidth)
- - Licensed under [MIT](http://mattn.mit-license.org/2013)
+ - Licensed under [MIT](http://mattn.mit-license.org)
- [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
- - Licensed under [MIT](http://mattn.mit-license.org/2014)
+ - Licensed under [MIT](http://mattn.mit-license.org)
+- [mattn/go-isatty](https://github.com/mattn/go-isatty)
+ - Licensed under [MIT](http://mattn.mit-license.org)
License
-------
diff --git a/src/ansi.go b/src/ansi.go
index 02be18c6..b7d3a9e3 100644
--- a/src/ansi.go
+++ b/src/ansi.go
@@ -7,7 +7,7 @@ import (
"strings"
"unicode/utf8"
- "github.com/junegunn/fzf/src/curses"
+ "github.com/junegunn/fzf/src/tui"
)
type ansiOffset struct {
@@ -16,9 +16,9 @@ type ansiOffset struct {
}
type ansiState struct {
- fg int
- bg int
- attr curses.Attr
+ fg tui.Color
+ bg tui.Color
+ attr tui.Attr
}
func (s *ansiState) colored() bool {
@@ -134,26 +134,26 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
case 49:
state.bg = -1
case 1:
- state.attr = curses.Bold
+ state.attr = tui.Bold
case 2:
- state.attr = curses.Dim
+ state.attr = tui.Dim
case 4:
- state.attr = curses.Underline
+ state.attr = tui.Underline
case 5:
- state.attr = curses.Blink
+ state.attr = tui.Blink
case 7:
- state.attr = curses.Reverse
+ state.attr = tui.Reverse
case 0:
init()
default:
if num >= 30 && num <= 37 {
- state.fg = num - 30
+ state.fg = tui.Color(num - 30)
} else if num >= 40 && num <= 47 {
- state.bg = num - 40
+ state.bg = tui.Color(num - 40)
} else if num >= 90 && num <= 97 {
- state.fg = num - 90 + 8
+ state.fg = tui.Color(num - 90 + 8)
} else if num >= 100 && num <= 107 {
- state.bg = num - 100 + 8
+ state.bg = tui.Color(num - 100 + 8)
}
}
case 1:
@@ -164,7 +164,7 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
state256 = 0
}
case 2:
- *ptr = num
+ *ptr = tui.Color(num)
state256 = 0
}
}
diff --git a/src/ansi_test.go b/src/ansi_test.go
index 0ba9e400..3afcc6c5 100644
--- a/src/ansi_test.go
+++ b/src/ansi_test.go
@@ -4,14 +4,14 @@ import (
"fmt"
"testing"
- "github.com/junegunn/fzf/src/curses"
+ "github.com/junegunn/fzf/src/tui"
)
func TestExtractColor(t *testing.T) {
- assert := func(offset ansiOffset, b int32, e int32, fg int, bg int, bold bool) {
- var attr curses.Attr
+ assert := func(offset ansiOffset, b int32, e int32, fg tui.Color, bg tui.Color, bold bool) {
+ var attr tui.Attr
if bold {
- attr = curses.Bold
+ attr = tui.Bold
}
if offset.offset[0] != b || offset.offset[1] != e ||
offset.color.fg != fg || offset.color.bg != bg || offset.color.attr != attr {
diff --git a/src/constants.go b/src/constants.go
index bec3bb6c..778d219a 100644
--- a/src/constants.go
+++ b/src/constants.go
@@ -15,7 +15,6 @@ const (
coordinatorDelayStep time.Duration = 10 * time.Millisecond
// Reader
- defaultCommand = `find . -path '*/\.*' -prune -o -type f -print -o -type l -print 2> /dev/null | sed s/^..//`
readerBufferSize = 64 * 1024
// Terminal
diff --git a/src/constants_unix.go b/src/constants_unix.go
new file mode 100644
index 00000000..52677e6c
--- /dev/null
+++ b/src/constants_unix.go
@@ -0,0 +1,8 @@
+// +build !windows
+
+package fzf
+
+const (
+ // Reader
+ defaultCommand = `find . -path '*/\.*' -prune -o -type f -print -o -type l -print 2> /dev/null | sed s/^..//`
+)
diff --git a/src/constants_windows.go b/src/constants_windows.go
new file mode 100644
index 00000000..efd3f11c
--- /dev/null
+++ b/src/constants_windows.go
@@ -0,0 +1,8 @@
+// +build windows
+
+package fzf
+
+const (
+ // Reader
+ defaultCommand = `dir /s/b`
+)
diff --git a/src/options.go b/src/options.go
index c5e6d7b7..a3ed22d9 100644
--- a/src/options.go
+++ b/src/options.go
@@ -9,7 +9,7 @@ import (
"unicode/utf8"
"github.com/junegunn/fzf/src/algo"
- "github.com/junegunn/fzf/src/curses"
+ "github.com/junegunn/fzf/src/tui"
"github.com/junegunn/go-shellwords"
)
@@ -142,7 +142,7 @@ type Options struct {
Multi bool
Ansi bool
Mouse bool
- Theme *curses.ColorTheme
+ Theme *tui.ColorTheme
Black bool
Reverse bool
Cycle bool
@@ -187,7 +187,7 @@ func defaultOptions() *Options {
Multi: false,
Ansi: false,
Mouse: true,
- Theme: curses.EmptyTheme(),
+ Theme: tui.EmptyTheme(),
Black: false,
Reverse: false,
Cycle: false,
@@ -358,60 +358,60 @@ func parseKeyChords(str string, message string) map[int]string {
chord := 0
switch lkey {
case "up":
- chord = curses.Up
+ chord = tui.Up
case "down":
- chord = curses.Down
+ chord = tui.Down
case "left":
- chord = curses.Left
+ chord = tui.Left
case "right":
- chord = curses.Right
+ chord = tui.Right
case "enter", "return":
- chord = curses.CtrlM
+ chord = tui.CtrlM
case "space":
- chord = curses.AltZ + int(' ')
+ chord = tui.AltZ + int(' ')
case "bspace", "bs":
- chord = curses.BSpace
+ chord = tui.BSpace
case "alt-enter", "alt-return":
- chord = curses.AltEnter
+ chord = tui.AltEnter
case "alt-space":
- chord = curses.AltSpace
+ chord = tui.AltSpace
case "alt-/":
- chord = curses.AltSlash
+ chord = tui.AltSlash
case "alt-bs", "alt-bspace":
- chord = curses.AltBS
+ chord = tui.AltBS
case "tab":
- chord = curses.Tab
+ chord = tui.Tab
case "btab", "shift-tab":
- chord = curses.BTab
+ chord = tui.BTab
case "esc":
- chord = curses.ESC
+ chord = tui.ESC
case "del":
- chord = curses.Del
+ chord = tui.Del
case "home":
- chord = curses.Home
+ chord = tui.Home
case "end":
- chord = curses.End
+ chord = tui.End
case "pgup", "page-up":
- chord = curses.PgUp
+ chord = tui.PgUp
case "pgdn", "page-down":
- chord = curses.PgDn
+ chord = tui.PgDn
case "shift-left":
- chord = curses.SLeft
+ chord = tui.SLeft
case "shift-right":
- chord = curses.SRight
+ chord = tui.SRight
case "double-click":
- chord = curses.DoubleClick
+ chord = tui.DoubleClick
case "f10":
- chord = curses.F10
+ chord = tui.F10
default:
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
- chord = curses.CtrlA + int(lkey[5]) - 'a'
+ chord = tui.CtrlA + int(lkey[5]) - 'a'
} else if len(key) == 5 && strings.HasPrefix(lkey, "alt-") && isAlphabet(lkey[4]) {
- chord = curses.AltA + int(lkey[4]) - 'a'
+ chord = tui.AltA + int(lkey[4]) - 'a'
} else if len(key) == 2 && strings.HasPrefix(lkey, "f") && key[1] >= '1' && key[1] <= '9' {
- chord = curses.F1 + int(key[1]) - '1'
+ chord = tui.F1 + int(key[1]) - '1'
} else if utf8.RuneCountInString(key) == 1 {
- chord = curses.AltZ + int([]rune(key)[0])
+ chord = tui.AltZ + int([]rune(key)[0])
} else {
errorExit("unsupported key: " + key)
}
@@ -458,7 +458,7 @@ func parseTiebreak(str string) []criterion {
return criteria
}
-func dupeTheme(theme *curses.ColorTheme) *curses.ColorTheme {
+func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
if theme != nil {
dupe := *theme
return &dupe
@@ -466,16 +466,16 @@ func dupeTheme(theme *curses.ColorTheme) *curses.ColorTheme {
return nil
}
-func parseTheme(defaultTheme *curses.ColorTheme, str string) *curses.ColorTheme {
+func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
theme := dupeTheme(defaultTheme)
for _, str := range strings.Split(strings.ToLower(str), ",") {
switch str {
case "dark":
- theme = dupeTheme(curses.Dark256)
+ theme = dupeTheme(tui.Dark256)
case "light":
- theme = dupeTheme(curses.Light256)
+ theme = dupeTheme(tui.Light256)
case "16":
- theme = dupeTheme(curses.Default16)
+ theme = dupeTheme(tui.Default16)
case "bw", "no":
theme = nil
default:
@@ -495,7 +495,7 @@ func parseTheme(defaultTheme *curses.ColorTheme, str string) *curses.ColorTheme
if err != nil || ansi32 < -1 || ansi32 > 255 {
fail()
}
- ansi := int16(ansi32)
+ ansi := tui.Color(ansi32)
switch pair[0] {
case "fg":
theme.Fg = ansi
@@ -572,9 +572,9 @@ func parseKeymap(keymap map[int]actionType, execmap map[int]string, str string)
}
var key int
if len(pair[0]) == 1 && pair[0][0] == escapedColon {
- key = ':' + curses.AltZ
+ key = ':' + tui.AltZ
} else if len(pair[0]) == 1 && pair[0][0] == escapedComma {
- key = ',' + curses.AltZ
+ key = ',' + tui.AltZ
} else {
keys := parseKeyChords(pair[0], "key name required")
key = firstKey(keys)
@@ -868,7 +868,7 @@ func parseOptions(opts *Options, allArgs []string) {
case "--color":
spec := optionalNextString(allArgs, &i)
if len(spec) == 0 {
- opts.Theme = curses.EmptyTheme()
+ opts.Theme = tui.EmptyTheme()
} else {
opts.Theme = parseTheme(opts.Theme, spec)
}
@@ -905,7 +905,7 @@ func parseOptions(opts *Options, allArgs []string) {
case "+c", "--no-color":
opts.Theme = nil
case "+2", "--no-256":
- opts.Theme = curses.Default16
+ opts.Theme = tui.Default16
case "--black":
opts.Black = true
case "--no-black":
@@ -1071,11 +1071,11 @@ func parseOptions(opts *Options, allArgs []string) {
func postProcessOptions(opts *Options) {
// Default actions for CTRL-N / CTRL-P when --history is set
if opts.History != nil {
- if _, prs := opts.Keymap[curses.CtrlP]; !prs {
- opts.Keymap[curses.CtrlP] = actPreviousHistory
+ if _, prs := opts.Keymap[tui.CtrlP]; !prs {
+ opts.Keymap[tui.CtrlP] = actPreviousHistory
}
- if _, prs := opts.Keymap[curses.CtrlN]; !prs {
- opts.Keymap[curses.CtrlN] = actNextHistory
+ if _, prs := opts.Keymap[tui.CtrlN]; !prs {
+ opts.Keymap[tui.CtrlN] = actNextHistory
}
}
diff --git a/src/options_test.go b/src/options_test.go
index 60136173..f16569e3 100644
--- a/src/options_test.go
+++ b/src/options_test.go
@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
- "github.com/junegunn/fzf/src/curses"
+ "github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
)
@@ -133,48 +133,48 @@ func TestParseKeys(t *testing.T) {
if len(pairs) != 11 {
t.Error(11)
}
- check(curses.CtrlZ, "ctrl-z")
- check(curses.AltZ, "alt-z")
- check(curses.F2, "f2")
- check(curses.AltZ+'@', "@")
- check(curses.AltA, "Alt-a")
- check(curses.AltZ+'!', "!")
- check(curses.CtrlA+'g'-'a', "ctrl-G")
- check(curses.AltZ+'J', "J")
- check(curses.AltZ+'g', "g")
- check(curses.AltEnter, "ALT-enter")
- check(curses.AltSpace, "alt-SPACE")
+ check(tui.CtrlZ, "ctrl-z")
+ check(tui.AltZ, "alt-z")
+ check(tui.F2, "f2")
+ check(tui.AltZ+'@', "@")
+ check(tui.AltA, "Alt-a")
+ check(tui.AltZ+'!', "!")
+ check(tui.CtrlA+'g'-'a', "ctrl-G")
+ check(tui.AltZ+'J', "J")
+ check(tui.AltZ+'g', "g")
+ check(tui.AltEnter, "ALT-enter")
+ check(tui.AltSpace, "alt-SPACE")
// Synonyms
pairs = parseKeyChords("enter,Return,space,tab,btab,esc,up,down,left,right", "")
if len(pairs) != 9 {
t.Error(9)
}
- check(curses.CtrlM, "Return")
- check(curses.AltZ+' ', "space")
- check(curses.Tab, "tab")
- check(curses.BTab, "btab")
- check(curses.ESC, "esc")
- check(curses.Up, "up")
- check(curses.Down, "down")
- check(curses.Left, "left")
- check(curses.Right, "right")
+ check(tui.CtrlM, "Return")
+ check(tui.AltZ+' ', "space")
+ check(tui.Tab, "tab")
+ check(tui.BTab, "btab")
+ check(tui.ESC, "esc")
+ check(tui.Up, "up")
+ check(tui.Down, "down")
+ check(tui.Left, "left")
+ check(tui.Right, "right")
pairs = parseKeyChords("Tab,Ctrl-I,PgUp,page-up,pgdn,Page-Down,Home,End,Alt-BS,Alt-BSpace,shift-left,shift-right,btab,shift-tab,return,Enter,bspace", "")
if len(pairs) != 11 {
t.Error(11)
}
- check(curses.Tab, "Ctrl-I")
- check(curses.PgUp, "page-up")
- check(curses.PgDn, "Page-Down")
- check(curses.Home, "Home")
- check(curses.End, "End")
- check(curses.AltBS, "Alt-BSpace")
- check(curses.SLeft, "shift-left")
- check(curses.SRight, "shift-right")
- check(curses.BTab, "shift-tab")
- check(curses.CtrlM, "Enter")
- check(curses.BSpace, "bspace")
+ check(tui.Tab, "Ctrl-I")
+ check(tui.PgUp, "page-up")
+ check(tui.PgDn, "Page-Down")
+ check(tui.Home, "Home")
+ check(tui.End, "End")
+ check(tui.AltBS, "Alt-BSpace")
+ check(tui.SLeft, "shift-left")
+ check(tui.SRight, "shift-right")
+ check(tui.BTab, "shift-tab")
+ check(tui.CtrlM, "Enter")
+ check(tui.BSpace, "bspace")
}
func TestParseKeysWithComma(t *testing.T) {
@@ -191,36 +191,36 @@ func TestParseKeysWithComma(t *testing.T) {
pairs := parseKeyChords(",", "")
checkN(len(pairs), 1)
- check(pairs, curses.AltZ+',', ",")
+ check(pairs, tui.AltZ+',', ",")
pairs = parseKeyChords(",,a,b", "")
checkN(len(pairs), 3)
- check(pairs, curses.AltZ+'a', "a")
- check(pairs, curses.AltZ+'b', "b")
- check(pairs, curses.AltZ+',', ",")
+ check(pairs, tui.AltZ+'a', "a")
+ check(pairs, tui.AltZ+'b', "b")
+ check(pairs, tui.AltZ+',', ",")
pairs = parseKeyChords("a,b,,", "")
checkN(len(pairs), 3)
- check(pairs, curses.AltZ+'a', "a")
- check(pairs, curses.AltZ+'b', "b")
- check(pairs, curses.AltZ+',', ",")
+ check(pairs, tui.AltZ+'a', "a")
+ check(pairs, tui.AltZ+'b', "b")
+ check(pairs, tui.AltZ+',', ",")
pairs = parseKeyChords("a,,,b", "")
checkN(len(pairs), 3)
- check(pairs, curses.AltZ+'a', "a")
- check(pairs, curses.AltZ+'b', "b")
- check(pairs, curses.AltZ+',', ",")
+ check(pairs, tui.AltZ+'a', "a")
+ check(pairs, tui.AltZ+'b', "b")
+ check(pairs, tui.AltZ+',', ",")
pairs = parseKeyChords("a,,,b,c", "")
checkN(len(pairs), 4)
- check(pairs, curses.AltZ+'a', "a")
- check(pairs, curses.AltZ+'b', "b")
- check(pairs, curses.AltZ+'c', "c")
- check(pairs, curses.AltZ+',', ",")
+ check(pairs, tui.AltZ+'a', "a")
+ check(pairs, tui.AltZ+'b', "b")
+ check(pairs, tui.AltZ+'c', "c")
+ check(pairs, tui.AltZ+',', ",")
pairs = parseKeyChords(",,,", "")
checkN(len(pairs), 1)
- check(pairs, curses.AltZ+',', ",")
+ check(pairs, tui.AltZ+',', ",")
}
func TestBind(t *testing.T) {
@@ -236,41 +236,41 @@ func TestBind(t *testing.T) {
}
keymap := defaultKeymap()
execmap := make(map[int]string)
- check(actBeginningOfLine, keymap[curses.CtrlA])
+ check(actBeginningOfLine, keymap[tui.CtrlA])
parseKeymap(keymap, execmap,
"ctrl-a:kill-line,ctrl-b:toggle-sort,c:page-up,alt-z:page-down,"+
"f1:execute(ls {}),f2:execute/echo {}, {}, {}/,f3:execute[echo '({})'],f4:execute;less {};,"+
"alt-a:execute@echo (,),[,],/,:,;,%,{}@,alt-b:execute;echo (,),[,],/,:,@,%,{};"+
",,:abort,::accept,X:execute:\nfoobar,Y:execute(baz)")
- check(actKillLine, keymap[curses.CtrlA])
- check(actToggleSort, keymap[curses.CtrlB])
- check(actPageUp, keymap[curses.AltZ+'c'])
- check(actAbort, keymap[curses.AltZ+','])
- check(actAccept, keymap[curses.AltZ+':'])
- check(actPageDown, keymap[curses.AltZ])
- check(actExecute, keymap[curses.F1])
- check(actExecute, keymap[curses.F2])
- check(actExecute, keymap[curses.F3])
- check(actExecute, keymap[curses.F4])
- checkString("ls {}", execmap[curses.F1])
- checkString("echo {}, {}, {}", execmap[curses.F2])
- checkString("echo '({})'", execmap[curses.F3])
- checkString("less {}", execmap[curses.F4])
- checkString("echo (,),[,],/,:,;,%,{}", execmap[curses.AltA])
- checkString("echo (,),[,],/,:,@,%,{}", execmap[curses.AltB])
- checkString("\nfoobar,Y:execute(baz)", execmap[curses.AltZ+'X'])
+ check(actKillLine, keymap[tui.CtrlA])
+ check(actToggleSort, keymap[tui.CtrlB])
+ check(actPageUp, keymap[tui.AltZ+'c'])
+ check(actAbort, keymap[tui.AltZ+','])
+ check(actAccept, keymap[tui.AltZ+':'])
+ check(actPageDown, keymap[tui.AltZ])
+ check(actExecute, keymap[tui.F1])
+ check(actExecute, keymap[tui.F2])
+ check(actExecute, keymap[tui.F3])
+ check(actExecute, keymap[tui.F4])
+ checkString("ls {}", execmap[tui.F1])
+ checkString("echo {}, {}, {}", execmap[tui.F2])
+ checkString("echo '({})'", execmap[tui.F3])
+ checkString("less {}", execmap[tui.F4])
+ checkString("echo (,),[,],/,:,;,%,{}", execmap[tui.AltA])
+ checkString("echo (,),[,],/,:,@,%,{}", execmap[tui.AltB])
+ checkString("\nfoobar,Y:execute(baz)", execmap[tui.AltZ+'X'])
for idx, char := range []rune{'~', '!', '@', '#', '$', '%', '^', '&', '*', '|', ';', '/'} {
parseKeymap(keymap, execmap, fmt.Sprintf("%d:execute%cfoobar%c", idx%10, char, char))
- checkString("foobar", execmap[curses.AltZ+int([]rune(fmt.Sprintf("%d", idx%10))[0])])
+ checkString("foobar", execmap[tui.AltZ+int([]rune(fmt.Sprintf("%d", idx%10))[0])])
}
parseKeymap(keymap, execmap, "f1:abort")
- check(actAbort, keymap[curses.F1])
+ check(actAbort, keymap[tui.F1])
}
func TestColorSpec(t *testing.T) {
- theme := curses.Dark256
+ theme := tui.Dark256
dark := parseTheme(theme, "dark")
if *dark != *theme {
t.Errorf("colors should be equivalent")
@@ -283,7 +283,7 @@ func TestColorSpec(t *testing.T) {
if *light == *theme {
t.Errorf("should not be equivalent")
}
- if *light != *curses.Light256 {
+ if *light != *tui.Light256 {
t.Errorf("colors should be equivalent")
}
if light == theme {
@@ -294,23 +294,23 @@ func TestColorSpec(t *testing.T) {
if customized.Fg != 231 || customized.Bg != 232 {
t.Errorf("color not customized")
}
- if *curses.Dark256 == *customized {
+ if *tui.Dark256 == *customized {
t.Errorf("colors should not be equivalent")
}
- customized.Fg = curses.Dark256.Fg
- customized.Bg = curses.Dark256.Bg
- if *curses.Dark256 != *customized {
- t.Errorf("colors should now be equivalent: %v, %v", curses.Dark256, customized)
+ customized.Fg = tui.Dark256.Fg
+ customized.Bg = tui.Dark256.Bg
+ if *tui.Dark256 != *customized {
+ t.Errorf("colors should now be equivalent: %v, %v", tui.Dark256, customized)
}
customized = parseTheme(theme, "fg:231,dark,bg:232")
- if customized.Fg != curses.Dark256.Fg || customized.Bg == curses.Dark256.Bg {
+ if customized.Fg != tui.Dark256.Fg || customized.Bg == tui.Dark256.Bg {
t.Errorf("color not customized")
}
}
func TestParseNilTheme(t *testing.T) {
- var theme *curses.ColorTheme
+ var theme *tui.ColorTheme
newTheme := parseTheme(theme, "prompt:12")
if newTheme != nil {
t.Errorf("color is disabled. keep it that way.")
@@ -330,21 +330,21 @@ func TestDefaultCtrlNP(t *testing.T) {
t.Error()
}
}
- check([]string{}, curses.CtrlN, actDown)
- check([]string{}, curses.CtrlP, actUp)
+ check([]string{}, tui.CtrlN, actDown)
+ check([]string{}, tui.CtrlP, actUp)
- check([]string{"--bind=ctrl-n:accept"}, curses.CtrlN, actAccept)
- check([]string{"--bind=ctrl-p:accept"}, curses.CtrlP, actAccept)
+ check([]string{"--bind=ctrl-n:accept"}, tui.CtrlN, actAccept)
+ check([]string{"--bind=ctrl-p:accept"}, tui.CtrlP, actAccept)
hist := "--history=/tmp/fzf-history"
- check([]string{hist}, curses.CtrlN, actNextHistory)
- check([]string{hist}, curses.CtrlP, actPreviousHistory)
+ check([]string{hist}, tui.CtrlN, actNextHistory)
+ check([]string{hist}, tui.CtrlP, actPreviousHistory)
- check([]string{hist, "--bind=ctrl-n:accept"}, curses.CtrlN, actAccept)
- check([]string{hist, "--bind=ctrl-n:accept"}, curses.CtrlP, actPreviousHistory)
+ check([]string{hist, "--bind=ctrl-n:accept"}, tui.CtrlN, actAccept)
+ check([]string{hist, "--bind=ctrl-n:accept"}, tui.CtrlP, actPreviousHistory)
- check([]string{hist, "--bind=ctrl-p:accept"}, curses.CtrlN, actNextHistory)
- check([]string{hist, "--bind=ctrl-p:accept"}, curses.CtrlP, actAccept)
+ check([]string{hist, "--bind=ctrl-p:accept"}, tui.CtrlN, actNextHistory)
+ check([]string{hist, "--bind=ctrl-p:accept"}, tui.CtrlP, actAccept)
}
func optsFor(words ...string) *Options {
diff --git a/src/result.go b/src/result.go
index 69e83f85..c0cf5d61 100644
--- a/src/result.go
+++ b/src/result.go
@@ -5,7 +5,7 @@ import (
"sort"
"unicode"
- "github.com/junegunn/fzf/src/curses"
+ "github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
)
@@ -14,8 +14,8 @@ type Offset [2]int32
type colorOffset struct {
offset [2]int32
- color int
- attr curses.Attr
+ color tui.ColorPair
+ attr tui.Attr
index int32
}
@@ -92,7 +92,7 @@ func minRank() rank {
return rank{index: 0, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
}
-func (result *Result) colorOffsets(matchOffsets []Offset, theme *curses.ColorTheme, color int, attr curses.Attr, current bool) []colorOffset {
+func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, color tui.ColorPair, attr tui.Attr, current bool) []colorOffset {
itemColors := result.item.Colors()
// No ANSI code, or --color=no
@@ -149,23 +149,23 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *curses.ColorThe
fg := ansi.color.fg
if fg == -1 {
if current {
- fg = int(theme.Current)
+ fg = theme.Current
} else {
- fg = int(theme.Fg)
+ fg = theme.Fg
}
}
bg := ansi.color.bg
if bg == -1 {
if current {
- bg = int(theme.DarkBg)
+ bg = theme.DarkBg
} else {
- bg = int(theme.Bg)
+ bg = theme.Bg
}
}
colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)},
- color: curses.PairFor(fg, bg),
- attr: ansi.color.attr | attr})
+ color: tui.PairFor(fg, bg),
+ attr: ansi.color.attr.Merge(attr)})
}
}
}
diff --git a/src/result_test.go b/src/result_test.go
index 645684dd..06402c1b 100644
--- a/src/result_test.go
+++ b/src/result_test.go
@@ -1,3 +1,5 @@
+// +build !termbox
+
package fzf
import (
@@ -5,7 +7,7 @@ import (
"sort"
"testing"
- "github.com/junegunn/fzf/src/curses"
+ "github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
)
@@ -98,26 +100,26 @@ func TestColorOffset(t *testing.T) {
item: &Item{
colors: &[]ansiOffset{
ansiOffset{[2]int32{0, 20}, ansiState{1, 5, 0}},
- ansiOffset{[2]int32{22, 27}, ansiState{2, 6, curses.Bold}},
+ ansiOffset{[2]int32{22, 27}, ansiState{2, 6, tui.Bold}},
ansiOffset{[2]int32{30, 32}, ansiState{3, 7, 0}},
- ansiOffset{[2]int32{33, 40}, ansiState{4, 8, curses.Bold}}}}}
+ ansiOffset{[2]int32{33, 40}, ansiState{4, 8, tui.Bold}}}}}
// [{[0 5] 9 false} {[5 15] 99 false} {[15 20] 9 false} {[22 25] 10 true} {[25 35] 99 false} {[35 40] 11 true}]
- colors := item.colorOffsets(offsets, curses.Dark256, 99, 0, true)
- assert := func(idx int, b int32, e int32, c int, bold bool) {
- var attr curses.Attr
+ colors := item.colorOffsets(offsets, tui.Dark256, 99, 0, true)
+ assert := func(idx int, b int32, e int32, c tui.ColorPair, bold bool) {
+ var attr tui.Attr
if bold {
- attr = curses.Bold
+ attr = tui.Bold
}
o := colors[idx]
if o.offset[0] != b || o.offset[1] != e || o.color != c || o.attr != attr {
t.Error(o)
}
}
- assert(0, 0, 5, curses.ColUser, false)
+ assert(0, 0, 5, tui.ColUser, false)
assert(1, 5, 15, 99, false)
- assert(2, 15, 20, curses.ColUser, false)
- assert(3, 22, 25, curses.ColUser+1, true)
+ assert(2, 15, 20, tui.ColUser, false)
+ assert(3, 22, 25, tui.ColUser+1, true)
assert(4, 25, 35, 99, false)
- assert(5, 35, 40, curses.ColUser+2, true)
+ assert(5, 35, 40, tui.ColUser+2, true)
}
diff --git a/src/terminal.go b/src/terminal.go
index 376c6408..39c78239 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -12,7 +12,7 @@ import (
"syscall"
"time"
- C "github.com/junegunn/fzf/src/curses"
+ "github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
"github.com/junegunn/go-runewidth"
@@ -69,9 +69,9 @@ type Terminal struct {
header0 []string
ansi bool
margin [4]sizeSpec
- window *C.Window
- bwindow *C.Window
- pwindow *C.Window
+ window *tui.Window
+ bwindow *tui.Window
+ pwindow *tui.Window
count int
progress int
reading bool
@@ -90,7 +90,7 @@ type Terminal struct {
suppress bool
startChan chan bool
slab *util.Slab
- theme *C.ColorTheme
+ theme *tui.ColorTheme
}
type selectedItem struct {
@@ -187,51 +187,51 @@ const (
func defaultKeymap() map[int]actionType {
keymap := make(map[int]actionType)
- keymap[C.Invalid] = actInvalid
- keymap[C.CtrlA] = actBeginningOfLine
- keymap[C.CtrlB] = actBackwardChar
- keymap[C.CtrlC] = actAbort
- keymap[C.CtrlG] = actAbort
- keymap[C.CtrlQ] = actAbort
- keymap[C.ESC] = actAbort
- keymap[C.CtrlD] = actDeleteCharEOF
- keymap[C.CtrlE] = actEndOfLine
- keymap[C.CtrlF] = actForwardChar
- keymap[C.CtrlH] = actBackwardDeleteChar
- keymap[C.BSpace] = actBackwardDeleteChar
- keymap[C.Tab] = actToggleDown
- keymap[C.BTab] = actToggleUp
- keymap[C.CtrlJ] = actDown
- keymap[C.CtrlK] = actUp
- keymap[C.CtrlL] = actClearScreen
- keymap[C.CtrlM] = actAccept
- keymap[C.CtrlN] = actDown
- keymap[C.CtrlP] = actUp
- keymap[C.CtrlU] = actUnixLineDiscard
- keymap[C.CtrlW] = actUnixWordRubout
- keymap[C.CtrlY] = actYank
-
- keymap[C.AltB] = actBackwardWord
- keymap[C.SLeft] = actBackwardWord
- keymap[C.AltF] = actForwardWord
- keymap[C.SRight] = actForwardWord
- keymap[C.AltD] = actKillWord
- keymap[C.AltBS] = actBackwardKillWord
-
- keymap[C.Up] = actUp
- keymap[C.Down] = actDown
- keymap[C.Left] = actBackwardChar
- keymap[C.Right] = actForwardChar
-
- keymap[C.Home] = actBeginningOfLine
- keymap[C.End] = actEndOfLine
- keymap[C.Del] = actDeleteChar
- keymap[C.PgUp] = actPageUp
- keymap[C.PgDn] = actPageDown
-
- keymap[C.Rune] = actRune
- keymap[C.Mouse] = actMouse
- keymap[C.DoubleClick] = actAccept
+ keymap[tui.Invalid] = actInvalid
+ keymap[tui.CtrlA] = actBeginningOfLine
+ keymap[tui.CtrlB] = actBackwardChar
+ keymap[tui.CtrlC] = actAbort
+ keymap[tui.CtrlG] = actAbort
+ keymap[tui.CtrlQ] = actAbort
+ keymap[tui.ESC] = actAbort
+ keymap[tui.CtrlD] = actDeleteCharEOF
+ keymap[tui.CtrlE] = actEndOfLine
+ keymap[tui.CtrlF] = actForwardChar
+ keymap[tui.CtrlH] = actBackwardDeleteChar
+ keymap[tui.BSpace] = actBackwardDeleteChar
+ keymap[tui.Tab] = actToggleDown
+ keymap[tui.BTab] = actToggleUp
+ keymap[tui.CtrlJ] = actDown
+ keymap[tui.CtrlK] = actUp
+ keymap[tui.CtrlL] = actClearScreen
+ keymap[tui.CtrlM] = actAccept
+ keymap[tui.CtrlN] = actDown
+ keymap[tui.CtrlP] = actUp
+ keymap[tui.CtrlU] = actUnixLineDiscard
+ keymap[tui.CtrlW] = actUnixWordRubout
+ keymap[tui.CtrlY] = actYank
+
+ keymap[tui.AltB] = actBackwardWord
+ keymap[tui.SLeft] = actBackwardWord
+ keymap[tui.AltF] = actForwardWord
+ keymap[tui.SRight] = actForwardWord
+ keymap[tui.AltD] = actKillWord
+ keymap[tui.AltBS] = actBackwardKillWord
+
+ keymap[tui.Up] = actUp
+ keymap[tui.Down] = actDown
+ keymap[tui.Left] = actBackwardChar
+ keymap[tui.Right] = actForwardChar
+
+ keymap[tui.Home] = actBeginningOfLine
+ keymap[tui.End] = actEndOfLine
+ keymap[tui.Del] = actDeleteChar
+ keymap[tui.PgUp] = actPageUp
+ keymap[tui.PgDn] = actPageDown
+
+ keymap[tui.Rune] = actRune
+ keymap[tui.Mouse] = actMouse
+ keymap[tui.DoubleClick] = actAccept
return keymap
}
@@ -299,7 +299,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
theme: opts.Theme,
startChan: make(chan bool, 1),
initFunc: func() {
- C.Init(opts.Theme, opts.Black, opts.Mouse)
+ tui.Init(opts.Theme, opts.Black, opts.Mouse)
}}
}
@@ -429,8 +429,8 @@ func calculateSize(base int, size sizeSpec, margin int, minSize int) int {
}
func (t *Terminal) resizeWindows() {
- screenWidth := C.MaxX()
- screenHeight := C.MaxY()
+ screenWidth := tui.MaxX()
+ screenHeight := tui.MaxY()
marginInt := [4]int{}
for idx, sizeSpec := range t.margin {
if sizeSpec.percent {
@@ -479,33 +479,33 @@ func (t *Terminal) resizeWindows() {
height := screenHeight - marginInt[0] - marginInt[2]
if t.isPreviewEnabled() {
createPreviewWindow := func(y int, x int, w int, h int) {
- t.bwindow = C.NewWindow(y, x, w, h, true)
- t.pwindow = C.NewWindow(y+1, x+2, w-4, h-2, false)
+ t.bwindow = tui.NewWindow(y, x, w, h, true)
+ t.pwindow = tui.NewWindow(y+1, x+2, w-4, h-2, false)
}
switch t.preview.position {
case posUp:
pheight := calculateSize(height, t.preview.size, minHeight, 3)
- t.window = C.NewWindow(
+ t.window = tui.NewWindow(
marginInt[0]+pheight, marginInt[3], width, height-pheight, false)
createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
case posDown:
pheight := calculateSize(height, t.preview.size, minHeight, 3)
- t.window = C.NewWindow(
+ t.window = tui.NewWindow(
marginInt[0], marginInt[3], width, height-pheight, false)
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
case posLeft:
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
- t.window = C.NewWindow(
+ t.window = tui.NewWindow(
marginInt[0], marginInt[3]+pwidth, width-pwidth, height, false)
createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
case posRight:
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
- t.window = C.NewWindow(
+ t.window = tui.NewWindow(
marginInt[0], marginInt[3], width-pwidth, height, false)
createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height)
}
} else {
- t.window = C.NewWindow(
+ t.window = tui.NewWindow(
marginInt[0],
marginInt[3],
width,
@@ -531,24 +531,24 @@ func (t *Terminal) placeCursor() {
func (t *Terminal) printPrompt() {
t.move(0, 0, true)
- t.window.CPrint(C.ColPrompt, C.Bold, t.prompt)
- t.window.CPrint(C.ColNormal, C.Bold, string(t.input))
+ t.window.CPrint(tui.ColPrompt, tui.Bold, t.prompt)
+ t.window.CPrint(tui.ColNormal, tui.Bold, string(t.input))
}
func (t *Terminal) printInfo() {
if t.inlineInfo {
t.move(0, displayWidth([]rune(t.prompt))+displayWidth(t.input)+1, true)
if t.reading {
- t.window.CPrint(C.ColSpinner, C.Bold, " < ")
+ t.window.CPrint(tui.ColSpinner, tui.Bold, " < ")
} else {
- t.window.CPrint(C.ColPrompt, C.Bold, " < ")
+ t.window.CPrint(tui.ColPrompt, tui.Bold, " < ")
}
} else {
t.move(1, 0, true)
if t.reading {
duration := int64(spinnerDuration)
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
- t.window.CPrint(C.ColSpinner, C.Bold, _spinner[idx])
+ t.window.CPrint(tui.ColSpinner, tui.Bold, _spinner[idx])
}
t.move(1, 2, false)
}
@@ -567,7 +567,7 @@ func (t *Terminal) printInfo() {
if t.progress > 0 && t.progress < 100 {
output += fmt.Sprintf(" (%d%%)", t.progress)
}
- t.window.CPrint(C.ColInfo, 0, output)
+ t.window.CPrint(tui.ColInfo, 0, output)
}
func (t *Terminal) printHeader() {
@@ -591,7 +591,8 @@ func (t *Terminal) printHeader() {
colors: colors}
t.move(line, 2, true)
- t.printHighlighted(&Result{item: item}, 0, C.ColHeader, 0, false)
+ t.printHighlighted(&Result{item: item},
+ tui.AttrRegular, tui.ColHeader, tui.ColDefault, false)
}
}
@@ -625,21 +626,21 @@ func (t *Terminal) printItem(result *Result, i int, current bool) {
} else if current {
label = ">"
}
- t.window.CPrint(C.ColCursor, C.Bold, label)
+ t.window.CPrint(tui.ColCursor, tui.Bold, label)
if current {
if selected {
- t.window.CPrint(C.ColSelected, C.Bold, ">")
+ t.window.CPrint(tui.ColSelected, tui.Bold, ">")
} else {
- t.window.CPrint(C.ColCurrent, C.Bold, " ")
+ t.window.CPrint(tui.ColCurrent, tui.Bold, " ")
}
- t.printHighlighted(result, C.Bold, C.ColCurrent, C.ColCurrentMatch, true)
+ t.printHighlighted(result, tui.Bold, tui.ColCurrent, tui.ColCurrentMatch, true)
} else {
if selected {
- t.window.CPrint(C.ColSelected, C.Bold, ">")
+ t.window.CPrint(tui.ColSelected, tui.Bold, ">")
} else {
t.window.Print(" ")
}
- t.printHighlighted(result, 0, C.ColNormal, C.ColMatch, false)
+ t.printHighlighted(result, 0, tui.ColNormal, tui.ColMatch, false)
}
}
@@ -695,7 +696,7 @@ func overflow(runes []rune, max int) bool {
return false
}
-func (t *Terminal) printHighlighted(result *Result, attr C.Attr, col1 int, col2 int, current bool) {
+func (t *Terminal) printHighlighted(result *Result, attr tui.Attr, col1 tui.ColorPair, col2 tui.ColorPair, current bool) {
item := result.item
// Overflow
@@ -827,7 +828,7 @@ func (t *Terminal) printPreview() {
if t.previewer.offset > 0 {
offset := fmt.Sprintf("%d/%d", t.previewer.offset+1, t.previewer.lines)
t.pwindow.Move(0, t.pwindow.Width-len(offset))
- t.pwindow.CPrint(C.ColInfo, C.Reverse, offset)
+ t.pwindow.CPrint(tui.ColInfo, tui.Reverse, offset)
}
}
@@ -858,11 +859,10 @@ func (t *Terminal) printAll() {
func (t *Terminal) refresh() {
if !t.suppress {
if t.isPreviewEnabled() {
- t.bwindow.Refresh()
- t.pwindow.Refresh()
+ tui.RefreshWindows([]*tui.Window{t.bwindow, t.pwindow, t.window})
+ } else {
+ tui.RefreshWindows([]*tui.Window{t.window})
}
- t.window.Refresh()
- C.DoUpdate()
}
}
@@ -912,10 +912,10 @@ func (t *Terminal) rubout(pattern string) {
t.input = append(t.input[:t.cx], after...)
}
-func keyMatch(key int, event C.Event) bool {
+func keyMatch(key int, event tui.Event) bool {
return event.Type == key ||
- event.Type == C.Rune && int(event.Char) == key-C.AltZ ||
- event.Type == C.Mouse && key == C.DoubleClick && event.MouseEvent.Double
+ event.Type == tui.Rune && int(event.Char) == key-tui.AltZ ||
+ event.Type == tui.Mouse && key == tui.DoubleClick && event.MouseEvent.Double
}
func quoteEntry(entry string) string {
@@ -980,7 +980,7 @@ func (t *Terminal) executeCommand(template string, items []*Item) {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
- C.Endwin()
+ tui.Pause()
cmd.Run()
t.refresh()
}
@@ -1014,7 +1014,7 @@ func (t *Terminal) Loop() {
}()
resizeChan := make(chan os.Signal, 1)
- signal.Notify(resizeChan, syscall.SIGWINCH)
+ notifyOnResize(resizeChan) // Non-portable
go func() {
for {
<-resizeChan
@@ -1126,12 +1126,11 @@ func (t *Terminal) Loop() {
case reqRefresh:
t.suppress = false
case reqRedraw:
- C.Clear()
- C.Endwin()
- C.Refresh()
+ tui.Clear()
+ tui.Refresh()
t.printAll()
case reqClose:
- C.Close()
+ tui.Close()
if t.output() {
exit(exitOk)
}
@@ -1144,11 +1143,11 @@ func (t *Terminal) Loop() {
case reqPreviewRefresh:
t.printPreview()
case reqPrintQuery:
- C.Close()
+ tui.Close()
t.printer(string(t.input))
exit(exitOk)
case reqQuit:
- C.Close()
+ tui.Close()
exit(exitInterrupt)
}
}
@@ -1161,7 +1160,7 @@ func (t *Terminal) Loop() {
looping := true
for looping {
- event := C.GetChar()
+ event := tui.GetChar()
t.mutex.Lock()
previousInput := t.input
@@ -1445,7 +1444,7 @@ func (t *Terminal) Loop() {
// Double-click
if my >= min {
if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
- return doAction(t.keymap[C.DoubleClick], C.DoubleClick)
+ return doAction(t.keymap[tui.DoubleClick], tui.DoubleClick)
}
}
} else if me.Down {
@@ -1468,8 +1467,8 @@ func (t *Terminal) Loop() {
mapkey := event.Type
if t.jumping == jumpDisabled {
action := t.keymap[mapkey]
- if mapkey == C.Rune {
- mapkey = int(event.Char) + int(C.AltZ)
+ if mapkey == tui.Rune {
+ mapkey = int(event.Char) + int(tui.AltZ)
if act, prs := t.keymap[mapkey]; prs {
action = act
}
@@ -1484,7 +1483,7 @@ func (t *Terminal) Loop() {
}
changed = string(previousInput) != string(t.input)
} else {
- if mapkey == C.Rune {
+ if mapkey == tui.Rune {
if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 && idx < t.maxItems() && idx < t.merger.Length() {
t.cy = idx + t.offset
if t.jumping == jumpAcceptEnabled {
diff --git a/src/terminal_unix.go b/src/terminal_unix.go
new file mode 100644
index 00000000..6284c22d
--- /dev/null
+++ b/src/terminal_unix.go
@@ -0,0 +1,13 @@
+// +build !windows
+
+package fzf
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+)
+
+func notifyOnResize(resizeChan chan<- os.Signal) {
+ signal.Notify(resizeChan, syscall.SIGWINCH)
+}
diff --git a/src/terminal_windows.go b/src/terminal_windows.go
new file mode 100644
index 00000000..5512bbaf
--- /dev/null
+++ b/src/terminal_windows.go
@@ -0,0 +1,11 @@
+// +build windows
+
+package fzf
+
+import (
+ "os"
+)
+
+func notifyOnResize(resizeChan chan<- os.Signal) {
+ // TODO
+}
diff --git a/src/curses/curses.go b/src/tui/ncurses.go
index 638a8629..7a443407 100644
--- a/src/curses/curses.go
+++ b/src/tui/ncurses.go
@@ -1,4 +1,7 @@
-package curses
+// +build !windows
+// +build !termbox
+
+package tui
/*
#include <ncurses.h>
@@ -10,7 +13,6 @@ package curses
SCREEN *c_newterm () {
return newterm(NULL, stderr, stdin);
}
-
*/
import "C"
@@ -23,6 +25,10 @@ import (
"unicode/utf8"
)
+type ColorPair int16
+type Attr C.int
+type WindowImpl C.WINDOW
+
const (
Bold = C.A_BOLD
Dim = C.A_DIM
@@ -31,89 +37,13 @@ const (
Underline = C.A_UNDERLINE
)
-type Attr C.int
-
-// Types of user action
const (
- Rune = iota
-
- CtrlA
- CtrlB
- CtrlC
- CtrlD
- CtrlE
- CtrlF
- CtrlG
- CtrlH
- Tab
- CtrlJ
- CtrlK
- CtrlL
- CtrlM
- CtrlN
- CtrlO
- CtrlP
- CtrlQ
- CtrlR
- CtrlS
- CtrlT
- CtrlU
- CtrlV
- CtrlW
- CtrlX
- CtrlY
- CtrlZ
- ESC
-
- Invalid
- Mouse
- DoubleClick
-
- BTab
- BSpace
-
- Del
- PgUp
- PgDn
-
- Up
- Down
- Left
- Right
- Home
- End
-
- SLeft
- SRight
-
- F1
- F2
- F3
- F4
- F5
- F6
- F7
- F8
- F9
- F10
-
- AltEnter
- AltSpace
- AltSlash
- AltBS
- AltA
- AltB
- AltC
- AltD
- AltE
- AltF
-
- AltZ = AltA + 'z' - 'a'
+ AttrRegular Attr = 0
)
// Pallete
const (
- _ = iota
+ ColDefault ColorPair = iota
ColNormal
ColPrompt
ColMatch
@@ -128,69 +58,95 @@ const (
ColUser // Should be the last entry
)
-const (
- doubleClickDuration = 500 * time.Millisecond
- colDefault = -1
- colUndefined = -2
+var (
+ _in *os.File
+ _screen *C.SCREEN
+ _colorMap map[int]ColorPair
+ _colorFn func(ColorPair, Attr) C.int
)
-type ColorTheme struct {
- Fg int16
- Bg int16
- DarkBg int16
- Prompt int16
- Match int16
- Current int16
- CurrentMatch int16
- Spinner int16
- Info int16
- Cursor int16
- Selected int16
- Header int16
- Border int16
-}
-
-type Event struct {
- Type int
- Char rune
- MouseEvent *MouseEvent
-}
-
-type MouseEvent struct {
- Y int
- X int
- S int
- Down bool
- Double bool
- Mod bool
+func init() {
+ _colorMap = make(map[int]ColorPair)
}
-var (
- _buf []byte
- _in *os.File
- _color bool
- _colorFn func(int, Attr) C.int
- _colorMap map[int]int
- _prevDownTime time.Time
- _clickY []int
- _screen *C.SCREEN
- Default16 *ColorTheme
- Dark256 *ColorTheme
- Light256 *ColorTheme
-)
+func (a Attr) Merge(b Attr) Attr {
+ return a | b
+}
+
+func DefaultTheme() *ColorTheme {
+ if C.tigetnum(C.CString("colors")) >= 256 {
+ return Dark256
+ }
+ return Default16
+}
+
+func Init(theme *ColorTheme, black bool, mouse bool) {
+ {
+ in, err := os.OpenFile("/dev/tty", syscall.O_RDONLY, 0)
+ if err != nil {
+ panic("Failed to open /dev/tty")
+ }
+ _in = in
+ // Break STDIN
+ // syscall.Dup2(int(in.Fd()), int(os.Stdin.Fd()))
+ }
+
+ C.setlocale(C.LC_ALL, C.CString(""))
+ _screen = C.c_newterm()
+ if _screen == nil {
+ fmt.Println("Invalid $TERM: " + os.Getenv("TERM"))
+ os.Exit(2)
+ }
+ C.set_term(_screen)
+ if mouse {
+ C.mousemask(C.ALL_MOUSE_EVENTS, nil)
+ }
+ C.noecho()
+ C.raw() // stty dsusp undef
+
+ _color = theme != nil
+ if _color {
+ C.start_color()
+ InitTheme(theme, black)
+ initPairs(theme)
+ C.bkgd(C.chtype(C.COLOR_PAIR(C.int(ColNormal))))
+ _colorFn = attrColored
+ } else {
+ _colorFn = attrMono
+ }
+}
+
+func initPairs(theme *ColorTheme) {
+ C.assume_default_colors(C.int(theme.Fg), C.int(theme.Bg))
+ initPair := func(group ColorPair, fg Color, bg Color) {
+ C.init_pair(C.short(group), C.short(fg), C.short(bg))
+ }
+ initPair(ColNormal, theme.Fg, theme.Bg)
+ initPair(ColPrompt, theme.Prompt, theme.Bg)
+ initPair(ColMatch, theme.Match, theme.Bg)
+ initPair(ColCurrent, theme.Current, theme.DarkBg)
+ initPair(ColCurrentMatch, theme.CurrentMatch, theme.DarkBg)
+ initPair(ColSpinner, theme.Spinner, theme.Bg)
+ initPair(ColInfo, theme.Info, theme.Bg)
+ initPair(ColCursor, theme.Cursor, theme.DarkBg)
+ initPair(ColSelected, theme.Selected, theme.DarkBg)
+ initPair(ColHeader, theme.Header, theme.Bg)
+ initPair(ColBorder, theme.Border, theme.Bg)
+}
+
+func Pause() {
+ C.endwin()
+}
-type Window struct {
- win *C.WINDOW
- Top int
- Left int
- Width int
- Height int
+func Close() {
+ C.endwin()
+ C.delscreen(_screen)
}
func NewWindow(top int, left int, width int, height int, border bool) *Window {
win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
if _color {
- C.wbkgd(win, C.chtype(C.COLOR_PAIR(ColNormal)))
+ C.wbkgd(win, C.chtype(C.COLOR_PAIR(C.int(ColNormal))))
}
if border {
attr := _colorFn(ColBorder, 0)
@@ -200,7 +156,7 @@ func NewWindow(top int, left int, width int, height int, border bool) *Window {
}
return &Window{
- win: win,
+ impl: (*WindowImpl)(win),
Top: top,
Left: left,
Width: width,
@@ -208,72 +164,7 @@ func NewWindow(top int, left int, width int, height int, border bool) *Window {
}
}
-func EmptyTheme() *ColorTheme {
- return &ColorTheme{
- Fg: colUndefined,
- Bg: colUndefined,
- DarkBg: colUndefined,
- Prompt: colUndefined,
- Match: colUndefined,
- Current: colUndefined,
- CurrentMatch: colUndefined,
- Spinner: colUndefined,
- Info: colUndefined,
- Cursor: colUndefined,
- Selected: colUndefined,
- Header: colUndefined,
- Border: colUndefined}
-}
-
-func init() {
- _prevDownTime = time.Unix(0, 0)
- _clickY = []int{}
- _colorMap = make(map[int]int)
- Default16 = &ColorTheme{
- Fg: colDefault,
- Bg: colDefault,
- DarkBg: C.COLOR_BLACK,
- Prompt: C.COLOR_BLUE,
- Match: C.COLOR_GREEN,
- Current: C.COLOR_YELLOW,
- CurrentMatch: C.COLOR_GREEN,
- Spinner: C.COLOR_GREEN,
- Info: C.COLOR_WHITE,
- Cursor: C.COLOR_RED,
- Selected: C.COLOR_MAGENTA,
- Header: C.COLOR_CYAN,
- Border: C.COLOR_BLACK}
- Dark256 = &ColorTheme{
- Fg: colDefault,
- Bg: colDefault,
- DarkBg: 236,
- Prompt: 110,
- Match: 108,
- Current: 254,
- CurrentMatch: 151,
- Spinner: 148,
- Info: 144,
- Cursor: 161,
- Selected: 168,
- Header: 109,
- Border: 59}
- Light256 = &ColorTheme{
- Fg: colDefault,
- Bg: colDefault,
- DarkBg: 251,
- Prompt: 25,
- Match: 66,
- Current: 237,
- CurrentMatch: 23,
- Spinner: 65,
- Info: 101,
- Cursor: 161,
- Selected: 168,
- Header: 31,
- Border: 145}
-}
-
-func attrColored(pair int, a Attr) C.int {
+func attrColored(pair ColorPair, a Attr) C.int {
var attr C.int
if pair > 0 {
attr = C.COLOR_PAIR(C.int(pair))
@@ -281,11 +172,11 @@ func attrColored(pair int, a Attr) C.int {
return attr | C.int(a)
}
-func attrMono(pair int, a Attr) C.int {
+func attrMono(pair ColorPair, a Attr) C.int {
var attr C.int
switch pair {
case ColCurrent:
- if a&C.A_BOLD == C.A_BOLD {
+ if C.int(a)&C.A_BOLD == C.A_BOLD {
attr = C.A_REVERSE
}
case ColMatch:
@@ -293,7 +184,7 @@ func attrMono(pair int, a Attr) C.int {
case ColCurrentMatch:
attr = C.A_UNDERLINE | C.A_REVERSE
}
- if a&C.A_BOLD == C.A_BOLD {
+ if C.int(a)&C.A_BOLD == C.A_BOLD {
attr = attr | C.A_BOLD
}
return attr
@@ -307,106 +198,95 @@ func MaxY() int {
return int(C.LINES)
}
-func getch(nonblock bool) int {
- b := make([]byte, 1)
- syscall.SetNonblock(int(_in.Fd()), nonblock)
- _, err := _in.Read(b)
- if err != nil {
- return -1
- }
- return int(b[0])
+func (w *Window) win() *C.WINDOW {
+ return (*C.WINDOW)(w.impl)
}
-func Init(theme *ColorTheme, black bool, mouse bool) {
- {
- in, err := os.OpenFile("/dev/tty", syscall.O_RDONLY, 0)
- if err != nil {
- panic("Failed to open /dev/tty")
- }
- _in = in
- // Break STDIN
- // syscall.Dup2(int(in.Fd()), int(os.Stdin.Fd()))
- }
+func (w *Window) Close() {
+ C.delwin(w.win())
+}
- C.setlocale(C.LC_ALL, C.CString(""))
- _screen = C.c_newterm()
- if _screen == nil {
- fmt.Println("Invalid $TERM: " + os.Getenv("TERM"))
- os.Exit(2)
- }
- C.set_term(_screen)
- if mouse {
- C.mousemask(C.ALL_MOUSE_EVENTS, nil)
- }
- C.noecho()
- C.raw() // stty dsusp undef
+func (w *Window) Enclose(y int, x int) bool {
+ return bool(C.wenclose(w.win(), C.int(y), C.int(x)))
+}
- _color = theme != nil
- if _color {
- C.start_color()
- var baseTheme *ColorTheme
- if C.tigetnum(C.CString("colors")) >= 256 {
- baseTheme = Dark256
- } else {
- baseTheme = Default16
- }
- initPairs(baseTheme, theme, black)
- C.bkgd(C.chtype(C.COLOR_PAIR(ColNormal)))
- _colorFn = attrColored
- } else {
- _colorFn = attrMono
- }
+func (w *Window) Move(y int, x int) {
+ C.wmove(w.win(), C.int(y), C.int(x))
}
-func override(baseTheme *ColorTheme, theme *ColorTheme) {
- o := func(a int16, b int16) int16 {
- if b == colUndefined {
- return a
+func (w *Window) MoveAndClear(y int, x int) {
+ w.Move(y, x)
+ C.wclrtoeol(w.win())
+}
+
+func (w *Window) Print(text string) {
+ C.waddstr(w.win(), C.CString(strings.Map(func(r rune) rune {
+ if r < 32 {
+ return -1
}
- return b
- }
- theme.Fg = o(baseTheme.Fg, theme.Fg)
- theme.Bg = o(baseTheme.Bg, theme.Bg)
- theme.DarkBg = o(baseTheme.DarkBg, theme.DarkBg)
- theme.Prompt = o(baseTheme.Prompt, theme.Prompt)
- theme.Match = o(baseTheme.Match, theme.Match)
- theme.Current = o(baseTheme.Current, theme.Current)
- theme.CurrentMatch = o(baseTheme.CurrentMatch, theme.CurrentMatch)
- theme.Spinner = o(baseTheme.Spinner, theme.Spinner)
- theme.Info = o(baseTheme.Info, theme.Info)
- theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
- theme.Selected = o(baseTheme.Selected, theme.Selected)
- theme.Header = o(baseTheme.Header, theme.Header)
- theme.Border = o(baseTheme.Border, theme.Border)
-}
-
-func initPairs(baseTheme *ColorTheme, theme *ColorTheme, black bool) {
- if black {
- theme.Bg = C.COLOR_BLACK
+ return r
+ }, text)))
+}
+
+func (w *Window) CPrint(pair ColorPair, a Attr, text string) {
+ attr := _colorFn(pair, a)
+ C.wattron(w.win(), attr)
+ w.Print(text)
+ C.wattroff(w.win(), attr)
+}
+
+func Clear() {
+ C.clear()
+ C.endwin()
+}
+
+func Refresh() {
+ C.refresh()
+}
+
+func (w *Window) Erase() {
+ C.werase(w.win())
+}
+
+func (w *Window) Fill(str string) bool {
+ return C.waddstr(w.win(), C.CString(str)) == C.OK
+}
+
+func (w *Window) CFill(str string, fg Color, bg Color, a Attr) bool {
+ attr := _colorFn(PairFor(fg, bg), a)
+ C.wattron(w.win(), attr)
+ ret := w.Fill(str)
+ C.wattroff(w.win(), attr)
+ return ret
+}
+
+func RefreshWindows(windows []*Window) {
+ for _, w := range windows {
+ C.wnoutrefresh(w.win())
}
- // Updates theme
- override(baseTheme, theme)
+ C.doupdate()
+}
- C.assume_default_colors(C.int(theme.Fg), C.int(theme.Bg))
- initPair := func(group C.short, fg int16, bg int16) {
- C.init_pair(group, C.short(fg), C.short(bg))
+func PairFor(fg Color, bg Color) ColorPair {
+ key := (int(fg) << 8) + int(bg)
+ if found, prs := _colorMap[key]; prs {
+ return found
}
- initPair(ColNormal, theme.Fg, theme.Bg)
- initPair(ColPrompt, theme.Prompt, theme.Bg)
- initPair(ColMatch, theme.Match, theme.Bg)
- initPair(ColCurrent, theme.Current, theme.DarkBg)
- initPair(ColCurrentMatch, theme.CurrentMatch, theme.DarkBg)
- initPair(ColSpinner, theme.Spinner, theme.Bg)
- initPair(ColInfo, theme.Info, theme.Bg)
- initPair(ColCursor, theme.Cursor, theme.DarkBg)
- initPair(ColSelected, theme.Selected, theme.DarkBg)
- initPair(ColHeader, theme.Header, theme.Bg)
- initPair(ColBorder, theme.Border, theme.Bg)
+
+ id := ColorPair(len(_colorMap) + int(ColUser))
+ C.init_pair(C.short(id), C.short(fg), C.short(bg))
+ _colorMap[key] = id
+ return id
}
-func Close() {
- C.endwin()
- C.delscreen(_screen)
+func getch(nonblock bool) int {
+ b := make([]byte, 1)
+ syscall.SetNonblock(int(_in.Fd()), nonblock)
+ _, err := _in.Read(b)
+ if err != nil {
+ return -1
+ }
+ return int(b[0])
}
func GetBytes() []byte {
@@ -631,84 +511,3 @@ func GetChar() Event {
sz = rsz
return Event{Rune, r, nil}
}
-
-func (w *Window) Close() {
- C.delwin(w.win)
-}
-
-func (w *Window) Enclose(y int, x int) bool {
- return bool(C.wenclose(w.win, C.int(y), C.int(x)))
-}
-
-func (w *Window) Move(y int, x int) {
- C.wmove(w.win, C.int(y), C.int(x))
-}
-
-func (w *Window) MoveAndClear(y int, x int) {
- w.Move(y, x)
- C.wclrtoeol(w.win)
-}
-
-func (w *Window) Print(text string) {
- C.waddstr(w.win, C.CString(strings.Map(func(r rune) rune {
- if r < 32 {
- return -1
- }
- return r
- }, text)))
-}
-
-func (w *Window) CPrint(pair int, a Attr, text string) {
- attr := _colorFn(pair, a)
- C.wattron(w.win, attr)
- w.Print(text)
- C.wattroff(w.win, attr)
-}
-
-func Clear() {
- C.clear()
-}
-
-func Endwin() {
- C.endwin()
-}
-
-func Refresh() {
- C.refresh()
-}
-
-func (w *Window) Erase() {
- C.werase(w.win)
-}
-
-func (w *Window) Fill(str string) bool {
- return C.waddstr(w.win, C.CString(str)) == C.OK
-}
-
-func (w *Window) CFill(str string, fg int, bg int, a Attr) bool {
- attr := _colorFn(PairFor(fg, bg), a)
- C.wattron(w.win, attr)
- ret := w.Fill(str)
- C.wattroff(w.win, attr)
- return ret
-}
-
-func (w *Window) Refresh() {
- C.wnoutrefresh(w.win)
-}
-
-func DoUpdate() {
- C.doupdate()
-}
-
-func PairFor(fg int, bg int) int {
- key := (fg << 8) + bg
- if found, prs := _colorMap[key]; prs {
- return found
- }
-
- id := len(_colorMap) + ColUser
- C.init_pair(C.short(id), C.short(fg), C.short(bg))
- _colorMap[key] = id
- return id
-}
diff --git a/src/tui/termbox.go b/src/tui/termbox.go
new file mode 100644
index 00000000..c49512ce
--- /dev/null
+++ b/src/tui/termbox.go
@@ -0,0 +1,151 @@
+// +build termbox windows
+
+package tui
+
+import (
+ "github.com/nsf/termbox-go"
+)
+
+type ColorPair [2]Color
+type Attr uint16
+type WindowImpl int // FIXME
+
+const (
+ // TODO
+ _ = iota
+ Bold
+ Dim
+ Blink
+ Reverse
+ Underline
+)
+
+const (
+ AttrRegular Attr = 0
+)
+
+var (
+ ColDefault = ColorPair{colDefault, colDefault}
+ ColNormal ColorPair
+ ColPrompt ColorPair
+ ColMatch ColorPair
+ ColCurrent ColorPair
+ ColCurrentMatch ColorPair
+ ColSpinner ColorPair
+ ColInfo ColorPair
+ ColCursor ColorPair
+ ColSelected ColorPair
+ ColHeader ColorPair
+ ColBorder ColorPair
+ ColUser ColorPair
+)
+
+func DefaultTheme() *ColorTheme {
+ if termbox.SetOutputMode(termbox.OutputCurrent) == termbox.Output256 {
+ return Dark256
+ }
+ return Default16
+}
+
+func PairFor(fg Color, bg Color) ColorPair {
+ return [2]Color{fg, bg}
+}
+
+func (a Attr) Merge(b Attr) Attr {
+ return a | b
+}
+
+func Init(theme *ColorTheme, black bool, mouse bool) {
+ ColNormal = ColorPair{theme.Fg, theme.Bg}
+ ColPrompt = ColorPair{theme.Prompt, theme.Bg}
+ ColMatch = ColorPair{theme.Match, theme.Bg}
+ ColCurrent = ColorPair{theme.Current, theme.DarkBg}
+ ColCurrentMatch = ColorPair{theme.CurrentMatch, theme.DarkBg}
+ ColSpinner = ColorPair{theme.Spinner, theme.Bg}
+ ColInfo = ColorPair{theme.Info, theme.Bg}
+ ColCursor = ColorPair{theme.Cursor, theme.DarkBg}
+ ColSelected = ColorPair{theme.Selected, theme.DarkBg}
+ ColHeader = ColorPair{theme.Header, theme.Bg}
+ ColBorder = ColorPair{theme.Border, theme.Bg}
+
+ // TODO
+}
+
+func MaxX() int {
+ // TODO
+ return 80
+}
+
+func MaxY() int {
+ // TODO
+ return 24
+}
+
+func Clear() {
+ // TODO
+}
+
+func Refresh() {
+ // TODO
+}
+
+func GetChar() Event {
+ // TODO
+ return Event{}
+}
+
+func Pause() {
+ // TODO
+}
+
+func Close() {
+ // TODO
+}
+
+func RefreshWindows(windows []*Window) {
+ // TODO
+}
+
+func NewWindow(top int, left int, width int, height int, border bool) *Window {
+ // TODO
+ return &Window{}
+}
+
+func (w *Window) Close() {
+ // TODO
+}
+
+func (w *Window) Erase() {
+ // TODO
+}
+
+func (w *Window) Enclose(y int, x int) bool {
+ // TODO
+ return false
+}
+
+func (w *Window) Move(y int, x int) {
+ // TODO
+}
+
+func (w *Window) MoveAndClear(y int, x int) {
+ // TODO
+}
+
+func (w *Window) Print(text string) {
+ // TODO
+}
+
+func (w *Window) CPrint(pair ColorPair, a Attr, text string) {
+ // TODO
+}
+
+func (w *Window) Fill(str string) bool {
+ // TODO
+ return false
+}
+
+func (w *Window) CFill(str string, fg Color, bg Color, a Attr) bool {
+ // TODO
+ return false
+}
diff --git a/src/tui/tui.go b/src/tui/tui.go
new file mode 100644
index 00000000..a33baa4a
--- /dev/null
+++ b/src/tui/tui.go
@@ -0,0 +1,250 @@
+package tui
+
+import (
+ "time"
+)
+
+// Types of user action
+const (
+ Rune = iota
+
+ CtrlA
+ CtrlB
+ CtrlC
+ CtrlD
+ CtrlE
+ CtrlF
+ CtrlG
+ CtrlH
+ Tab
+ CtrlJ
+ CtrlK
+ CtrlL
+ CtrlM
+ CtrlN
+ CtrlO
+ CtrlP
+ CtrlQ
+ CtrlR
+ CtrlS
+ CtrlT
+ CtrlU
+ CtrlV
+ CtrlW
+ CtrlX
+ CtrlY
+ CtrlZ
+ ESC
+
+ Invalid
+ Mouse
+ DoubleClick
+
+ BTab
+ BSpace
+
+ Del
+ PgUp
+ PgDn
+
+ Up
+ Down
+ Left
+ Right
+ Home
+ End
+
+ SLeft
+ SRight
+
+ F1
+ F2
+ F3
+ F4
+ F5
+ F6
+ F7
+ F8
+ F9
+ F10
+
+ AltEnter
+ AltSpace
+ AltSlash
+ AltBS
+ AltA
+ AltB
+ AltC
+ AltD
+ AltE
+ AltF
+
+ AltZ = AltA + 'z' - 'a'
+)
+
+const (
+ doubleClickDuration = 500 * time.Millisecond
+)
+
+type Color int16
+
+const (
+ colUndefined Color = -2
+ colDefault = -1
+)
+
+const (
+ colBlack Color = iota
+ colRed
+ colGreen
+ colYellow
+ colBlue
+ colMagenta
+ colCyan
+ colWhite
+)
+
+type ColorTheme struct {
+ Fg Color
+ Bg Color
+ DarkBg Color
+ Prompt Color
+ Match Color
+ Current Color
+ CurrentMatch Color
+ Spinner Color
+ Info Color
+ Cursor Color
+ Selected Color
+ Header Color
+ Border Color
+}
+
+type Event struct {
+ Type int
+ Char rune
+ MouseEvent *MouseEvent
+}
+
+type MouseEvent struct {
+ Y int
+ X int
+ S int
+ Down bool
+ Double bool
+ Mod bool
+}
+
+var (
+ _buf []byte
+ _color bool
+ _prevDownTime time.Time
+ _clickY []int
+ Default16 *ColorTheme
+ Dark256 *ColorTheme
+ Light256 *ColorTheme
+)
+
+type Window struct {
+ impl *WindowImpl
+ Top int
+ Left int
+ Width int
+ Height int
+}
+
+func EmptyTheme() *ColorTheme {
+ return &ColorTheme{
+ Fg: colUndefined,
+ Bg: colUndefined,
+ DarkBg: colUndefined,
+ Prompt: colUndefined,
+ Match: colUndefined,
+ Current: colUndefined,
+ CurrentMatch: colUndefined,
+ Spinner: colUndefined,
+ Info: colUndefined,
+ Cursor: colUndefined,
+ Selected: colUndefined,
+ Header: colUndefined,
+ Border: colUndefined}
+}
+
+func init() {
+ _prevDownTime = time.Unix(0, 0)
+ _clickY = []int{}
+ Default16 = &ColorTheme{
+ Fg: colDefault,
+ Bg: colDefault,
+ DarkBg: colBlack,
+ Prompt: colBlue,
+ Match: colGreen,
+ Current: colYellow,
+ CurrentMatch: colGreen,
+ Spinner: colGreen,
+ Info: colWhite,
+ Cursor: colRed,
+ Selected: colMagenta,
+ Header: colCyan,
+ Border: colBlack}
+ Dark256 = &ColorTheme{
+ Fg: colDefault,
+ Bg: colDefault,
+ DarkBg: 236,
+ Prompt: 110,
+ Match: 108,
+ Current: 254,
+ CurrentMatch: 151,
+ Spinner: 148,
+ Info: 144,
+ Cursor: 161,
+ Selected: 168,
+ Header: 109,
+ Border: 59}
+ Light256 = &ColorTheme{
+ Fg: colDefault,
+ Bg: colDefault,
+ DarkBg: 251,
+ Prompt: 25,
+ Match: 66,
+ Current: 237,
+ CurrentMatch: 23,
+ Spinner: 65,
+ Info: 101,
+ Cursor: 161,
+ Selected: 168,
+ Header: 31,
+ Border: 145}
+}
+
+func InitTheme(theme *ColorTheme, black bool) {
+ _color = theme != nil
+ if !_color {
+ return
+ }
+
+ baseTheme := DefaultTheme()
+ if black {
+ theme.Bg = colBlack
+ }
+
+ o := func(a Color, b Color) Color {
+ if b == colUndefined {
+ return a
+ }
+ return b
+ }
+ theme.Fg = o(baseTheme.Fg, theme.Fg)
+ theme.Bg = o(baseTheme.Bg, theme.Bg)
+ theme.DarkBg = o(baseTheme.DarkBg, theme.DarkBg)
+ theme.Prompt = o(baseTheme.Prompt, theme.Prompt)
+ theme.Match = o(baseTheme.Match, theme.Match)
+ theme.Current = o(baseTheme.Current, theme.Current)
+ theme.CurrentMatch = o(baseTheme.CurrentMatch, theme.CurrentMatch)
+ theme.Spinner = o(baseTheme.Spinner, theme.Spinner)
+ theme.Info = o(baseTheme.Info, theme.Info)
+ theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
+ theme.Selected = o(baseTheme.Selected, theme.Selected)
+ theme.Header = o(baseTheme.Header, theme.Header)
+ theme.Border = o(baseTheme.Border, theme.Border)
+}
diff --git a/src/curses/curses_test.go b/src/tui/tui_test.go
index db75c408..4a2fee91 100644
--- a/src/curses/curses_test.go
+++ b/src/tui/tui_test.go
@@ -1,4 +1,4 @@
-package curses
+package tui
import (
"testing"
diff --git a/src/util/util.go b/src/util/util.go
index e273882c..2a1607ce 100644
--- a/src/util/util.go
+++ b/src/util/util.go
@@ -1,13 +1,11 @@
package util
-// #include <unistd.h>
-import "C"
-
import (
"math"
"os"
- "os/exec"
"time"
+
+ "github.com/junegunn/go-isatty"
)
// Max returns the largest integer
@@ -95,14 +93,5 @@ func DurWithin(
// IsTty returns true is stdin is a terminal
func IsTty() bool {
- return int(C.isatty(C.int(os.Stdin.Fd()))) != 0
-}
-
-// ExecCommand executes the given command with $SHELL
-func ExecCommand(command string) *exec.Cmd {
- shell := os.Getenv("SHELL")
- if len(shell) == 0 {
- shell = "sh"
- }
- return exec.Command(shell, "-c", command)
+ return isatty.IsTerminal(os.Stdin.Fd())
}
diff --git a/src/util/util_unix.go b/src/util/util_unix.go
new file mode 100644
index 00000000..dcc5cb5e
--- /dev/null
+++ b/src/util/util_unix.go
@@ -0,0 +1,17 @@
+// +build !windows
+
+package util
+
+import (
+ "os"
+ "os/exec"
+)
+
+// ExecCommand executes the given command with $SHELL
+func ExecCommand(command string) *exec.Cmd {
+ shell := os.Getenv("SHELL")
+ if len(shell) == 0 {
+ shell = "sh"
+ }
+ return exec.Command(shell, "-c", command)
+}
diff --git a/src/util/util_windows.go b/src/util/util_windows.go
new file mode 100644
index 00000000..a660f39e
--- /dev/null
+++ b/src/util/util_windows.go
@@ -0,0 +1,17 @@
+// +build windows
+
+package util
+
+import (
+ "os"
+ "os/exec"
+)
+
+// ExecCommand executes the given command with $SHELL
+func ExecCommand(command string) *exec.Cmd {
+ shell := os.Getenv("SHELL")
+ if len(shell) == 0 {
+ shell = "cmd"
+ }
+ return exec.Command(shell, "/c", command)
+}