pb_win.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // +build windows
  2. package pb
  3. import (
  4. "errors"
  5. "fmt"
  6. "os"
  7. "sync"
  8. "syscall"
  9. "unsafe"
  10. )
  11. var tty = os.Stdin
  12. var (
  13. kernel32 = syscall.NewLazyDLL("kernel32.dll")
  14. // GetConsoleScreenBufferInfo retrieves information about the
  15. // specified console screen buffer.
  16. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx
  17. procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
  18. // GetConsoleMode retrieves the current input mode of a console's
  19. // input buffer or the current output mode of a console screen buffer.
  20. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
  21. getConsoleMode = kernel32.NewProc("GetConsoleMode")
  22. // SetConsoleMode sets the input mode of a console's input buffer
  23. // or the output mode of a console screen buffer.
  24. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
  25. setConsoleMode = kernel32.NewProc("SetConsoleMode")
  26. // SetConsoleCursorPosition sets the cursor position in the
  27. // specified console screen buffer.
  28. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx
  29. setConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
  30. )
  31. type (
  32. // Defines the coordinates of the upper left and lower right corners
  33. // of a rectangle.
  34. // See
  35. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311(v=vs.85).aspx
  36. smallRect struct {
  37. Left, Top, Right, Bottom int16
  38. }
  39. // Defines the coordinates of a character cell in a console screen
  40. // buffer. The origin of the coordinate system (0,0) is at the top, left cell
  41. // of the buffer.
  42. // See
  43. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx
  44. coordinates struct {
  45. X, Y int16
  46. }
  47. word int16
  48. // Contains information about a console screen buffer.
  49. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
  50. consoleScreenBufferInfo struct {
  51. dwSize coordinates
  52. dwCursorPosition coordinates
  53. wAttributes word
  54. srWindow smallRect
  55. dwMaximumWindowSize coordinates
  56. }
  57. )
  58. // terminalWidth returns width of the terminal.
  59. func terminalWidth() (width int, err error) {
  60. var info consoleScreenBufferInfo
  61. _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0)
  62. if e != 0 {
  63. return 0, error(e)
  64. }
  65. return int(info.dwSize.X) - 1, nil
  66. }
  67. func getCursorPos() (pos coordinates, err error) {
  68. var info consoleScreenBufferInfo
  69. _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0)
  70. if e != 0 {
  71. return info.dwCursorPosition, error(e)
  72. }
  73. return info.dwCursorPosition, nil
  74. }
  75. func setCursorPos(pos coordinates) error {
  76. _, _, e := syscall.Syscall(setConsoleCursorPosition.Addr(), 2, uintptr(syscall.Stdout), uintptr(uint32(uint16(pos.Y))<<16|uint32(uint16(pos.X))), 0)
  77. if e != 0 {
  78. return error(e)
  79. }
  80. return nil
  81. }
  82. var ErrPoolWasStarted = errors.New("Bar pool was started")
  83. var echoLocked bool
  84. var echoLockMutex sync.Mutex
  85. var oldState word
  86. func lockEcho() (shutdownCh chan struct{}, err error) {
  87. echoLockMutex.Lock()
  88. defer echoLockMutex.Unlock()
  89. if echoLocked {
  90. err = ErrPoolWasStarted
  91. return
  92. }
  93. echoLocked = true
  94. if _, _, e := syscall.Syscall(getConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&oldState)), 0); e != 0 {
  95. err = fmt.Errorf("Can't get terminal settings: %v", e)
  96. return
  97. }
  98. newState := oldState
  99. const ENABLE_ECHO_INPUT = 0x0004
  100. const ENABLE_LINE_INPUT = 0x0002
  101. newState = newState & (^(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))
  102. if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(newState), 0); e != 0 {
  103. err = fmt.Errorf("Can't set terminal settings: %v", e)
  104. return
  105. }
  106. shutdownCh = make(chan struct{})
  107. return
  108. }
  109. func unlockEcho() (err error) {
  110. echoLockMutex.Lock()
  111. defer echoLockMutex.Unlock()
  112. if !echoLocked {
  113. return
  114. }
  115. echoLocked = false
  116. if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(oldState), 0); e != 0 {
  117. err = fmt.Errorf("Can't set terminal settings")
  118. }
  119. return
  120. }