termenv_windows.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. //go:build windows
  2. // +build windows
  3. package termenv
  4. import (
  5. "fmt"
  6. "strconv"
  7. "golang.org/x/sys/windows"
  8. )
  9. func (o *Output) ColorProfile() Profile {
  10. if !o.isTTY() {
  11. return Ascii
  12. }
  13. if o.environ.Getenv("ConEmuANSI") == "ON" {
  14. return TrueColor
  15. }
  16. winVersion, _, buildNumber := windows.RtlGetNtVersionNumbers()
  17. if buildNumber < 10586 || winVersion < 10 {
  18. // No ANSI support before Windows 10 build 10586.
  19. if o.environ.Getenv("ANSICON") != "" {
  20. conVersion := o.environ.Getenv("ANSICON_VER")
  21. cv, err := strconv.ParseInt(conVersion, 10, 64)
  22. if err != nil || cv < 181 {
  23. // No 8 bit color support before v1.81 release.
  24. return ANSI
  25. }
  26. return ANSI256
  27. }
  28. return Ascii
  29. }
  30. if buildNumber < 14931 {
  31. // No true color support before build 14931.
  32. return ANSI256
  33. }
  34. return TrueColor
  35. }
  36. func (o Output) foregroundColor() Color {
  37. // default gray
  38. return ANSIColor(7)
  39. }
  40. func (o Output) backgroundColor() Color {
  41. // default black
  42. return ANSIColor(0)
  43. }
  44. // EnableWindowsANSIConsole enables virtual terminal processing on Windows
  45. // platforms. This allows the use of ANSI escape sequences in Windows console
  46. // applications. Ensure this gets called before anything gets rendered with
  47. // termenv.
  48. //
  49. // Returns the original console mode and an error if one occurred.
  50. func EnableWindowsANSIConsole() (uint32, error) {
  51. handle, err := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
  52. if err != nil {
  53. return 0, err
  54. }
  55. var mode uint32
  56. err = windows.GetConsoleMode(handle, &mode)
  57. if err != nil {
  58. return 0, err
  59. }
  60. // See https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
  61. if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING {
  62. vtpmode := mode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
  63. if err := windows.SetConsoleMode(handle, vtpmode); err != nil {
  64. return 0, err
  65. }
  66. }
  67. return mode, nil
  68. }
  69. // RestoreWindowsConsole restores the console mode to a previous state.
  70. func RestoreWindowsConsole(mode uint32) error {
  71. handle, err := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
  72. if err != nil {
  73. return err
  74. }
  75. return windows.SetConsoleMode(handle, mode)
  76. }
  77. // EnableVirtualTerminalProcessing enables virtual terminal processing on
  78. // Windows for o and returns a function that restores o to its previous state.
  79. // On non-Windows platforms, or if o does not refer to a terminal, then it
  80. // returns a non-nil no-op function and no error.
  81. func EnableVirtualTerminalProcessing(o *Output) (restoreFunc func() error, err error) {
  82. // There is nothing to restore until we set the console mode.
  83. restoreFunc = func() error {
  84. return nil
  85. }
  86. // If o is not a tty, then there is nothing to do.
  87. tty := o.TTY()
  88. if tty == nil {
  89. return
  90. }
  91. // Get the current console mode. If there is an error, assume that o is not
  92. // a terminal, discard the error, and return.
  93. var mode uint32
  94. if err2 := windows.GetConsoleMode(windows.Handle(tty.Fd()), &mode); err2 != nil {
  95. return
  96. }
  97. // If virtual terminal processing is already set, then there is nothing to
  98. // do and nothing to restore.
  99. if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING == windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING {
  100. return
  101. }
  102. // Enable virtual terminal processing. See
  103. // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
  104. if err2 := windows.SetConsoleMode(windows.Handle(tty.Fd()), mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err2 != nil {
  105. err = fmt.Errorf("windows.SetConsoleMode: %w", err2)
  106. return
  107. }
  108. // Set the restore function. We maintain a reference to the tty in the
  109. // closure (rather than just its handle) to ensure that the tty is not
  110. // closed by a finalizer.
  111. restoreFunc = func() error {
  112. return windows.SetConsoleMode(windows.Handle(tty.Fd()), mode)
  113. }
  114. return
  115. }