github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/output/colorstyle/console_windows.go (about)

     1  package colorstyle
     2  
     3  /*
     4  Sourced from: https://github.com/daviddengcn/go-colortext
     5  See license.txt in same directory for license information
     6  */
     7  
     8  import (
     9  	"io"
    10  	"os"
    11  	"syscall"
    12  	"unsafe"
    13  
    14  	"github.com/ActiveState/cli/internal/logging"
    15  	"github.com/ActiveState/cli/internal/multilog"
    16  	"github.com/ActiveState/cli/internal/rollbar"
    17  )
    18  
    19  var consoleStyleMap = map[Style]uint16{
    20  	Black:   0,
    21  	Red:     consoleRed,
    22  	Green:   consoleGreen,
    23  	Yellow:  consoleRed | consoleGreen | consoleIntensity,
    24  	Blue:    consoleBlue,
    25  	Magenta: consoleRed | consoleBlue,
    26  	Cyan:    consoleGreen | consoleBlue,
    27  	White:   consoleRed | consoleGreen | consoleBlue,
    28  	Orange:  consoleRed | consoleGreen,
    29  }
    30  
    31  const (
    32  	consoleBlue      = uint16(0x0001)
    33  	consoleGreen     = uint16(0x0002)
    34  	consoleRed       = uint16(0x0004)
    35  	consoleIntensity = uint16(0x0008)
    36  
    37  	consoleColorMask = consoleBlue | consoleGreen | consoleRed | consoleIntensity
    38  )
    39  
    40  const (
    41  	stdOutHandle = uint32(-11 & 0xFFFFFFFF)
    42  )
    43  
    44  type consoleBufferDimensions struct {
    45  	X, Y int16
    46  }
    47  
    48  type consoleBuffer struct {
    49  	DwSize           consoleBufferDimensions
    50  	DwCursorPosition consoleBufferDimensions
    51  	WAttributes      uint16
    52  	SrWindow         struct {
    53  		Left, Top, Right, Bottom int16
    54  	}
    55  	DwMaximumWindowSize consoleBufferDimensions
    56  }
    57  
    58  var (
    59  	kernel32 = syscall.NewLazyDLL("kernel32.dll")
    60  
    61  	procGetStdHandle               = kernel32.NewProc("GetStdHandle")
    62  	procSetconsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
    63  	procGetconsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
    64  
    65  	hStdout    uintptr
    66  	bufferInfo *consoleBuffer
    67  )
    68  
    69  func init() {
    70  	kernel32 := syscall.NewLazyDLL("kernel32.dll")
    71  	procGetStdHandle = kernel32.NewProc("GetStdHandle")
    72  	hStdout, _, _ = procGetStdHandle.Call(uintptr(stdOutHandle))
    73  	bufferInfo = getConsoleScreenBufferInfo(hStdout)
    74  	_, _ = syscall.LoadDLL("")
    75  }
    76  
    77  type Styler struct {
    78  }
    79  
    80  func New(writer io.Writer) *Styler {
    81  	return &Styler{}
    82  }
    83  
    84  func (st *Styler) SetStyle(s Style, bright bool) {
    85  	if bufferInfo == nil {
    86  		return
    87  	}
    88  
    89  	defer func() {
    90  		if r := recover(); r != nil {
    91  			if os.Getenv("CI") == "" {
    92  				multilog.Log(logging.ErrorNoStacktrace, rollbar.Error)("colorstyle.SetStyle failed with: %v", r)
    93  			}
    94  		}
    95  	}()
    96  
    97  	if s == Bold || s == Underline {
    98  		return // underline/bold is not supported on windows
    99  	}
   100  
   101  	attr := uint16(0)
   102  	if s == Default || s == Reset {
   103  		attr = bufferInfo.WAttributes
   104  	} else if s == Dim {
   105  		attr = attr & ^consoleColorMask | consoleStyleMap[Black]
   106  		bright = true
   107  	} else {
   108  		if style, ok := consoleStyleMap[s]; ok {
   109  			attr = attr & ^consoleColorMask | style
   110  		}
   111  	}
   112  	if bright {
   113  		attr |= consoleIntensity
   114  	}
   115  	setConsoleTextAttribute(hStdout, attr)
   116  }
   117  
   118  func setConsoleTextAttribute(hconsoleOutput uintptr, wAttributes uint16) bool {
   119  	ret, _, _ := procSetconsoleTextAttribute.Call(
   120  		hconsoleOutput,
   121  		uintptr(wAttributes))
   122  	return ret != 0
   123  }
   124  
   125  func getConsoleScreenBufferInfo(hconsoleOutput uintptr) *consoleBuffer {
   126  	var csbi consoleBuffer
   127  	if ret, _, _ := procGetconsoleScreenBufferInfo.Call(hconsoleOutput, uintptr(unsafe.Pointer(&csbi))); ret == 0 {
   128  		return nil
   129  	}
   130  	return &csbi
   131  }