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.
 
 
 

156 line
4.2 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. "syscall"
  18. "unsafe"
  19. )
  20. const (
  21. enableLineInput = 2
  22. enableEchoInput = 4
  23. enableProcessedInput = 1
  24. enableWindowInput = 8
  25. enableMouseInput = 16
  26. enableInsertMode = 32
  27. enableQuickEditMode = 64
  28. enableExtendedFlags = 128
  29. enableAutoPosition = 256
  30. enableProcessedOutput = 1
  31. enableWrapAtEolOutput = 2
  32. )
  33. var kernel32 = syscall.NewLazyDLL("kernel32.dll")
  34. var (
  35. procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
  36. procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
  37. procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
  38. )
  39. type (
  40. short int16
  41. word uint16
  42. coord struct {
  43. x short
  44. y short
  45. }
  46. smallRect struct {
  47. left short
  48. top short
  49. right short
  50. bottom short
  51. }
  52. consoleScreenBufferInfo struct {
  53. size coord
  54. cursorPosition coord
  55. attributes word
  56. window smallRect
  57. maximumWindowSize coord
  58. }
  59. )
  60. type State struct {
  61. mode uint32
  62. }
  63. // IsTerminal returns true if the given file descriptor is a terminal.
  64. func IsTerminal(fd int) bool {
  65. var st uint32
  66. r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  67. return r != 0 && e == 0
  68. }
  69. // MakeRaw put the terminal connected to the given file descriptor into raw
  70. // mode and returns the previous state of the terminal so that it can be
  71. // restored.
  72. func MakeRaw(fd int) (*State, error) {
  73. var st uint32
  74. _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  75. if e != 0 {
  76. return nil, error(e)
  77. }
  78. raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
  79. _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
  80. if e != 0 {
  81. return nil, error(e)
  82. }
  83. return &State{st}, nil
  84. }
  85. // GetState returns the current state of a terminal which may be useful to
  86. // restore the terminal after a signal.
  87. func GetState(fd int) (*State, error) {
  88. var st uint32
  89. _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  90. if e != 0 {
  91. return nil, error(e)
  92. }
  93. return &State{st}, nil
  94. }
  95. // Restore restores the terminal connected to the given file descriptor to a
  96. // previous state.
  97. func Restore(fd int, state *State) error {
  98. _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0)
  99. return err
  100. }
  101. // GetSize returns the dimensions of the given terminal.
  102. func GetSize(fd int) (width, height int, err error) {
  103. var info consoleScreenBufferInfo
  104. _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0)
  105. if e != 0 {
  106. return 0, 0, error(e)
  107. }
  108. return int(info.size.x), int(info.size.y), nil
  109. }
  110. // passwordReader is an io.Reader that reads from a specific Windows HANDLE.
  111. type passwordReader int
  112. func (r passwordReader) Read(buf []byte) (int, error) {
  113. return syscall.Read(syscall.Handle(r), buf)
  114. }
  115. // ReadPassword reads a line of input from a terminal without local echo. This
  116. // is commonly used for inputting passwords and other sensitive data. The slice
  117. // returned does not include the \n.
  118. func ReadPassword(fd int) ([]byte, error) {
  119. var st uint32
  120. _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
  121. if e != 0 {
  122. return nil, error(e)
  123. }
  124. old := st
  125. st &^= (enableEchoInput)
  126. st |= (enableProcessedInput | enableLineInput | enableProcessedOutput)
  127. _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
  128. if e != 0 {
  129. return nil, error(e)
  130. }
  131. defer func() {
  132. syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0)
  133. }()
  134. return readPasswordLine(passwordReader(fd))
  135. }