mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-06-13 20:52:21 +00:00
d274d093af
See https://github.com/junegunn/fzf/discussions/3792 This allows us to separately capture the standard error from fzf and its child processes, and there's less chance of user errors of redirecting the error stream and hiding fzf.
159 lines
4.8 KiB
Go
159 lines
4.8 KiB
Go
//go:build windows
|
|
|
|
package tui
|
|
|
|
import (
|
|
"os"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/junegunn/fzf/src/util"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
const (
|
|
timeoutInterval = 10
|
|
)
|
|
|
|
var (
|
|
consoleFlagsInput = uint32(windows.ENABLE_VIRTUAL_TERMINAL_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_EXTENDED_FLAGS)
|
|
consoleFlagsOutput = uint32(windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | windows.ENABLE_PROCESSED_OUTPUT | windows.DISABLE_NEWLINE_AUTO_RETURN)
|
|
)
|
|
|
|
// IsLightRendererSupported checks to see if the Light renderer is supported
|
|
func IsLightRendererSupported() bool {
|
|
var oldState uint32
|
|
// enable vt100 emulation (https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences)
|
|
if windows.GetConsoleMode(windows.Stderr, &oldState) != nil {
|
|
return false
|
|
}
|
|
// attempt to set mode to determine if we support VT 100 codes. This will work on newer Windows 10
|
|
// version:
|
|
canSetVt100 := windows.SetConsoleMode(windows.Stderr, oldState|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) == nil
|
|
var checkState uint32
|
|
if windows.GetConsoleMode(windows.Stderr, &checkState) != nil ||
|
|
(checkState&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) != windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING {
|
|
return false
|
|
}
|
|
windows.SetConsoleMode(windows.Stderr, oldState)
|
|
return canSetVt100
|
|
}
|
|
|
|
func (r *LightRenderer) defaultTheme() *ColorTheme {
|
|
// the getenv check is borrowed from here: https://github.com/gdamore/tcell/commit/0c473b86d82f68226a142e96cc5a34c5a29b3690#diff-b008fcd5e6934bf31bc3d33bf49f47d8R178:
|
|
if !IsLightRendererSupported() || os.Getenv("ConEmuPID") != "" || os.Getenv("TCELL_TRUECOLOR") == "disable" {
|
|
return Default16
|
|
}
|
|
return Dark256
|
|
}
|
|
|
|
func (r *LightRenderer) initPlatform() error {
|
|
//outHandle := windows.Stdout
|
|
outHandle, _ := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
|
|
// enable vt100 emulation (https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences)
|
|
if err := windows.GetConsoleMode(windows.Handle(outHandle), &r.origStateOutput); err != nil {
|
|
return err
|
|
}
|
|
r.outHandle = uintptr(outHandle)
|
|
inHandle, _ := syscall.Open("CONIN$", syscall.O_RDWR, 0)
|
|
if err := windows.GetConsoleMode(windows.Handle(inHandle), &r.origStateInput); err != nil {
|
|
return err
|
|
}
|
|
r.inHandle = uintptr(inHandle)
|
|
|
|
r.setupTerminal()
|
|
|
|
// channel for non-blocking reads. Buffer to make sure
|
|
// we get the ESC sets:
|
|
r.ttyinChannel = make(chan byte, 1024)
|
|
|
|
// the following allows for non-blocking IO.
|
|
// syscall.SetNonblock() is a NOOP under Windows.
|
|
go func() {
|
|
fd := int(r.inHandle)
|
|
b := make([]byte, 1)
|
|
for !r.closed.Get() {
|
|
// HACK: if run from PSReadline, something resets ConsoleMode to remove ENABLE_VIRTUAL_TERMINAL_INPUT.
|
|
_ = windows.SetConsoleMode(windows.Handle(r.inHandle), consoleFlagsInput)
|
|
|
|
_, err := util.Read(fd, b)
|
|
if err == nil {
|
|
r.ttyinChannel <- b[0]
|
|
}
|
|
}
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *LightRenderer) closePlatform() {
|
|
windows.SetConsoleMode(windows.Handle(r.outHandle), r.origStateOutput)
|
|
windows.SetConsoleMode(windows.Handle(r.inHandle), r.origStateInput)
|
|
}
|
|
|
|
func openTtyIn() (*os.File, error) {
|
|
// not used
|
|
return nil, nil
|
|
}
|
|
|
|
func openTtyOut() (*os.File, error) {
|
|
// not used
|
|
return nil, nil
|
|
}
|
|
|
|
func (r *LightRenderer) setupTerminal() error {
|
|
if err := windows.SetConsoleMode(windows.Handle(r.outHandle), consoleFlagsOutput); err != nil {
|
|
return err
|
|
}
|
|
return windows.SetConsoleMode(windows.Handle(r.inHandle), consoleFlagsInput)
|
|
}
|
|
|
|
func (r *LightRenderer) restoreTerminal() error {
|
|
if err := windows.SetConsoleMode(windows.Handle(r.inHandle), r.origStateInput); err != nil {
|
|
return err
|
|
}
|
|
return windows.SetConsoleMode(windows.Handle(r.outHandle), r.origStateOutput)
|
|
}
|
|
|
|
func (r *LightRenderer) Size() TermSize {
|
|
var w, h int
|
|
var bufferInfo windows.ConsoleScreenBufferInfo
|
|
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(r.outHandle), &bufferInfo); err != nil {
|
|
w = getEnv("COLUMNS", defaultWidth)
|
|
h = r.maxHeightFunc(getEnv("LINES", defaultHeight))
|
|
|
|
} else {
|
|
w = int(bufferInfo.Window.Right - bufferInfo.Window.Left)
|
|
h = r.maxHeightFunc(int(bufferInfo.Window.Bottom - bufferInfo.Window.Top))
|
|
}
|
|
return TermSize{h, w, 0, 0}
|
|
}
|
|
|
|
func (r *LightRenderer) updateTerminalSize() {
|
|
size := r.Size()
|
|
r.width = size.Columns
|
|
r.height = size.Lines
|
|
}
|
|
|
|
func (r *LightRenderer) findOffset() (row int, col int) {
|
|
var bufferInfo windows.ConsoleScreenBufferInfo
|
|
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(r.outHandle), &bufferInfo); err != nil {
|
|
return -1, -1
|
|
}
|
|
return int(bufferInfo.CursorPosition.X), int(bufferInfo.CursorPosition.Y)
|
|
}
|
|
|
|
func (r *LightRenderer) getch(nonblock bool) (int, bool) {
|
|
if nonblock {
|
|
select {
|
|
case bc := <-r.ttyinChannel:
|
|
return int(bc), true
|
|
case <-time.After(timeoutInterval * time.Millisecond):
|
|
return 0, false
|
|
}
|
|
} else {
|
|
bc := <-r.ttyinChannel
|
|
return int(bc), true
|
|
}
|
|
}
|