github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/lib/terminal/terminal.go (about) 1 // Package terminal provides VT100 terminal codes and a windows 2 // implementation of that. 3 package terminal 4 5 import ( 6 "context" 7 "io" 8 "os" 9 "runtime" 10 "sync" 11 12 colorable "github.com/mattn/go-colorable" 13 "github.com/rclone/rclone/fs" 14 ) 15 16 // VT100 codes 17 const ( 18 EraseLine = "\x1b[2K" 19 MoveToStartOfLine = "\x1b[1G" 20 MoveUp = "\x1b[1A" 21 22 Reset = "\x1b[0m" 23 Bright = "\x1b[1m" 24 Dim = "\x1b[2m" 25 Underscore = "\x1b[4m" 26 Blink = "\x1b[5m" 27 Reverse = "\x1b[7m" 28 Hidden = "\x1b[8m" 29 30 BlackFg = "\x1b[30m" 31 RedFg = "\x1b[31m" 32 GreenFg = "\x1b[32m" 33 YellowFg = "\x1b[33m" 34 BlueFg = "\x1b[34m" 35 MagentaFg = "\x1b[35m" 36 CyanFg = "\x1b[36m" 37 WhiteFg = "\x1b[37m" 38 39 BlackBg = "\x1b[40m" 40 RedBg = "\x1b[41m" 41 GreenBg = "\x1b[42m" 42 YellowBg = "\x1b[43m" 43 BlueBg = "\x1b[44m" 44 MagentaBg = "\x1b[45m" 45 CyanBg = "\x1b[46m" 46 WhiteBg = "\x1b[47m" 47 48 HiBlackFg = "\x1b[90m" 49 HiRedFg = "\x1b[91m" 50 HiGreenFg = "\x1b[92m" 51 HiYellowFg = "\x1b[93m" 52 HiBlueFg = "\x1b[94m" 53 HiMagentaFg = "\x1b[95m" 54 HiCyanFg = "\x1b[96m" 55 HiWhiteFg = "\x1b[97m" 56 57 HiBlackBg = "\x1b[100m" 58 HiRedBg = "\x1b[101m" 59 HiGreenBg = "\x1b[102m" 60 HiYellowBg = "\x1b[103m" 61 HiBlueBg = "\x1b[104m" 62 HiMagentaBg = "\x1b[105m" 63 HiCyanBg = "\x1b[106m" 64 HiWhiteBg = "\x1b[107m" 65 66 ChangeTitle = "\033]0;" 67 BEL = "\007" 68 ) 69 70 var ( 71 // make sure that start is only called once 72 once sync.Once 73 ) 74 75 // Start the terminal - must be called before use 76 func Start() { 77 once.Do(func() { 78 ci := fs.GetConfig(context.Background()) 79 80 f := os.Stdout 81 if !IsTerminal(int(f.Fd())) { 82 // If stdout is not a tty, remove escape codes EXCEPT if terminal color mode equals "ALWAYS" 83 if ci.TerminalColorMode == fs.TerminalColorModeAlways { 84 Out = colorable.NewColorable(f) 85 } else { 86 Out = colorable.NewNonColorable(f) 87 } 88 } else if runtime.GOOS == "windows" && os.Getenv("TERM") != "" { 89 // If TERM is set just use stdout 90 Out = f 91 } else if ci.TerminalColorMode == fs.TerminalColorModeNever { 92 Out = colorable.NewNonColorable(f) 93 } else { 94 Out = colorable.NewColorable(f) 95 } 96 }) 97 } 98 99 // WriteString writes the string passed in to the terminal 100 func WriteString(s string) { 101 Write([]byte(s)) 102 } 103 104 // Out is an io.Writer which can be used to write to the terminal 105 // e.g. for use with fmt.Fprintf(terminal.Out, "terminal fun: %d\n", n) 106 var Out io.Writer 107 108 // Write sends out to the VT100 terminal. 109 // It will initialise the terminal if this is the first call. 110 func Write(out []byte) { 111 Start() 112 _, _ = Out.Write(out) 113 } 114 115 // EnableColorsStdout enable colors if possible. 116 // This enables virtual terminal processing on Windows 10 console, 117 // adding native support for VT100 escape codes. When this terminal 118 // package is used for output, the result is that the colorable library 119 // don't have to decode the escapes and explicitly write text with color 120 // formatting to the console using Windows API functions, but can simply 121 // relay everything to stdout. 122 func EnableColorsStdout() { 123 _ = colorable.EnableColorsStdout(nil) 124 }