1// +build windows 2 3// Display color on windows 4// refer: 5// golang.org/x/sys/windows 6// golang.org/x/crypto/ssh/terminal 7// https://docs.microsoft.com/en-us/windows/console 8package color 9 10import ( 11 "fmt" 12 "syscall" 13 "unsafe" 14) 15 16// color on windows cmd 17// you can see on windows by command: COLOR /? 18// windows color build by: "Bg + Fg" OR only "Fg" 19// Consists of any two of the following: 20// the first is the background color, and the second is the foreground color 21// 颜色属性由两个十六进制数字指定 22// - 第一个对应于背景,第二个对应于前景。 23// - 当只传入一个值时,则认为是前景色 24// 每个数字可以为以下任何值: 25// more see: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/cmd 26const ( 27 // Foreground colors. 28 winFgBlack uint16 = 0x00 // 0 黑色 29 winFgBlue uint16 = 0x01 // 1 蓝色 30 winFgGreen uint16 = 0x02 // 2 绿色 31 winFgAqua uint16 = 0x03 // 3 浅绿 skyblue 32 winFgRed uint16 = 0x04 // 4 红色 33 winFgPink uint16 = 0x05 // 5 紫色/品红 34 winFgYellow uint16 = 0x06 // 6 黄色 35 winFgWhite uint16 = 0x07 // 7 白色 36 winFgGray uint16 = 0x08 // 8 灰色 37 38 winFgLightBlue uint16 = 0x09 // 9 淡蓝色 39 winFgLightGreen uint16 = 0x0a // 10 淡绿色 40 winFgLightAqua uint16 = 0x0b // 11 淡浅绿色 41 winFgLightRed uint16 = 0x0c // 12 淡红色 42 winFgLightPink uint16 = 0x0d // 13 Purple 淡紫色, Pink 粉红 43 winFgLightYellow uint16 = 0x0e // 14 淡黄色 44 winFgLightWhite uint16 = 0x0f // 15 亮白色 45 46 // Background colors. 47 winBgBlack uint16 = 0x00 // 黑色 48 winBgBlue uint16 = 0x10 // 蓝色 49 winBgGreen uint16 = 0x20 // 绿色 50 winBgAqua uint16 = 0x30 // 浅绿 skyblue 51 winBgRed uint16 = 0x40 // 红色 52 winBgPink uint16 = 0x50 // 紫色 53 winBgYellow uint16 = 0x60 // 黄色 54 winBgWhite uint16 = 0x70 // 白色 55 winBgGray uint16 = 0x80 // 128 灰色 56 57 winBgLightBlue uint16 = 0x90 // 淡蓝色 58 winBgLightGreen uint16 = 0xa0 // 淡绿色 59 winBgLightAqua uint16 = 0xb0 // 淡浅绿色 60 winBgLightRed uint16 = 0xc0 // 淡红色 61 winBgLightPink uint16 = 0xd0 // 淡紫色 62 winBgLightYellow uint16 = 0xe0 // 淡黄色 63 winBgLightWhite uint16 = 0xf0 // 240 亮白色 64 65 // bg black, fg white 66 winDefSetting = winBgBlack | winFgWhite 67 68 // Option settings 69 // see https://docs.microsoft.com/en-us/windows/console/char-info-str 70 winFgIntensity uint16 = 0x0008 // 8 前景强度 71 winBgIntensity uint16 = 0x0080 // 128 背景强度 72 73 WinOpLeading uint16 = 0x0100 // 前导字节 74 WinOpTrailing uint16 = 0x0200 // 尾随字节 75 WinOpHorizontal uint16 = 0x0400 // 顶部水平 76 WinOpReverse uint16 = 0x4000 // 反转前景和背景 77 WinOpUnderscore uint16 = 0x8000 // 32768 下划线 78) 79 80// color on windows 81var winColorsMap map[Color]uint16 82 83var ( 84 // for cmd.exe 85 // echo %ESC%[1;33;40m Yellow on black %ESC%[0m 86 escChar = "" 87 // isMSys bool 88 kernel32 *syscall.LazyDLL 89 90 procGetConsoleMode *syscall.LazyProc 91 // procSetConsoleMode *syscall.LazyProc 92 93 procSetTextAttribute *syscall.LazyProc 94 procGetConsoleScreenBufferInfo *syscall.LazyProc 95 96 // console screen buffer info 97 // eg {size:{x:215 y:3000} cursorPosition:{x:0 y:893} attributes:7 window:{left:0 top:882 right:214 bottom:893} maximumWindowSize:{x:215 y:170}} 98 defScreenInfo consoleScreenBufferInfo 99) 100 101func init() { 102 // if at linux, mac, or windows's ConEmu, Cmder, putty 103 if isSupportColor { 104 return 105 } 106 107 // init some info 108 isLikeInCmd = true 109 initWinColorsMap() 110 111 // isMSys = utils.IsMSys() 112 kernel32 = syscall.NewLazyDLL("kernel32.dll") 113 114 // https://docs.microsoft.com/en-us/windows/console/setconsolemode 115 procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 116 // procSetConsoleMode = kernel32.NewProc("SetConsoleMode") 117 118 procSetTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") 119 // https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo 120 procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 121 122 // fetch console screen buffer info 123 getConsoleScreenBufferInfo(uintptr(syscall.Stdout), &defScreenInfo) 124} 125 126// initWinColorsMap init colors to win-colors mapping 127func initWinColorsMap() { 128 // init map 129 winColorsMap = map[Color]uint16{ 130 // Foreground colors 131 FgBlack: winFgBlack, 132 FgRed: winFgRed, 133 FgGreen: winFgGreen, 134 FgYellow: winFgYellow, 135 FgBlue: winFgBlue, 136 FgMagenta: winFgPink, // diff 137 FgCyan: winFgAqua, // diff 138 FgWhite: winFgWhite, 139 FgDefault: winFgWhite, 140 141 // Extra Foreground colors 142 FgDarkGray: winFgGray, 143 FgLightRed: winFgLightBlue, 144 FgLightGreen: winFgLightGreen, 145 FgLightYellow: winFgLightYellow, 146 FgLightBlue: winFgLightRed, 147 FgLightMagenta: winFgLightPink, 148 FgLightCyan: winFgLightAqua, 149 FgLightWhite: winFgLightWhite, 150 151 // Background colors 152 BgBlack: winBgBlack, 153 BgRed: winBgRed, 154 BgGreen: winBgGreen, 155 BgYellow: winBgYellow, 156 BgBlue: winBgBlue, 157 BgMagenta: winBgPink, // diff 158 BgCyan: winBgAqua, // diff 159 BgWhite: winBgWhite, 160 BgDefault: winBgBlack, 161 162 // Extra Background colors 163 BgDarkGray: winBgGray, 164 BgLightRed: winBgLightBlue, 165 BgLightGreen: winBgLightGreen, 166 BgLightYellow: winBgLightYellow, 167 BgLightBlue: winBgLightRed, 168 BgLightMagenta: winBgLightPink, 169 BgLightCyan: winBgLightAqua, 170 BgLightWhite: winBgLightWhite, 171 172 // Option settings(注释掉的,将在win cmd中忽略掉) 173 // OpReset: winDefSetting, // 重置所有设置 174 OpBold: winFgIntensity, // 加粗 -> 175 // OpFuzzy: // 模糊(不是所有的终端仿真器都支持) 176 // OpItalic // 斜体(不是所有的终端仿真器都支持) 177 OpUnderscore: WinOpUnderscore, // 下划线 178 // OpBlink // 闪烁 179 // OpFastBlink // 快速闪烁(未广泛支持) 180 // OpReverse: WinOpReverse // 颠倒的 交换背景色与前景色 181 // OpConcealed // 隐匿的 182 // OpStrikethrough // 删除的,删除线(未广泛支持) 183 } 184} 185 186// winPrint 187func winPrint(str string, colors ...Color) { 188 winInternalPrint(str, convertColorsToWinAttr(colors), false) 189} 190 191// winPrintln 192func winPrintln(str string, colors ...Color) { 193 winInternalPrint(str, convertColorsToWinAttr(colors), true) 194} 195 196// winInternalPrint 197// winInternalPrint("hello [OK];", 2|8, true) //亮绿色 198func winInternalPrint(str string, attribute uint16, newline bool) (int, error) { 199 if !Enable { // not enable 200 if newline { 201 return fmt.Println(str) 202 } 203 204 return fmt.Print(str) 205 } 206 207 // fmt.Print("attribute val: ", attribute, "\n") 208 setConsoleTextAttr(uintptr(syscall.Stdout), attribute) 209 210 if newline { 211 fmt.Println(str) 212 } else { 213 fmt.Print(str) 214 } 215 216 // handle, _, _ = procSetTextAttribute.Call(uintptr(syscall.Stdout), winDefSetting) 217 // closeHandle := kernel32.NewProc("CloseHandle") 218 // closeHandle.Call(handle) 219 220 return winReset() 221} 222 223// func winRender(str string, colors ...Color) string { 224// setConsoleTextAttr(uintptr(syscall.Stdout), convertColorsToWinAttr(colors)) 225// 226// return str 227// } 228 229// winSet set console color attributes 230func winSet(colors ...Color) (int, error) { 231 if !Enable { // not enable 232 return 0, nil 233 } 234 235 return setConsoleTextAttr(uintptr(syscall.Stdout), convertColorsToWinAttr(colors)) 236} 237 238// winReset reset color settings to default 239func winReset() (int, error) { 240 // not enable 241 if !Enable { 242 return 0, nil 243 } 244 245 return setConsoleTextAttr(uintptr(syscall.Stdout), winDefSetting) 246} 247 248// convertColorsToWinAttr convert generic colors to win-colors attribute 249func convertColorsToWinAttr(colors []Color) uint16 { 250 var setting uint16 251 for _, c := range colors { 252 // check exists 253 if wc, ok := winColorsMap[c]; ok { 254 setting |= wc 255 } 256 } 257 258 return setting 259} 260 261// getWinColor convert Color to win-color value 262func getWinColor(color Color) uint16 { 263 if wc, ok := winColorsMap[color]; ok { 264 return wc 265 } 266 267 return 0 268} 269 270// setConsoleTextAttr 271// ret != 0 is OK. 272func setConsoleTextAttr(consoleOutput uintptr, winAttr uint16) (n int, err error) { 273 ret, _, err := procSetTextAttribute.Call(consoleOutput, uintptr(winAttr)) 274 275 return int(ret), err 276} 277 278// IsTty returns true if the given file descriptor is a terminal. 279func IsTty(fd uintptr) bool { 280 var st uint32 281 r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) 282 return r != 0 && e == 0 283} 284 285// IsTerminal returns true if the given file descriptor is a terminal. 286// Usage: 287// fd := os.Stdout.Fd() 288// fd := uintptr(syscall.Stdout) // for windows 289// IsTerminal(fd) 290func IsTerminal(fd int) bool { 291 var st uint32 292 r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) 293 return r != 0 && e == 0 294} 295 296// from package: golang.org/x/sys/windows 297type ( 298 short int16 299 word uint16 300 301 // coord cursor position coordinates 302 coord struct { 303 x short 304 y short 305 } 306 307 smallRect struct { 308 left short 309 top short 310 right short 311 bottom short 312 } 313 314 // Used with GetConsoleScreenBuffer to retrieve information about a console 315 // screen buffer. See 316 // https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str 317 // for details. 318 consoleScreenBufferInfo struct { 319 size coord 320 cursorPosition coord 321 attributes word // is windows color setting 322 window smallRect 323 maximumWindowSize coord 324 } 325) 326 327// GetSize returns the dimensions of the given terminal. 328func getSize(fd int) (width, height int, err error) { 329 var info consoleScreenBufferInfo 330 if err := getConsoleScreenBufferInfo(uintptr(fd), &info); err != nil { 331 return 0, 0, err 332 } 333 334 return int(info.size.x), int(info.size.y), nil 335} 336 337// from package: golang.org/x/sys/windows 338func getConsoleScreenBufferInfo(consoleOutput uintptr, info *consoleScreenBufferInfo) (err error) { 339 r1, _, e1 := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, consoleOutput, uintptr(unsafe.Pointer(info)), 0) 340 if r1 == 0 { 341 if e1 != 0 { 342 err = e1 343 } else { 344 err = syscall.EINVAL 345 } 346 } 347 348 return 349} 350