forked from TrueCloudLab/restic
Previously the progress bar / status update interval used stdoutIsTerminal to determine whether it is possible to update the progress bar or not. However, its implementation differed from the detection within the backup command which included additional checks to detect the presence of mintty on Windows. mintty behaves like a terminal but uses pipes for communication. This adds stdoutCanUpdateStatus() which calls the same terminal detection code used by backup. This ensures that all commands consistently switch between interactive and non-interactive terminal mode. stdoutIsTerminal() now also returns true whenever stdoutCanUpdateStatus() does so. This is required to properly handle the special case of mintty.
98 lines
2.9 KiB
Go
98 lines
2.9 KiB
Go
// +build windows
|
|
|
|
package termstatus
|
|
|
|
import (
|
|
"io"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"golang.org/x/crypto/ssh/terminal"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
// clearCurrentLine removes all characters from the current line and resets the
|
|
// cursor position to the first column.
|
|
func clearCurrentLine(wr io.Writer, fd uintptr) func(io.Writer, uintptr) {
|
|
// easy case, the terminal is cmd or psh, without redirection
|
|
if isWindowsTerminal(fd) {
|
|
return windowsClearCurrentLine
|
|
}
|
|
|
|
// assume we're running in mintty/cygwin
|
|
return posixClearCurrentLine
|
|
}
|
|
|
|
// moveCursorUp moves the cursor to the line n lines above the current one.
|
|
func moveCursorUp(wr io.Writer, fd uintptr) func(io.Writer, uintptr, int) {
|
|
// easy case, the terminal is cmd or psh, without redirection
|
|
if isWindowsTerminal(fd) {
|
|
return windowsMoveCursorUp
|
|
}
|
|
|
|
// assume we're running in mintty/cygwin
|
|
return posixMoveCursorUp
|
|
}
|
|
|
|
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
|
|
var (
|
|
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
|
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
|
|
)
|
|
|
|
// windowsClearCurrentLine removes all characters from the current line and
|
|
// resets the cursor position to the first column.
|
|
func windowsClearCurrentLine(wr io.Writer, fd uintptr) {
|
|
var info windows.ConsoleScreenBufferInfo
|
|
windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info)
|
|
|
|
// clear the line
|
|
cursor := windows.Coord{
|
|
X: info.Window.Left,
|
|
Y: info.CursorPosition.Y,
|
|
}
|
|
var count, w uint32
|
|
count = uint32(info.Size.X)
|
|
procFillConsoleOutputAttribute.Call(fd, uintptr(info.Attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w)))
|
|
procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w)))
|
|
}
|
|
|
|
// windowsMoveCursorUp moves the cursor to the line n lines above the current one.
|
|
func windowsMoveCursorUp(wr io.Writer, fd uintptr, n int) {
|
|
var info windows.ConsoleScreenBufferInfo
|
|
windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info)
|
|
|
|
// move cursor up by n lines and to the first column
|
|
windows.SetConsoleCursorPosition(windows.Handle(fd), windows.Coord{
|
|
X: 0,
|
|
Y: info.CursorPosition.Y - int16(n),
|
|
})
|
|
}
|
|
|
|
// isWindowsTerminal return true if the file descriptor is a windows terminal (cmd, psh).
|
|
func isWindowsTerminal(fd uintptr) bool {
|
|
return terminal.IsTerminal(int(fd))
|
|
}
|
|
|
|
func isPipe(fd uintptr) bool {
|
|
typ, err := windows.GetFileType(windows.Handle(fd))
|
|
return err == nil && typ == windows.FILE_TYPE_PIPE
|
|
}
|
|
|
|
// CanUpdateStatus returns true if status lines can be printed, the process
|
|
// output is not redirected to a file or pipe.
|
|
func CanUpdateStatus(fd uintptr) bool {
|
|
// easy case, the terminal is cmd or psh, without redirection
|
|
if isWindowsTerminal(fd) {
|
|
return true
|
|
}
|
|
|
|
// check that the output file type is a pipe (0x0003)
|
|
if !isPipe(fd) {
|
|
return false
|
|
}
|
|
|
|
// assume we're running in mintty/cygwin
|
|
return true
|
|
}
|