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