terminal_windows.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // Based on ssh/terminal:
  2. // Copyright 2011 The Go Authors. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. // +build windows,!appengine
  6. package logrus
  7. import (
  8. "bytes"
  9. "errors"
  10. "io"
  11. "os"
  12. "os/exec"
  13. "strconv"
  14. "strings"
  15. "syscall"
  16. "unsafe"
  17. )
  18. var kernel32 = syscall.NewLazyDLL("kernel32.dll")
  19. var (
  20. procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
  21. procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
  22. )
  23. const (
  24. enableProcessedOutput = 0x0001
  25. enableWrapAtEolOutput = 0x0002
  26. enableVirtualTerminalProcessing = 0x0004
  27. )
  28. func getVersion() (float64, error) {
  29. stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
  30. cmd := exec.Command("cmd", "ver")
  31. cmd.Stdout = stdout
  32. cmd.Stderr = stderr
  33. err := cmd.Run()
  34. if err != nil {
  35. return -1, err
  36. }
  37. // The output should be like "Microsoft Windows [Version XX.X.XXXXXX]"
  38. version := strings.Replace(stdout.String(), "\n", "", -1)
  39. version = strings.Replace(version, "\r\n", "", -1)
  40. x1 := strings.Index(version, "[Version")
  41. if x1 == -1 || strings.Index(version, "]") == -1 {
  42. return -1, errors.New("Can't determine Windows version")
  43. }
  44. return strconv.ParseFloat(version[x1+9:x1+13], 64)
  45. }
  46. func init() {
  47. ver, err := getVersion()
  48. if err != nil {
  49. return
  50. }
  51. // Activate Virtual Processing for Windows CMD
  52. // Info: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
  53. if ver >= 10 {
  54. handle := syscall.Handle(os.Stderr.Fd())
  55. procSetConsoleMode.Call(uintptr(handle), enableProcessedOutput|enableWrapAtEolOutput|enableVirtualTerminalProcessing)
  56. }
  57. }
  58. // IsTerminal returns true if stderr's file descriptor is a terminal.
  59. func IsTerminal(f io.Writer) bool {
  60. switch v := f.(type) {
  61. case *os.File:
  62. var st uint32
  63. r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
  64. return r != 0 && e == 0
  65. default:
  66. return false
  67. }
  68. }