| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- // +build windows
- package tty
- import (
- "os"
- "syscall"
- "unsafe"
- "github.com/mattn/go-isatty"
- )
- const (
- rightAltPressed = 1
- leftAltPressed = 2
- rightCtrlPressed = 4
- leftCtrlPressed = 8
- shiftPressed = 0x0010
- ctrlPressed = rightCtrlPressed | leftCtrlPressed
- altPressed = rightAltPressed | leftAltPressed
- )
- const (
- enableProcessedInput = 0x1
- enableLineInput = 0x2
- enableEchoInput = 0x4
- enableWindowInput = 0x8
- enableMouseInput = 0x10
- enableInsertMode = 0x20
- enableQuickEditMode = 0x40
- enableExtendedFlag = 0x80
- enableProcessedOutput = 1
- enableWrapAtEolOutput = 2
- keyEvent = 0x1
- mouseEvent = 0x2
- windowBufferSizeEvent = 0x4
- )
- var kernel32 = syscall.NewLazyDLL("kernel32.dll")
- var (
- procAllocConsole = kernel32.NewProc("AllocConsole")
- procSetStdHandle = kernel32.NewProc("SetStdHandle")
- procGetStdHandle = kernel32.NewProc("GetStdHandle")
- procSetConsoleScreenBufferSize = kernel32.NewProc("SetConsoleScreenBufferSize")
- procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer")
- procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
- procWriteConsoleOutputCharacter = kernel32.NewProc("WriteConsoleOutputCharacterW")
- procWriteConsoleOutputAttribute = kernel32.NewProc("WriteConsoleOutputAttribute")
- procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
- procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
- procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
- procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW")
- procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
- procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
- procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
- procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
- procScrollConsoleScreenBuffer = kernel32.NewProc("ScrollConsoleScreenBufferW")
- )
- type wchar uint16
- type short int16
- type dword uint32
- type word uint16
- type coord struct {
- x short
- y short
- }
- type smallRect struct {
- left short
- top short
- right short
- bottom short
- }
- type consoleScreenBufferInfo struct {
- size coord
- cursorPosition coord
- attributes word
- window smallRect
- maximumWindowSize coord
- }
- type consoleCursorInfo struct {
- size dword
- visible int32
- }
- type inputRecord struct {
- eventType word
- _ [2]byte
- event [16]byte
- }
- type keyEventRecord struct {
- keyDown int32
- repeatCount word
- virtualKeyCode word
- virtualScanCode word
- unicodeChar wchar
- controlKeyState dword
- }
- type windowBufferSizeRecord struct {
- size coord
- }
- type mouseEventRecord struct {
- mousePos coord
- buttonState dword
- controlKeyState dword
- eventFlags dword
- }
- type charInfo struct {
- unicodeChar wchar
- attributes word
- }
- type TTY struct {
- in *os.File
- out *os.File
- st uint32
- rs []rune
- ws chan WINSIZE
- }
- func readConsoleInput(fd uintptr, record *inputRecord) (err error) {
- var w uint32
- r1, _, err := procReadConsoleInput.Call(fd, uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&w)))
- if r1 == 0 {
- return err
- }
- return nil
- }
- func open() (*TTY, error) {
- tty := new(TTY)
- if false && isatty.IsTerminal(os.Stdin.Fd()) {
- tty.in = os.Stdin
- } else {
- in, err := syscall.Open("CONIN$", syscall.O_RDWR, 0)
- if err != nil {
- return nil, err
- }
- tty.in = os.NewFile(uintptr(in), "/dev/tty")
- }
- if isatty.IsTerminal(os.Stdout.Fd()) {
- tty.out = os.Stdout
- } else {
- procAllocConsole.Call()
- out, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
- if err != nil {
- return nil, err
- }
- tty.out = os.NewFile(uintptr(out), "/dev/tty")
- }
- h := tty.in.Fd()
- var st uint32
- r1, _, err := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&st)))
- if r1 == 0 {
- return nil, err
- }
- tty.st = st
- st &^= enableEchoInput
- st &^= enableInsertMode
- st &^= enableLineInput
- st &^= enableMouseInput
- st &^= enableWindowInput
- st &^= enableExtendedFlag
- st &^= enableQuickEditMode
- st &^= enableProcessedInput
- // ignore error
- procSetConsoleMode.Call(h, uintptr(st))
- tty.ws = make(chan WINSIZE)
- return tty, nil
- }
- func (tty *TTY) buffered() bool {
- return len(tty.rs) > 0
- }
- func (tty *TTY) readRune() (rune, error) {
- if len(tty.rs) > 0 {
- r := tty.rs[0]
- tty.rs = tty.rs[1:]
- return r, nil
- }
- var ir inputRecord
- err := readConsoleInput(tty.in.Fd(), &ir)
- if err != nil {
- return 0, err
- }
- switch ir.eventType {
- case windowBufferSizeEvent:
- wr := (*windowBufferSizeRecord)(unsafe.Pointer(&ir.event))
- tty.ws <- WINSIZE{
- W: int(wr.size.x),
- H: int(wr.size.y),
- }
- case keyEvent:
- kr := (*keyEventRecord)(unsafe.Pointer(&ir.event))
- if kr.keyDown != 0 {
- if kr.controlKeyState&altPressed != 0 && kr.unicodeChar > 0 {
- tty.rs = []rune{rune(kr.unicodeChar)}
- return rune(0x1b), nil
- }
- if kr.unicodeChar > 0 {
- if kr.controlKeyState&shiftPressed != 0 {
- switch kr.unicodeChar {
- case 0x09:
- tty.rs = []rune{0x5b, 0x5a}
- return rune(0x1b), nil
- }
- }
- return rune(kr.unicodeChar), nil
- }
- vk := kr.virtualKeyCode
- switch vk {
- case 0x21: // page-up
- tty.rs = []rune{0x5b, 0x35, 0x7e}
- return rune(0x1b), nil
- case 0x22: // page-down
- tty.rs = []rune{0x5b, 0x36, 0x7e}
- return rune(0x1b), nil
- case 0x23: // end
- tty.rs = []rune{0x5b, 0x46}
- return rune(0x1b), nil
- case 0x24: // home
- tty.rs = []rune{0x5b, 0x48}
- return rune(0x1b), nil
- case 0x25: // left
- tty.rs = []rune{0x5b, 0x44}
- return rune(0x1b), nil
- case 0x26: // up
- tty.rs = []rune{0x5b, 0x41}
- return rune(0x1b), nil
- case 0x27: // right
- tty.rs = []rune{0x5b, 0x43}
- return rune(0x1b), nil
- case 0x28: // down
- tty.rs = []rune{0x5b, 0x42}
- return rune(0x1b), nil
- case 0x2e: // delete
- tty.rs = []rune{0x5b, 0x33, 0x7e}
- return rune(0x1b), nil
- case 0x70, 0x71, 0x72, 0x73: // F1,F2,F3,F4
- tty.rs = []rune{0x5b, 0x4f, rune(vk) - 0x20}
- return rune(0x1b), nil
- case 0x074, 0x75, 0x76, 0x77: // F5,F6,F7,F8
- tty.rs = []rune{0x5b, 0x31, rune(vk) - 0x3f, 0x7e}
- return rune(0x1b), nil
- case 0x78, 0x79: // F9,F10
- tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x48, 0x7e}
- return rune(0x1b), nil
- case 0x7a, 0x7b: // F11,F12
- tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x47, 0x7e}
- return rune(0x1b), nil
- }
- return 0, nil
- }
- }
- return 0, nil
- }
- func (tty *TTY) close() error {
- close(tty.ws)
- procSetConsoleMode.Call(tty.in.Fd(), uintptr(tty.st))
- return nil
- }
- func (tty *TTY) size() (int, int, error) {
- var csbi consoleScreenBufferInfo
- r1, _, err := procGetConsoleScreenBufferInfo.Call(tty.out.Fd(), uintptr(unsafe.Pointer(&csbi)))
- if r1 == 0 {
- return 0, 0, err
- }
- return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1), nil
- }
- func (tty *TTY) input() *os.File {
- return tty.in
- }
- func (tty *TTY) output() *os.File {
- return tty.out
- }
- func (tty *TTY) raw() (func() error, error) {
- var st uint32
- r1, _, err := procGetConsoleMode.Call(tty.in.Fd(), uintptr(unsafe.Pointer(&st)))
- if r1 == 0 {
- return nil, err
- }
- mode := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
- r1, _, err = procSetConsoleMode.Call(tty.in.Fd(), uintptr(mode))
- if r1 == 0 {
- return nil, err
- }
- return func() error {
- r1, _, err := procSetConsoleMode.Call(tty.in.Fd(), uintptr(st))
- if r1 == 0 {
- return err
- }
- return nil
- }, nil
- }
- func (tty *TTY) sigwinch() chan WINSIZE {
- return tty.ws
- }
|