You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 lines
4.3 KiB

  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build windows
  5. // Package terminal provides support functions for dealing with terminals, as
  6. // commonly found on UNIX systems.
  7. //
  8. // Putting a terminal into raw mode is the most common requirement:
  9. //
  10. // oldState, err := terminal.MakeRaw(0)
  11. // if err != nil {
  12. // panic(err)
  13. // }
  14. // defer terminal.Restore(0, oldState)
  15. package terminal
  16. import (
  17. "io"
  18. "syscall"
  19. "unsafe"
  20. )
  21. const (
  22. enableLineInput = 2
  23. enableEchoInput = 4
  24. enableProcessedInput = 1
  25. enableWindowInput = 8
  26. enableMouseInput = 16
  27. enableInsertMode = 32
  28. enableQuickEditMode = 64
  29. enableExtendedFlags = 128
  30. enableAutoPosition = 256
  31. enableProcessedOutput = 1
  32. enableWrapAtEolOutput = 2
  33. )
  34. var kernel32 = syscall.NewLazyDLL("kernel32.dll")
  35. var (
  36. procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
  37. procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
  38. procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
  39. )
  40. type (
  41. short int16
  42. word uint16
  43. coord struct {
  44. x short
  45. y short
  46. }
  47. smallRect struct {
  48. left short
  49. top short
  50. right short
  51. bottom short
  52. }
  53. consoleScreenBufferInfo struct {
  54. size coord
  55. cursorPosition coord
  56. attributes word
  57. window smallRect
  58. maximumWindowSize coord
  59. }
  60. )
  61. type State struct {
  62. mode uint32
  63. }
  64. // IsTerminal returns true if the given file descriptor is a terminal.
  65. func IsTerminal(fd int) bool {
  66. var st uint32
  67. r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  68. return r != 0 && e == 0
  69. }
  70. // MakeRaw put the terminal connected to the given file descriptor into raw
  71. // mode and returns the previous state of the terminal so that it can be
  72. // restored.
  73. func MakeRaw(fd int) (*State, error) {
  74. var st uint32
  75. _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  76. if e != 0 {
  77. return nil, error(e)
  78. }
  79. st &^= (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
  80. _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
  81. if e != 0 {
  82. return nil, error(e)
  83. }
  84. return &State{st}, nil
  85. }
  86. // GetState returns the current state of a terminal which may be useful to
  87. // restore the terminal after a signal.
  88. func GetState(fd int) (*State, error) {
  89. var st uint32
  90. _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  91. if e != 0 {
  92. return nil, error(e)
  93. }
  94. return &State{st}, nil
  95. }
  96. // Restore restores the terminal connected to the given file descriptor to a
  97. // previous state.
  98. func Restore(fd int, state *State) error {
  99. _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0)
  100. return err
  101. }
  102. // GetSize returns the dimensions of the given terminal.
  103. func GetSize(fd int) (width, height int, err error) {
  104. var info consoleScreenBufferInfo
  105. _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0)
  106. if e != 0 {
  107. return 0, 0, error(e)
  108. }
  109. return int(info.size.x), int(info.size.y), nil
  110. }
  111. // ReadPassword reads a line of input from a terminal without local echo. This
  112. // is commonly used for inputting passwords and other sensitive data. The slice
  113. // returned does not include the \n.
  114. func ReadPassword(fd int) ([]byte, error) {
  115. var st uint32
  116. _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  117. if e != 0 {
  118. return nil, error(e)
  119. }
  120. old := st
  121. st &^= (enableEchoInput)
  122. st |= (enableProcessedInput | enableLineInput | enableProcessedOutput)
  123. _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
  124. if e != 0 {
  125. return nil, error(e)
  126. }
  127. defer func() {
  128. syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0)
  129. }()
  130. var buf [16]byte
  131. var ret []byte
  132. for {
  133. n, err := syscall.Read(syscall.Handle(fd), buf[:])
  134. if err != nil {
  135. return nil, err
  136. }
  137. if n == 0 {
  138. if len(ret) == 0 {
  139. return nil, io.EOF
  140. }
  141. break
  142. }
  143. if buf[n-1] == '\n' {
  144. n--
  145. }
  146. if n > 0 && buf[n-1] == '\r' {
  147. n--
  148. }
  149. ret = append(ret, buf[:n]...)
  150. if n < len(buf) {
  151. break
  152. }
  153. }
  154. return ret, nil
  155. }