├── .gitignore ├── LICENSE ├── README.md ├── ct.go ├── ct_ansi.go ├── ct_ansi_test.go ├── ct_test.go ├── ct_win.go ├── fmt └── ctfmt.go ├── go.mod └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | =========== 3 | 4 | Copyright (c) 2016, David Deng 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name of go-colortext nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | 33 | MIT License 34 | =========== 35 | 36 | Copyright (c) 2016 David Deng 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in all 46 | copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-colortext package [![GoSearch](http://go-search.org/badge?id=github.com%2Fdaviddengcn%2Fgo-colortext)](http://go-search.org/view?id=github.com%2Fdaviddengcn%2Fgo-colortext) 2 | ==================== 3 | 4 | This is a package to change the color of the text and background in the console, working both under Windows and other systems. 5 | 6 | Under Windows, the console APIs are used. Otherwise, ANSI texts are output. 7 | 8 | Docs: http://godoc.org/github.com/daviddengcn/go-colortext ([packages that import ct](http://go-search.org/view?id=github.com%2fdaviddengcn%2fgo-colortext)) 9 | 10 | Usage: 11 | ```go 12 | ct.Foreground(Green, false) 13 | fmt.Println("Green text starts here...") 14 | ct.ChangeColor(Red, true, White, false) 15 | fmt.Println(...) 16 | ct.ResetColor() 17 | ``` 18 | 19 | LICENSE 20 | ======= 21 | BSD/MIT license 22 | -------------------------------------------------------------------------------- /ct.go: -------------------------------------------------------------------------------- 1 | /* 2 | ct package provides functions to change the color of console text. 3 | 4 | Under windows platform, the Console API is used. Under other systems, ANSI text mode is used. 5 | */ 6 | package ct 7 | 8 | import ( 9 | "io" 10 | "os" 11 | ) 12 | 13 | // Color is the type of color to be set. 14 | type Color int 15 | 16 | const ( 17 | // No change of color 18 | None = Color(iota) 19 | Black 20 | Red 21 | Green 22 | Yellow 23 | Blue 24 | Magenta 25 | Cyan 26 | White 27 | ) 28 | 29 | // Writer is the io.Writer where ANSI escape codes will be written to 30 | var Writer io.Writer = os.Stdout 31 | 32 | // ResetColor resets the foreground and background to original colors 33 | func ResetColor() { 34 | resetColor() 35 | } 36 | 37 | // ChangeColor sets the foreground and background colors. If the value of the color is None, 38 | // the corresponding color keeps unchanged. 39 | // If fgBright or bgBright is set true, corresponding color use bright color. bgBright may be 40 | // ignored in some OS environment. 41 | func ChangeColor(fg Color, fgBright bool, bg Color, bgBright bool) { 42 | changeColor(fg, fgBright, bg, bgBright) 43 | } 44 | 45 | // Foreground changes the foreground color. 46 | func Foreground(cl Color, bright bool) { 47 | ChangeColor(cl, bright, None, false) 48 | } 49 | 50 | // Background changes the background color. 51 | func Background(cl Color, bright bool) { 52 | ChangeColor(None, false, cl, bright) 53 | } 54 | -------------------------------------------------------------------------------- /ct_ansi.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package ct 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "strconv" 9 | ) 10 | 11 | func isDumbTerm() bool { 12 | return os.Getenv("TERM") == "dumb" 13 | } 14 | 15 | func resetColor() { 16 | if isDumbTerm() { 17 | return 18 | } 19 | fmt.Fprint(Writer, "\x1b[0m") 20 | } 21 | 22 | func ansiText(fg Color, fgBright bool, bg Color, bgBright bool) string { 23 | if fg == None && bg == None { 24 | return "" 25 | } 26 | s := []byte("\x1b[0") 27 | if fg != None { 28 | s = strconv.AppendUint(append(s, ";"...), 30+(uint64)(fg-Black), 10) 29 | if fgBright { 30 | s = append(s, ";1"...) 31 | } 32 | } 33 | if bg != None { 34 | s = strconv.AppendUint(append(s, ";"...), 40+(uint64)(bg-Black), 10) 35 | if bgBright { 36 | s = append(s, ";1"...) 37 | } 38 | } 39 | s = append(s, "m"...) 40 | return string(s) 41 | } 42 | 43 | func changeColor(fg Color, fgBright bool, bg Color, bgBright bool) { 44 | if isDumbTerm() { 45 | return 46 | } 47 | if fg == None && bg == None { 48 | return 49 | } 50 | fmt.Fprint(Writer, ansiText(fg, fgBright, bg, bgBright)) 51 | } 52 | -------------------------------------------------------------------------------- /ct_ansi_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package ct 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/golangplus/testing/assert" 9 | ) 10 | 11 | func TestAnsiText(t *testing.T) { 12 | assert.Equal(t, "ansiText", ansiText(None, false, None, false), "") 13 | assert.Equal(t, "ansiText", ansiText(Red, false, None, false), "\x1b[0;31m") 14 | assert.Equal(t, "ansiText", ansiText(Red, true, None, false), "\x1b[0;31;1m") 15 | assert.Equal(t, "ansiText", ansiText(None, false, Green, false), "\x1b[0;42m") 16 | assert.Equal(t, "ansiText", ansiText(Red, false, Green, false), "\x1b[0;31;42m") 17 | assert.Equal(t, "ansiText", ansiText(Red, true, Green, false), "\x1b[0;31;1;42m") 18 | } 19 | -------------------------------------------------------------------------------- /ct_test.go: -------------------------------------------------------------------------------- 1 | package ct 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestChangeColor(t *testing.T) { 9 | defer ResetColor() 10 | fmt.Println("Normal text...") 11 | text := "This is an demo of using ChangeColor to output colorful texts" 12 | i := 1 13 | for _, c := range text { 14 | ChangeColor(Color(i/2%8)+Black, i%2 == 1, Color((i+2)/2%8)+Black, false) 15 | fmt.Print(string(c)) 16 | i++ 17 | } 18 | fmt.Println() 19 | ChangeColor(Red, true, White, false) 20 | fmt.Println("Before reset.") 21 | ChangeColor(Red, false, White, true) 22 | fmt.Println("Before reset.") 23 | ResetColor() 24 | fmt.Println("After reset.") 25 | fmt.Println("After reset.") 26 | } 27 | 28 | func TestForeground(t *testing.T) { 29 | ResetColor() 30 | defer ResetColor() 31 | 32 | fmt.Println("Please check the words under the following text shows with the corresponding front color:") 33 | 34 | colorToText := [...]string{ 35 | Black: "black", 36 | Red: "red", 37 | Green: "green", 38 | Yellow: "yellow", 39 | Blue: "blue", 40 | Magenta: "magenta", 41 | Cyan: "cyan", 42 | White: "white", 43 | } 44 | for i, txt := range colorToText { 45 | cl := Color(i) 46 | if cl != None { 47 | Foreground(cl, false) 48 | fmt.Print(txt, ",") 49 | Foreground(cl, true) 50 | fmt.Print(txt, ",") 51 | } 52 | } 53 | fmt.Println() 54 | } 55 | 56 | func TestBackground(t *testing.T) { 57 | ResetColor() 58 | defer ResetColor() 59 | 60 | fmt.Println("Please check the words under the following text shows with the corresponding background color:") 61 | 62 | colorToText := [...]string{ 63 | Black: "black", 64 | Red: "red", 65 | Green: "green", 66 | Yellow: "yellow", 67 | Blue: "blue", 68 | Magenta: "magenta", 69 | Cyan: "cyan", 70 | White: "white", 71 | } 72 | for i, txt := range colorToText { 73 | cl := Color(i) 74 | if cl != None { 75 | Background(cl, false) 76 | fmt.Print(txt, ",") 77 | Background(cl, true) 78 | fmt.Print(txt, ",") 79 | } 80 | } 81 | fmt.Println() 82 | } 83 | -------------------------------------------------------------------------------- /ct_win.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package ct 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | var fg_colors = []uint16{ 11 | 0, 12 | 0, 13 | foreground_red, 14 | foreground_green, 15 | foreground_red | foreground_green, 16 | foreground_blue, 17 | foreground_red | foreground_blue, 18 | foreground_green | foreground_blue, 19 | foreground_red | foreground_green | foreground_blue} 20 | 21 | var bg_colors = []uint16{ 22 | 0, 23 | 0, 24 | background_red, 25 | background_green, 26 | background_red | background_green, 27 | background_blue, 28 | background_red | background_blue, 29 | background_green | background_blue, 30 | background_red | background_green | background_blue} 31 | 32 | const ( 33 | foreground_blue = uint16(0x0001) 34 | foreground_green = uint16(0x0002) 35 | foreground_red = uint16(0x0004) 36 | foreground_intensity = uint16(0x0008) 37 | background_blue = uint16(0x0010) 38 | background_green = uint16(0x0020) 39 | background_red = uint16(0x0040) 40 | background_intensity = uint16(0x0080) 41 | 42 | foreground_mask = foreground_blue | foreground_green | foreground_red | foreground_intensity 43 | background_mask = background_blue | background_green | background_red | background_intensity 44 | ) 45 | 46 | var ( 47 | kernel32 = syscall.NewLazyDLL("kernel32.dll") 48 | 49 | procGetStdHandle = kernel32.NewProc("GetStdHandle") 50 | procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") 51 | procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 52 | 53 | hStdout uintptr 54 | initScreenInfo *console_screen_buffer_info 55 | ) 56 | 57 | func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { 58 | ret, _, _ := procSetConsoleTextAttribute.Call( 59 | hConsoleOutput, 60 | uintptr(wAttributes)) 61 | return ret != 0 62 | } 63 | 64 | type coord struct { 65 | X, Y int16 66 | } 67 | 68 | type small_rect struct { 69 | Left, Top, Right, Bottom int16 70 | } 71 | 72 | type console_screen_buffer_info struct { 73 | DwSize coord 74 | DwCursorPosition coord 75 | WAttributes uint16 76 | SrWindow small_rect 77 | DwMaximumWindowSize coord 78 | } 79 | 80 | func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *console_screen_buffer_info { 81 | var csbi console_screen_buffer_info 82 | if ret, _, _ := procGetConsoleScreenBufferInfo.Call(hConsoleOutput, uintptr(unsafe.Pointer(&csbi))); ret == 0 { 83 | return nil 84 | } 85 | return &csbi 86 | } 87 | 88 | const ( 89 | std_output_handle = uint32(-11 & 0xFFFFFFFF) 90 | ) 91 | 92 | func init() { 93 | kernel32 := syscall.NewLazyDLL("kernel32.dll") 94 | 95 | procGetStdHandle = kernel32.NewProc("GetStdHandle") 96 | 97 | hStdout, _, _ = procGetStdHandle.Call(uintptr(std_output_handle)) 98 | 99 | initScreenInfo = getConsoleScreenBufferInfo(hStdout) 100 | 101 | syscall.LoadDLL("") 102 | } 103 | 104 | func resetColor() { 105 | if initScreenInfo == nil { // No console info - Ex: stdout redirection 106 | return 107 | } 108 | setConsoleTextAttribute(hStdout, initScreenInfo.WAttributes) 109 | } 110 | 111 | func changeColor(fg Color, fgBright bool, bg Color, bgBright bool) { 112 | attr := uint16(0) 113 | if fg == None || bg == None { 114 | cbufinfo := getConsoleScreenBufferInfo(hStdout) 115 | if cbufinfo == nil { // No console info - Ex: stdout redirection 116 | return 117 | } 118 | attr = cbufinfo.WAttributes 119 | } 120 | if fg != None { 121 | attr = attr & ^foreground_mask | fg_colors[fg] 122 | if fgBright { 123 | attr |= foreground_intensity 124 | } 125 | } 126 | if bg != None { 127 | attr = attr & ^background_mask | bg_colors[bg] 128 | if bgBright { 129 | attr |= background_intensity 130 | } 131 | } 132 | setConsoleTextAttribute(hStdout, attr) 133 | } 134 | -------------------------------------------------------------------------------- /fmt/ctfmt.go: -------------------------------------------------------------------------------- 1 | // Package ctfmt is a handful wrapping of go-colortext (ct) package and fmt package. 2 | package ctfmt 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/daviddengcn/go-colortext" 8 | ) 9 | 10 | // Print calls fmt.Print with foreground color set. 11 | func Print(cl ct.Color, bright bool, a ...interface{}) (n int, err error) { 12 | ct.Foreground(cl, bright) 13 | defer ct.ResetColor() 14 | 15 | return fmt.Print(a...) 16 | } 17 | 18 | // Println calls fmt.Println with foreground color set. 19 | func Println(cl ct.Color, bright bool, a ...interface{}) (n int, err error) { 20 | ct.Foreground(cl, bright) 21 | defer ct.ResetColor() 22 | 23 | return fmt.Println(a...) 24 | } 25 | 26 | // Printf calls fmt.Printf with foreground color set. 27 | func Printf(cl ct.Color, bright bool, format string, a ...interface{}) (n int, err error) { 28 | ct.Foreground(cl, bright) 29 | defer ct.ResetColor() 30 | 31 | return fmt.Printf(format, a...) 32 | } 33 | 34 | // Printfln calls fmt.Printf and add an extra new-line char with foreground color set. 35 | func Printfln(cl ct.Color, bright bool, format string, a ...interface{}) (n int, err error) { 36 | ct.Foreground(cl, bright) 37 | defer ct.ResetColor() 38 | 39 | return fmt.Printf(format+"\n", a...) 40 | } 41 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/daviddengcn/go-colortext 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/golangplus/bytes v1.0.0 // indirect 7 | github.com/golangplus/testing v1.0.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450 h1:7xqw01UYS+KCI25bMrPxwNYkSns2Db1ziQPpVq99FpE= 2 | github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= 3 | github.com/golangplus/bytes v1.0.0 h1:YQKBijBVMsBxIiXT4IEhlKR2zHohjEqPole4umyDX+c= 4 | github.com/golangplus/bytes v1.0.0/go.mod h1:AdRaCFwmc/00ZzELMWb01soso6W1R/++O1XL80yAn+A= 5 | github.com/golangplus/fmt v1.0.0 h1:FnUKtw86lXIPfBMc3FimNF3+ABcV+aH5F17OOitTN+E= 6 | github.com/golangplus/fmt v1.0.0/go.mod h1:zpM0OfbMCjPtd2qkTD/jX2MgiFCqklhSUFyDW44gVQE= 7 | github.com/golangplus/testing v1.0.0 h1:+ZeeiKZENNOMkTTELoSySazi+XaEhVO0mb+eanrSEUQ= 8 | github.com/golangplus/testing v1.0.0/go.mod h1:ZDreixUV3YzhoVraIDyOzHrr76p6NUh6k/pPg/Q3gYA= 9 | --------------------------------------------------------------------------------