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