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.

171 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 readline
  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. coord struct {
  42. x short
  43. y short
  44. }
  45. smallRect struct {
  46. left short
  47. top short
  48. right short
  49. bottom short
  50. }
  51. consoleScreenBufferInfo struct {
  52. size coord
  53. cursorPosition coord
  54. attributes word
  55. window smallRect
  56. maximumWindowSize coord
  57. }
  58. )
  59. type State struct {
  60. mode uint32
  61. }
  62. // IsTerminal returns true if the given file descriptor is a terminal.
  63. func IsTerminal(fd int) bool {
  64. var st uint32
  65. r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  66. return r != 0 && e == 0
  67. }
  68. // MakeRaw put the terminal connected to the given file descriptor into raw
  69. // mode and returns the previous state of the terminal so that it can be
  70. // restored.
  71. func MakeRaw(fd int) (*State, error) {
  72. var st uint32
  73. _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  74. if e != 0 {
  75. return nil, error(e)
  76. }
  77. raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
  78. _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
  79. if e != 0 {
  80. return nil, error(e)
  81. }
  82. return &State{st}, nil
  83. }
  84. // GetState returns the current state of a terminal which may be useful to
  85. // restore the terminal after a signal.
  86. func GetState(fd int) (*State, error) {
  87. var st uint32
  88. _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  89. if e != 0 {
  90. return nil, error(e)
  91. }
  92. return &State{st}, nil
  93. }
  94. // Restore restores the terminal connected to the given file descriptor to a
  95. // previous state.
  96. func restoreTerm(fd int, state *State) error {
  97. _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0)
  98. return err
  99. }
  100. // GetSize returns the dimensions of the given terminal.
  101. func GetSize(fd int) (width, height int, err error) {
  102. var info consoleScreenBufferInfo
  103. _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0)
  104. if e != 0 {
  105. return 0, 0, error(e)
  106. }
  107. return int(info.size.x), int(info.size.y), nil
  108. }
  109. // ReadPassword reads a line of input from a terminal without local echo. This
  110. // is commonly used for inputting passwords and other sensitive data. The slice
  111. // returned does not include the \n.
  112. func ReadPassword(fd int) ([]byte, error) {
  113. var st uint32
  114. _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  115. if e != 0 {
  116. return nil, error(e)
  117. }
  118. old := st
  119. st &^= (enableEchoInput)
  120. st |= (enableProcessedInput | enableLineInput | enableProcessedOutput)
  121. _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
  122. if e != 0 {
  123. return nil, error(e)
  124. }
  125. defer func() {
  126. syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0)
  127. }()
  128. var buf [16]byte
  129. var ret []byte
  130. for {
  131. n, err := syscall.Read(syscall.Handle(fd), buf[:])
  132. if err != nil {
  133. return nil, err
  134. }
  135. if n == 0 {
  136. if len(ret) == 0 {
  137. return nil, io.EOF
  138. }
  139. break
  140. }
  141. if buf[n-1] == '\n' {
  142. n--
  143. }
  144. if n > 0 && buf[n-1] == '\r' {
  145. n--
  146. }
  147. ret = append(ret, buf[:n]...)
  148. if n < len(buf) {
  149. break
  150. }
  151. }
  152. return ret, nil
  153. }