m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-16 15:23:48 -05:00

Compare commits

...

15 Commits

Author SHA1 Message Date
Junegunn Choi
58b5be8ab6 0.17.0-2 2017-09-05 13:40:58 +09:00
Jan Edmund Lazo
26d7896877 [vim] Bind Ctrl-J in Vim terminal to fix enter key
Temporary workaround for non-Windows environment

Reference:
https://github.com/vim/vim/issues/1998
https://github.com/junegunn/fzf/pull/1019#issuecomment-327008348
2017-09-05 13:29:46 +09:00
Jan Edmund Lazo
fd6bc7308f [vim] Use s:execute_term in Windows
IMPORTANT:
cmd.exe and powershell are fine in default Windows terminal.
cmd.exe prompt is broken on ConEmu because it natively supports ucs-2 only.
utf-16 support is exclusive to .Net (ie. powershell).
utf-8 supports requires chcp, external program, but does not fix the cmd.exe prompt.
Use powershell on ConEmu to avoid corrupted text on display
2017-09-05 13:29:46 +09:00
Jan Edmund Lazo
6c41c95f28 [vim] s:execute_term works in GVim on Windows
Requirements:
- compiled with +terminal
- has patch-8.0.995
- has('gui_running') returns 1
2017-09-05 13:29:46 +09:00
Jan Edmund Lazo
446e04469d [neovim] use batchfile for s:execute_term in Windows 2017-09-05 13:29:46 +09:00
Michael Smith
5097e563df [neovim] Fix terminal buffer marker on Windows
Original Patch: a9bf29b65e
2017-09-05 13:29:46 +09:00
Jan Edmund Lazo
c7ad97c641 [neovim] use terminal in Windows for v0.2.1+ 2017-09-05 13:29:46 +09:00
Junegunn Choi
9516fe3324 [install] Add --no-{bash,zsh,fish}
Close #1040
2017-09-03 11:45:22 +09:00
Junegunn Choi
20cdbac8c3 [install] Ignore user-defined grep aliases 2017-09-03 11:38:22 +09:00
Junegunn Choi
e3e7b3360c Delete ncurses implementation 2017-09-02 03:19:50 +09:00
Junegunn Choi
655dfb8328 [fzf-tmux] Remove cat command
Close #1039
2017-09-01 18:46:00 +09:00
Mike Hearn
9b9c67b768 [fzf-tmux] Add pane_height/pane_width fallback (#1037) 2017-09-01 11:16:00 +09:00
Junegunn Choi
5b7457ff08 [install] Wait for a linefeed when asking for confirmation
Close #1035
2017-09-01 02:45:48 +09:00
Junegunn Choi
48adad5454 [neovim] Set &shell to sh (again) after opening a new window
Close #1031
2017-08-30 18:58:28 +09:00
Jack O'Connor
b27dc3eb17 [vim] Add parens around piped source commands (#1029)
Previously a command like `echo a && echo b` would get transformed into
`echo a && echo b | fzf`, which only pipes the output of the second
command. Adding parentheses around the source command avoids this issue,
and works on both Unix and Windows.
2017-08-28 22:32:13 +09:00
5 changed files with 81 additions and 536 deletions

View File

@@ -1,6 +1,17 @@
CHANGELOG
=========
0.17.0-2
--------
A maintenance release for auxiliary scripts. fzf binaries are not updated.
- Experimental support for the builtin terminal of Vim 8
- fzf can now run inside GVim
- Updated Vim plugin to better handle `&shell` issue on fish
- Fixed a bug of fzf-tmux where invalid output is generated
- Fixed fzf-tmux to work even when `tput` does not work
0.17.0
------
- Performance optimization

View File

@@ -16,8 +16,8 @@ skip=""
swap=""
close=""
term=""
[[ -n "$LINES" ]] && lines=$LINES || lines=$(tput lines)
[[ -n "$COLUMNS" ]] && columns=$COLUMNS || columns=$(tput cols)
[[ -n "$LINES" ]] && lines=$LINES || lines=$(tput lines) || lines=$(tmux display-message -p "#{pane_height}")
[[ -n "$COLUMNS" ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p "#{pane_width}")
help() {
>&2 echo 'usage: fzf-tmux [-u|-d [HEIGHT[%]]] [-l|-r [WIDTH[%]]] [--] [FZF OPTIONS]
@@ -176,7 +176,6 @@ close="; trap - EXIT SIGINT SIGTERM $close"
if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<< "\"$fzf\" $opts > $fifo2; echo \$? > $fifo3 $close" >> $argsf
cat $argsf
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux set-window-option synchronize-panes off \;\
set-window-option remain-on-exit off \;\
split-window $opt "cd $(printf %q "$PWD");$envs bash $argsf" $swap \

52
install
View File

@@ -8,6 +8,7 @@ key_bindings=
update_config=2
binary_arch=
allow_legacy=
shells="bash zsh fish"
help() {
cat << EOF
@@ -21,6 +22,10 @@ usage: $0 [OPTIONS]
--[no-]completion Enable/disable fuzzy completion (bash & zsh)
--[no-]update-rc Whether or not to update shell configuration files
--no-bash Do not set up bash configuration
--no-zsh Do not set up zsh configuration
--no-fish Do not set up fish configuration
--32 Download 32-bit binary
--64 Download 64-bit binary
EOF
@@ -47,6 +52,9 @@ for opt in "$@"; do
--32) binary_arch=386 ;;
--64) binary_arch=amd64 ;;
--bin) ;;
--no-bash) shells=${shells/bash/} ;;
--no-zsh) shells=${shells/zsh/} ;;
--no-fish) shells=${shells/fish/} ;;
*)
echo "unknown option: $opt"
help
@@ -59,14 +67,14 @@ cd "$(dirname "${BASH_SOURCE[0]}")"
fzf_base="$(pwd)"
ask() {
# If stdin is a tty, we are "interactive".
# non-interactive shell: wait for a linefeed
# interactive shell: continue after a single keypress
read_n=$([ -t 0 ] && echo "-n 1")
read -p "$1 ([y]/n) " $read_n -r
echo
[[ $REPLY =~ ^[Nn]$ ]]
while true; do
read -p "$1 ([y]/n) " -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
return 1
elif [[ $REPLY =~ ^[Nn]$ ]]; then
return 0
fi
done
}
check_binary() {
@@ -204,6 +212,17 @@ fi
[[ "$*" =~ "--bin" ]] && exit 0
for s in $shells; do
if ! command -v "$s" > /dev/null; then
shells=${shells/$s/}
fi
done
if [[ ${#shells} -lt 3 ]]; then
echo "No shell configuration to be updated."
exit 0
fi
# Auto-completion
if [ -z "$auto_completion" ]; then
ask "Do you want to enable fuzzy auto-completion?"
@@ -217,9 +236,8 @@ if [ -z "$key_bindings" ]; then
fi
echo
has_zsh=$(command -v zsh > /dev/null && echo 1 || echo 0)
shells=$([ $has_zsh -eq 1 ] && echo "bash zsh" || echo "bash")
for shell in $shells; do
[[ "$shell" = fish ]] && continue
echo -n "Generate ~/.fzf.$shell ... "
src=~/.fzf.${shell}
@@ -253,11 +271,10 @@ EOF
done
# fish
has_fish=$(command -v fish > /dev/null && echo 1 || echo 0)
if [ $has_fish -eq 1 ]; then
if [[ "$shells" =~ fish ]]; then
echo -n "Update fish_user_paths ... "
fish << EOF
echo \$fish_user_paths | grep $fzf_base/bin > /dev/null
echo \$fish_user_paths | \grep $fzf_base/bin > /dev/null
or set --universal fish_user_paths \$fish_user_paths $fzf_base/bin
EOF
[ $? -eq 0 ] && echo "OK" || echo "Failed"
@@ -330,11 +347,12 @@ if [ $update_config -eq 2 ]; then
fi
echo
for shell in $shells; do
[[ "$shell" = fish ]] && continue
[ $shell = zsh ] && dest=${ZDOTDIR:-~}/.zshrc || dest=~/.bashrc
append_line $update_config "[ -f ~/.fzf.${shell} ] && source ~/.fzf.${shell}" "$dest" "~/.fzf.${shell}"
done
if [ $key_bindings -eq 1 ] && [ $has_fish -eq 1 ]; then
if [ $key_bindings -eq 1 ] && [[ "$shells" =~ fish ]]; then
bind_file=~/.config/fish/functions/fish_user_key_bindings.fish
if [ ! -e "$bind_file" ]; then
create_file "$bind_file" \
@@ -348,9 +366,9 @@ fi
if [ $update_config -eq 1 ]; then
echo 'Finished. Restart your shell or reload config file.'
echo ' source ~/.bashrc # bash'
[ $has_zsh -eq 1 ] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh"
[ $has_fish -eq 1 ] && [ $key_bindings -eq 1 ] && echo ' fzf_key_bindings # fish'
[[ "$shells" =~ bash ]] && echo ' source ~/.bashrc # bash'
[[ "$shells" =~ zsh ]] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh"
[[ "$shells" =~ fish ]] && [ $key_bindings -eq 1 ] && echo ' fzf_key_bindings # fish'
echo
echo 'Use uninstall script to remove fzf.'
echo

View File

@@ -35,6 +35,8 @@ else
let s:base_dir = expand('<sfile>:h:h')
endif
if s:is_win
let s:term_marker = '&::FZF'
function! s:fzf_call(fn, ...)
let shellslash = &shellslash
try
@@ -53,6 +55,8 @@ if s:is_win
\ ['chcp %origchcp% > nul']
endfunction
else
let s:term_marker = ";#FZF"
function! s:fzf_call(fn, ...)
return call(a:fn, a:000)
endfunction
@@ -328,17 +332,20 @@ function! fzf#wrap(...)
return opts
endfunction
function! fzf#run(...) abort
try
let oshell = &shell
let useshellslash = &shellslash
function! s:use_sh()
let [shell, shellslash] = [&shell, &shellslash]
if s:is_win
set shell=cmd.exe
set noshellslash
else
set shell=sh
endif
return [shell, shellslash]
endfunction
function! fzf#run(...) abort
try
let [shell, shellslash] = s:use_sh()
let dict = exists('a:1') ? s:upgrade(a:1) : {}
let temps = { 'result': s:fzf_tempname() }
@@ -366,7 +373,7 @@ try
let source = dict.source
let type = type(source)
if type == 1
let prefix = source.'|'
let prefix = '( '.source.' )|'
elseif type == 3
let temps.input = s:fzf_tempname()
call writefile(source, temps.input)
@@ -382,7 +389,7 @@ try
let use_height = has_key(dict, 'down') &&
\ !(has('nvim') || s:is_win || has('win32unix') || s:present(dict, 'up', 'left', 'right')) &&
\ executable('tput') && filereadable('/dev/tty')
let use_term = has('nvim') && !s:is_win
let use_term = has('nvim-0.2.1') || (has('nvim') && !s:is_win) || (has('terminal') && has('patch-8.0.995') && (has('gui_running') || s:is_win))
let use_tmux = (!use_height && !use_term || prefer_tmux) && !has('win32unix') && s:tmux_enabled() && s:splittable(dict)
if prefer_tmux && use_tmux
let use_height = 0
@@ -393,6 +400,9 @@ try
let optstr .= ' --height='.height
elseif use_term
let optstr .= ' --no-height'
if !has('nvim') && !s:is_win
let optstr .= ' --bind ctrl-j:accept'
endif
endif
let command = prefix.(use_tmux ? s:fzf_tmux(dict) : fzf_exec).' '.optstr.' > '.temps.result
@@ -405,8 +415,7 @@ try
call s:callback(dict, lines)
return lines
finally
let &shell = oshell
let &shellslash = useshellslash
let [&shell, &shellslash] = [shell, shellslash]
endtry
endfunction
@@ -625,6 +634,7 @@ function! s:execute_term(dict, command, temps) abort
let winrest = winrestcmd()
let pbuf = bufnr('')
let [ppos, winopts] = s:split(a:dict)
call s:use_sh()
let b:fzf = a:dict
let fzf = { 'buf': bufnr(''), 'pbuf': pbuf, 'ppos': ppos, 'dict': a:dict, 'temps': a:temps,
\ 'winopts': winopts, 'winrest': winrest, 'lines': &lines,
@@ -640,7 +650,7 @@ function! s:execute_term(dict, command, temps) abort
endif
endif
endfunction
function! fzf.on_exit(id, code, _event)
function! fzf.on_exit(id, code, ...)
if s:getpos() == self.ppos " {'window': 'enew'}
for [opt, val] in items(self.winopts)
execute 'let' opt '=' val
@@ -678,7 +688,19 @@ function! s:execute_term(dict, command, temps) abort
if s:present(a:dict, 'dir')
execute 'lcd' s:escape(a:dict.dir)
endif
call termopen(a:command . ';#FZF', fzf)
if s:is_win
let fzf.temps.batchfile = s:fzf_tempname().'.bat'
call writefile(s:wrap_cmds(a:command), fzf.temps.batchfile)
let command = fzf.temps.batchfile
else
let command = a:command
endif
let command .= s:term_marker
if has('nvim')
call termopen(command, fzf)
else
call term_start([&shell, &shellcmdflag, command], {'curwin': fzf.buf, 'exit_cb': function(fzf.on_exit)})
endif
finally
if s:present(a:dict, 'dir')
lcd -

View File

@@ -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}
}