diff --git a/.travis.yml b/.travis.yml index 9a9779a..ce3b11b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,10 @@ language: go go: - - 1.4 - 1.5 before_install: - go get golang.org/x/crypto/ssh/terminal script: - - go install github.com/chzyer/readline/example + - GOOS=windows go install github.com/chzyer/readline/example + - GOOS=linux go install github.com/chzyer/readline/example + - GOOS=darwin go install github.com/chzyer/readline/example - go test -v diff --git a/std_ansi.go b/ansi_windows.go similarity index 97% rename from std_ansi.go rename to ansi_windows.go index 322f24c..68c79b8 100644 --- a/std_ansi.go +++ b/ansi_windows.go @@ -12,9 +12,41 @@ import ( "unsafe" ) -func init() { - Stdout = NewANSIWriter(Stdout) - Stderr = NewANSIWriter(Stderr) +const ( + _ = uint16(0) + COLOR_FBLUE = 0x0001 + COLOR_FGREEN = 0x0002 + COLOR_FRED = 0x0004 + COLOR_FINTENSITY = 0x0008 + + COLOR_BBLUE = 0x0010 + COLOR_BGREEN = 0x0020 + COLOR_BRED = 0x0040 + COLOR_BINTENSITY = 0x0080 + + COMMON_LVB_UNDERSCORE = 0x8000 +) + +var ColorTableFg = []word{ + 0, // 30: Black + COLOR_FRED, // 31: Red + COLOR_FGREEN, // 32: Green + COLOR_FRED | COLOR_FGREEN, // 33: Yellow + COLOR_FBLUE, // 34: Blue + COLOR_FRED | COLOR_FBLUE, // 35: Magenta + COLOR_FGREEN | COLOR_FBLUE, // 36: Cyan + COLOR_FRED | COLOR_FBLUE | COLOR_FGREEN, // 37: White +} + +var ColorTableBg = []word{ + 0, // 40: Black + COLOR_BRED, // 41: Red + COLOR_BGREEN, // 42: Green + COLOR_BRED | COLOR_BGREEN, // 43: Yellow + COLOR_BBLUE, // 44: Blue + COLOR_BRED | COLOR_BBLUE, // 45: Magenta + COLOR_BGREEN | COLOR_BBLUE, // 46: Cyan + COLOR_BRED | COLOR_BBLUE | COLOR_BGREEN, // 47: White } type ANSIWriter struct { @@ -140,11 +172,13 @@ func (a *ANSIWriter) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) boo break } if c >= 30 && c < 40 { + color ^= COLOR_FINTENSITY color |= ColorTableFg[c-30] } else if c >= 40 && c < 50 { + color ^= COLOR_BINTENSITY color |= ColorTableBg[c-40] } else if c == 4 { - color |= COMMON_LVB_UNDERSCORE + color |= COMMON_LVB_UNDERSCORE | ColorTableFg[7] } else { // unknown code treat as reset color = ColorTableFg[7] } @@ -225,40 +259,3 @@ func eraseLine() error { uintptr(unsafe.Pointer(&written)), ) } - -const ( - _ = uint16(0) - COLOR_FBLUE = 0x0001 - COLOR_FGREEN = 0x0002 - COLOR_FRED = 0x0004 - COLOR_FINTENSITY = 0x0008 - - COLOR_BBLUE = 0x0010 - COLOR_BGREEN = 0x0020 - COLOR_BRED = 0x0040 - COLOR_BINTENSITY = 0x0080 - - COMMON_LVB_UNDERSCORE = 0x8000 -) - -var ColorTableFg = []word{ - 0, // 30: Black - COLOR_FRED, // 31: Red - COLOR_FGREEN, // 32: Green - COLOR_FRED | COLOR_FGREEN, // 33: Yellow - COLOR_FBLUE, // 34: Blue - COLOR_FRED | COLOR_FBLUE, // 35: Magenta - COLOR_FGREEN | COLOR_FBLUE, // 36: Cyan - COLOR_FRED | COLOR_FBLUE | COLOR_FGREEN, // 37: White -} - -var ColorTableBg = []word{ - 0, // 40: Black - COLOR_BRED, // 41: Red - COLOR_BGREEN, // 42: Green - COLOR_BRED | COLOR_BGREEN, // 43: Yellow - COLOR_BBLUE, // 44: Blue - COLOR_BRED | COLOR_BBLUE, // 45: Magenta - COLOR_BGREEN | COLOR_BBLUE, // 46: Cyan - COLOR_BRED | COLOR_BBLUE | COLOR_BGREEN, // 47: White -} diff --git a/char.go b/char.go index ec6cd1d..1293e16 100644 --- a/char.go +++ b/char.go @@ -26,7 +26,7 @@ const ( ) const ( - MetaPrev = -iota - 1 + MetaPrev rune = -iota - 1 MetaNext MetaDelete MetaBackspace diff --git a/example/main.go b/example/main.go index 87f9eba..e13d6b3 100644 --- a/example/main.go +++ b/example/main.go @@ -7,6 +7,8 @@ import ( "strings" "time" + "gopkg.in/logex.v1" + "github.com/chzyer/readline" ) @@ -39,6 +41,7 @@ func main() { HistoryFile: "/tmp/readline.tmp", AutoComplete: completer, }) + logex.SetStd(logex.NewLoggerEx(l.Stderr())) if err != nil { panic(err) } diff --git a/rawreader_windows.go b/rawreader_windows.go new file mode 100644 index 0000000..5e7b873 --- /dev/null +++ b/rawreader_windows.go @@ -0,0 +1,122 @@ +// +build windows + +package readline + +import "unsafe" + +const ( + VK_CANCEL = 0x03 + VK_BACK = 0x08 + VK_TAB = 0x09 + VK_RETURN = 0x0D + VK_SHIFT = 0x10 + VK_CONTROL = 0x11 + VK_MENU = 0x12 + VK_ESCAPE = 0x1B + VK_LEFT = 0x25 + VK_UP = 0x26 + VK_RIGHT = 0x27 + VK_DOWN = 0x28 + VK_DELETE = 0x2E + VK_LSHIFT = 0xA0 + VK_RSHIFT = 0xA1 + VK_LCONTROL = 0xA2 + VK_RCONTROL = 0xA3 +) + +type RawReader struct { + ctrlKey bool + altKey bool +} + +func NewRawReader() *RawReader { + r := new(RawReader) + return r +} + +func (r *RawReader) Read(buf []byte) (int, error) { + ir := new(_INPUT_RECORD) + var read int + var err error +next: + err = kernel.ReadConsoleInputW(stdin, + uintptr(unsafe.Pointer(ir)), + 1, + uintptr(unsafe.Pointer(&read)), + ) + if err != nil { + return 0, err + } + if ir.EventType != EVENT_KEY { + goto next + } + ker := (*_KEY_EVENT_RECORD)(unsafe.Pointer(&ir.Event[0])) + if ker.bKeyDown == 0 { // keyup + if r.ctrlKey || r.altKey { + switch ker.wVirtualKeyCode { + case VK_RCONTROL, VK_LCONTROL: + r.ctrlKey = false + case VK_MENU: //alt + r.altKey = false + } + } + goto next + } + + if ker.unicodeChar == 0 { + var target rune + switch ker.wVirtualKeyCode { + case VK_RCONTROL, VK_LCONTROL: + r.ctrlKey = true + case VK_MENU: //alt + r.altKey = true + case VK_LEFT: + target = CharBackward + case VK_RIGHT: + target = CharForward + case VK_UP: + target = CharPrev + case VK_DOWN: + target = CharNext + } + if target != 0 { + return r.write(buf, target) + } + goto next + } + char := rune(ker.unicodeChar) + if r.ctrlKey { + switch char { + case 'A': + char = CharLineStart + case 'E': + char = CharLineEnd + case 'R': + char = CharBckSearch + case 'S': + char = CharFwdSearch + } + } else if r.altKey { + switch char { + case VK_BACK: + char = CharBackspace + } + return r.writeEsc(buf, char) + } + return r.write(buf, char) +} + +func (r *RawReader) writeEsc(b []byte, char rune) (int, error) { + b[0] = '\033' + n := copy(b[1:], []byte(string(char))) + return n + 1, nil +} + +func (r *RawReader) write(b []byte, char rune) (int, error) { + n := copy(b, []byte(string(char))) + return n, nil +} + +func (r *RawReader) Close() error { + return nil +} diff --git a/std.go b/std.go index 6a3f876..3baf5a7 100644 --- a/std.go +++ b/std.go @@ -6,6 +6,7 @@ import ( ) var ( + Stdin io.ReadCloser = os.Stdin Stdout io.WriteCloser = os.Stdout Stderr io.WriteCloser = os.Stderr ) diff --git a/std_windows.go b/std_windows.go new file mode 100644 index 0000000..b10f91b --- /dev/null +++ b/std_windows.go @@ -0,0 +1,9 @@ +// +build windows + +package readline + +func init() { + Stdin = NewRawReader() + Stdout = NewANSIWriter(Stdout) + Stderr = NewANSIWriter(Stderr) +} diff --git a/terminal.go b/terminal.go index 23bb3a1..12ac640 100644 --- a/terminal.go +++ b/terminal.go @@ -3,7 +3,6 @@ package readline import ( "bufio" "fmt" - "os" "sync" "sync/atomic" @@ -81,7 +80,7 @@ func (t *Terminal) ioloop() { expectNextChar bool ) - buf := bufio.NewReader(os.Stdin) + buf := bufio.NewReader(Stdin) for { if !expectNextChar { atomic.StoreInt64(&t.isReading, 0) diff --git a/windows_api.go b/windows_api.go index 0a059b3..63f4f7b 100644 --- a/windows_api.go +++ b/windows_api.go @@ -11,14 +11,16 @@ import ( var ( kernel = NewKernel() stdout = uintptr(syscall.Stdout) + stdin = uintptr(syscall.Stdin) ) type Kernel struct { SetConsoleCursorPosition, SetConsoleTextAttribute, - GetConsoleScreenBufferInfo, FillConsoleOutputCharacterW, FillConsoleOutputAttribute, + ReadConsoleInputW, + GetConsoleScreenBufferInfo, GetConsoleCursorInfo, GetStdHandle CallFunc } @@ -26,6 +28,7 @@ type Kernel struct { type short int16 type word uint16 type dword uint32 +type wchar uint16 type _COORD struct { x short @@ -36,6 +39,34 @@ func (c *_COORD) ptr() uintptr { return uintptr(*(*int32)(unsafe.Pointer(c))) } +const ( + EVENT_KEY = 0x0001 + EVENT_MOUSE = 0x0002 + EVENT_WINDOW_BUFFER_SIZE = 0x0004 + EVENT_MENU = 0x0008 + EVENT_FOCUS = 0x0010 +) + +type _KEY_EVENT_RECORD struct { + bKeyDown int32 + wRepeatCount word + wVirtualKeyCode word + wVirtualScanCode word + unicodeChar wchar + dwControlKeyState dword +} + +// KEY_EVENT_RECORD KeyEvent; +// MOUSE_EVENT_RECORD MouseEvent; +// WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent; +// MENU_EVENT_RECORD MenuEvent; +// FOCUS_EVENT_RECORD FocusEvent; +type _INPUT_RECORD struct { + EventType word + Padding uint16 + Event [16]byte +} + type _CONSOLE_SCREEN_BUFFER_INFO struct { dwSize _COORD dwCursorPosition _COORD