ansi.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // +build windows
  2. package winterm
  3. import (
  4. "fmt"
  5. "os"
  6. "strconv"
  7. "strings"
  8. "syscall"
  9. "github.com/Azure/go-ansiterm"
  10. windows "golang.org/x/sys/windows"
  11. )
  12. // Windows keyboard constants
  13. // See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx.
  14. const (
  15. VK_PRIOR = 0x21 // PAGE UP key
  16. VK_NEXT = 0x22 // PAGE DOWN key
  17. VK_END = 0x23 // END key
  18. VK_HOME = 0x24 // HOME key
  19. VK_LEFT = 0x25 // LEFT ARROW key
  20. VK_UP = 0x26 // UP ARROW key
  21. VK_RIGHT = 0x27 // RIGHT ARROW key
  22. VK_DOWN = 0x28 // DOWN ARROW key
  23. VK_SELECT = 0x29 // SELECT key
  24. VK_PRINT = 0x2A // PRINT key
  25. VK_EXECUTE = 0x2B // EXECUTE key
  26. VK_SNAPSHOT = 0x2C // PRINT SCREEN key
  27. VK_INSERT = 0x2D // INS key
  28. VK_DELETE = 0x2E // DEL key
  29. VK_HELP = 0x2F // HELP key
  30. VK_F1 = 0x70 // F1 key
  31. VK_F2 = 0x71 // F2 key
  32. VK_F3 = 0x72 // F3 key
  33. VK_F4 = 0x73 // F4 key
  34. VK_F5 = 0x74 // F5 key
  35. VK_F6 = 0x75 // F6 key
  36. VK_F7 = 0x76 // F7 key
  37. VK_F8 = 0x77 // F8 key
  38. VK_F9 = 0x78 // F9 key
  39. VK_F10 = 0x79 // F10 key
  40. VK_F11 = 0x7A // F11 key
  41. VK_F12 = 0x7B // F12 key
  42. RIGHT_ALT_PRESSED = 0x0001
  43. LEFT_ALT_PRESSED = 0x0002
  44. RIGHT_CTRL_PRESSED = 0x0004
  45. LEFT_CTRL_PRESSED = 0x0008
  46. SHIFT_PRESSED = 0x0010
  47. NUMLOCK_ON = 0x0020
  48. SCROLLLOCK_ON = 0x0040
  49. CAPSLOCK_ON = 0x0080
  50. ENHANCED_KEY = 0x0100
  51. )
  52. type ansiCommand struct {
  53. CommandBytes []byte
  54. Command string
  55. Parameters []string
  56. IsSpecial bool
  57. }
  58. func newAnsiCommand(command []byte) *ansiCommand {
  59. if isCharacterSelectionCmdChar(command[1]) {
  60. // Is Character Set Selection commands
  61. return &ansiCommand{
  62. CommandBytes: command,
  63. Command: string(command),
  64. IsSpecial: true,
  65. }
  66. }
  67. // last char is command character
  68. lastCharIndex := len(command) - 1
  69. ac := &ansiCommand{
  70. CommandBytes: command,
  71. Command: string(command[lastCharIndex]),
  72. IsSpecial: false,
  73. }
  74. // more than a single escape
  75. if lastCharIndex != 0 {
  76. start := 1
  77. // skip if double char escape sequence
  78. if command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_ESCAPE_SECONDARY {
  79. start++
  80. }
  81. // convert this to GetNextParam method
  82. ac.Parameters = strings.Split(string(command[start:lastCharIndex]), ansiterm.ANSI_PARAMETER_SEP)
  83. }
  84. return ac
  85. }
  86. func (ac *ansiCommand) paramAsSHORT(index int, defaultValue int16) int16 {
  87. if index < 0 || index >= len(ac.Parameters) {
  88. return defaultValue
  89. }
  90. param, err := strconv.ParseInt(ac.Parameters[index], 10, 16)
  91. if err != nil {
  92. return defaultValue
  93. }
  94. return int16(param)
  95. }
  96. func (ac *ansiCommand) String() string {
  97. return fmt.Sprintf("0x%v \"%v\" (\"%v\")",
  98. bytesToHex(ac.CommandBytes),
  99. ac.Command,
  100. strings.Join(ac.Parameters, "\",\""))
  101. }
  102. // isAnsiCommandChar returns true if the passed byte falls within the range of ANSI commands.
  103. // See http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html.
  104. func isAnsiCommandChar(b byte) bool {
  105. switch {
  106. case ansiterm.ANSI_COMMAND_FIRST <= b && b <= ansiterm.ANSI_COMMAND_LAST && b != ansiterm.ANSI_ESCAPE_SECONDARY:
  107. return true
  108. case b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_OSC || b == ansiterm.ANSI_CMD_DECPAM || b == ansiterm.ANSI_CMD_DECPNM:
  109. // non-CSI escape sequence terminator
  110. return true
  111. case b == ansiterm.ANSI_CMD_STR_TERM || b == ansiterm.ANSI_BEL:
  112. // String escape sequence terminator
  113. return true
  114. }
  115. return false
  116. }
  117. func isXtermOscSequence(command []byte, current byte) bool {
  118. return (len(command) >= 2 && command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_CMD_OSC && current != ansiterm.ANSI_BEL)
  119. }
  120. func isCharacterSelectionCmdChar(b byte) bool {
  121. return (b == ansiterm.ANSI_CMD_G0 || b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_G2 || b == ansiterm.ANSI_CMD_G3)
  122. }
  123. // bytesToHex converts a slice of bytes to a human-readable string.
  124. func bytesToHex(b []byte) string {
  125. hex := make([]string, len(b))
  126. for i, ch := range b {
  127. hex[i] = fmt.Sprintf("%X", ch)
  128. }
  129. return strings.Join(hex, "")
  130. }
  131. // ensureInRange adjusts the passed value, if necessary, to ensure it is within
  132. // the passed min / max range.
  133. func ensureInRange(n int16, min int16, max int16) int16 {
  134. if n < min {
  135. return min
  136. } else if n > max {
  137. return max
  138. } else {
  139. return n
  140. }
  141. }
  142. func GetStdFile(nFile int) (*os.File, uintptr) {
  143. var file *os.File
  144. // syscall uses negative numbers
  145. // windows package uses very big uint32
  146. // Keep these switches split so we don't have to convert ints too much.
  147. switch uint32(nFile) {
  148. case windows.STD_INPUT_HANDLE:
  149. file = os.Stdin
  150. case windows.STD_OUTPUT_HANDLE:
  151. file = os.Stdout
  152. case windows.STD_ERROR_HANDLE:
  153. file = os.Stderr
  154. default:
  155. switch nFile {
  156. case syscall.STD_INPUT_HANDLE:
  157. file = os.Stdin
  158. case syscall.STD_OUTPUT_HANDLE:
  159. file = os.Stdout
  160. case syscall.STD_ERROR_HANDLE:
  161. file = os.Stderr
  162. default:
  163. panic(fmt.Errorf("Invalid standard handle identifier: %v", nFile))
  164. }
  165. }
  166. fd, err := syscall.GetStdHandle(nFile)
  167. if err != nil {
  168. panic(fmt.Errorf("Invalid standard handle identifier: %v -- %v", nFile, err))
  169. }
  170. return file, uintptr(fd)
  171. }