123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905 |
- /*
- * Public Domain Software
- *
- * I (Matthias Ladkau) am the author of the source code in this file.
- * I have placed the source code in this file in the public domain.
- *
- * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
- */
- package getch
- /*
- The code in this file was inspired by:
- http://code.google.com/p/termbox
- Pure Go termbox implementation
- */
- import (
- "fmt"
- "os"
- "os/signal"
- "strconv"
- "strings"
- "sync"
- "syscall"
- "time"
- "unicode/utf8"
- "unsafe"
- )
- /*
- attachReader attaches a character reader on Windows.
- */
- func attachReader() (getch, error) {
- // Detect the current terminal
- keys, err := detectTerminal()
- if err == nil {
- var inFd int
- // Open the tty for reading and signals
- inFd, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
- if err == nil {
- var out *os.File
- // Open a file descriptor on tty to perform ioctl calls
- out, err = os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
- if err == nil {
- gl := &getchLinux{make(chan os.Signal), inFd, out, keys, syscall.Termios{},
- make(chan *internalKeyEvent), make(chan bool), &sync.WaitGroup{}}
- in := uintptr(gl.inFd)
- // Notify signal channel when I/O is possible
- // Signal: SIGIO 29 I/O now possible (4.2 BSD)
- signal.Notify(gl.sigio, syscall.SIGIO)
- // Set the file status flag for /dev/tty to
- // O_ASYNC Enable generation of signals on the file descriptor
- // O_NONBLOCK Do not wait for input
- err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK)
- if err == nil {
- // Set the process ID that will receive SIGIO signals for events
- // on the file descriptor in.
- err = fcntl(in, syscall.F_SETOWN, syscall.Getpid())
- if err == nil {
- // Read the current serial port settings (terminal attributes)
- err = ioctl(gl.out.Fd(), syscall.TCGETS, &gl.origTios)
- if err == nil {
- // Reconfigure terminal attributes - see Linux termios
- tios := gl.origTios
- // Unsetting the following input mode flags means:
- // IGNBRK Don't ignore BREAK condition on input
- // BRKINT Breaks don't cause SIGINT to be send and read as \0
- // PARMRK Bytes with parity or framing errors are not marked
- // ISTRIP Do not strip off the eighth bit
- // INLCR Do not translate newline to carriage return on input
- // IGNCR No not ignore carriage return on input
- // ICRNL Do not translate carriage return to newline on input
- // IXON Do not enable XON/XOFF flow control on input
- tios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK |
- syscall.ISTRIP | syscall.INLCR | syscall.IGNCR |
- syscall.ICRNL | syscall.IXON
- // Unsetting the following local mode flags means:
- // ECHO Do not echo input characters
- // ECHONL Do not echo newline characters
- // ICANON Do not operate in canonical mode - i.e. no line buffering
- // ISIG Do not generate signals when receiving either INTR, QUIT,
- // SUSP, or DSUSP characters
- // IEXTEN Do not enable implementation-defined input processing
- tios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON |
- syscall.ISIG | syscall.IEXTEN
- // Unsetting the following control mode flags means:
- // CSIZE Clear any character size mask
- // PARENB Do not enable parity generation on output and
- // parity checking for input
- tios.Cflag &^= syscall.CSIZE | syscall.PARENB
- // Set character size mask 8 bit
- tios.Cflag |= syscall.CS8
- // Set minimum number of characters for noncanonical read
- tios.Cc[syscall.VMIN] = 1
- // Set timeout in deciseconds for noncanonical read
- tios.Cc[syscall.VTIME] = 0
- err = ioctl(gl.out.Fd(), syscall.TCSETS, &tios)
- if err == nil {
- // All is well we can start listening for events
- go gl.eventListener()
- return gl, nil
- }
- }
- }
- }
- }
- }
- }
- return nil, err
- }
- // Getch object implementation for linux
- // =====================================
- /*
- getchLinux is the Linux based getch implementation.
- */
- type getchLinux struct {
- sigio chan os.Signal // Channel to receive input signals
- inFd int // Open file descriptor of /dev/tty (reading / signals)
- out *os.File // Open file descriptor on /dev/tty (setting attributes)
- escKeys []string // Escape sequences for non-printable keys
- origTios syscall.Termios // Original terminal attributes
- keyEventChan chan *internalKeyEvent // Channel for input events
- stopChan chan bool // Channel for shutdown event
- wg *sync.WaitGroup // Waitgroup for shutdown process
- }
- var posBuf = make([]byte, 20)
- /*
- CursorPosition returns the current cursor position.
- */
- func (gl *getchLinux) CursorPosition() (int, int, error) {
- var n, x, y int
- var err error
- // Do a normal terminal communication via stdin and stdout
- gl.out.WriteString("\033[6n") // Ask via ANSI escape code
- if n, err = os.Stdin.Read(posBuf); n > 2 && err == nil {
- if res := string(posBuf[:n]); res[:2] == "\033[" {
- // Parse the result
- posStr := strings.Split(res[2:n-1], ";")
- if len(posStr) == 2 {
- if y, err = strconv.Atoi(posStr[0]); err == nil {
- x, err = strconv.Atoi(posStr[1])
- }
- }
- }
- }
- return x - 1, y, err
- }
- /*
- SetCursorPosition sets the current cursor position
- */
- func (gl *getchLinux) SetCursorPosition(x, y int) error {
- // Set position via ANSI escape code
- gl.out.WriteString("\033[")
- gl.out.WriteString(fmt.Sprint(y))
- gl.out.WriteString(";")
- gl.out.WriteString(fmt.Sprint(x + 1))
- gl.out.WriteString("H")
- return nil
- }
- /*
- GetKey returns the next key event or an error. This function blocks if no key
- event is available.
- */
- func (gl *getchLinux) GetKey() *internalKeyEvent {
- return <-gl.keyEventChan
- }
- /*
- GetKeyAsync returns the next key event or an error. This function does not block if no key
- event is available - in this case nil is returned.
- */
- func (gl *getchLinux) GetKeyAsync() *internalKeyEvent {
- select {
- case e := <-gl.keyEventChan:
- return e
- default:
- }
- return nil
- }
- /*
- Close detaches the character reader.
- */
- func (gl *getchLinux) Close() {
- // Send stop command and wait for the eventListener thread to end
- gl.wg.Add(1)
- // Empty all pending keys
- for gl.GetKeyAsync() != nil {
- time.Sleep(10 * time.Millisecond)
- }
- gl.stopChan <- true
- gl.wg.Wait()
- // Reset the original terminal state
- ioctl(gl.out.Fd(), syscall.TCSETS, &gl.origTios)
- // Ignoring errors here since we are closing
- gl.out.Close()
- syscall.Close(gl.inFd)
- }
- /*
- eventListener is a thread which will listen for events.
- */
- func (gl *getchLinux) eventListener() {
- // Create input buffer
- buf := make([]byte, 128)
- // Loop until an exit is requested
- loop:
- for true {
- select { // Wait for input event or shutdown - block until one is received
- case <-gl.sigio: // Check for input event
- for {
- n, err := syscall.Read(gl.inFd, buf)
- if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
- break // Nothing more to read - wait for more
- } else if err != nil {
- gl.keyEventChan <- &internalKeyEvent{err: err}
- }
- // Either stop or send the event - block if the channel is full
- select {
- case <-gl.stopChan:
- break loop
- case gl.keyEventChan <- gl.buildKeyEvent(buf[:n]):
- continue
- }
- }
- case <-gl.stopChan: // Check for shutdown event
- break loop
- }
- }
- gl.wg.Done()
- }
- /*
- buildKeyEvent builds an internalKeyEvent from a terminal input.
- */
- func (gl *getchLinux) buildKeyEvent(buf []byte) *internalKeyEvent {
- ike := &internalKeyEvent{&KeyEvent{}, nil}
- if len(buf) > 0 {
- bufstr := string(buf)
- // Check for escape sequence
- if buf[0] == '\033' && len(buf) > 1 {
- for i, ek := range gl.escKeys {
- if strings.HasPrefix(bufstr, ek) {
- // Found a known termkey - set the code
- termkey := termKey(0xFFFF - i)
- // Codes for non-printable keys
- ike.Rune = 0x00
- switch termkey {
- case termKeyF1:
- ike.Code = KeyF1
- case termKeyF2:
- ike.Code = KeyF2
- case termKeyF3:
- ike.Code = KeyF3
- case termKeyF4:
- ike.Code = KeyF4
- case termKeyF5:
- ike.Code = KeyF5
- case termKeyF6:
- ike.Code = KeyF6
- case termKeyF7:
- ike.Code = KeyF7
- case termKeyF8:
- ike.Code = KeyF8
- case termKeyF9:
- ike.Code = KeyF9
- case termKeyF10:
- ike.Code = KeyF10
- case termKeyF11:
- ike.Code = KeyF11
- case termKeyF12:
- ike.Code = KeyF12
- case termKeyInsert:
- ike.Code = KeyInsert
- case termKeyDelete:
- ike.Code = KeyDelete
- case termKeyHome:
- ike.Code = KeyHome
- case termKeyHome1:
- ike.Code = KeyHome
- case termKeyEnd:
- ike.Code = KeyEnd
- case termKeyEnd1:
- ike.Code = KeyEnd
- case termKeyPgup:
- ike.Code = KeyPgup
- case termKeyPgdn:
- ike.Code = KeyPgdn
- case termKeyArrowUp:
- ike.Code = KeyArrowUp
- case termKeyArrowDown:
- ike.Code = KeyArrowDown
- case termKeyArrowLeft:
- ike.Code = KeyArrowLeft
- case termKeyArrowRight:
- ike.Code = KeyArrowRight
- }
- }
- }
- if ike.KeyEvent.Code == "" {
- // Escape sequence was not detected
- ike.err = &ErrUnknownEscapeSequence{buf}
- }
- } else {
- // Not an escape sequence
- de, deSize := utf8.DecodeRune(buf)
- if len(buf) > deSize {
- // There was more than one character - append the raw input buffer
- ike.RawBuf = make([]byte, len(buf))
- copy(ike.RawBuf, buf)
- }
- // Check for printable character
- ike.Rune = de
- switch de {
- case 'a': // Letters:
- ike.Code = KeyA
- case 'b':
- ike.Code = KeyB
- case 'c':
- ike.Code = KeyC
- case 'd':
- ike.Code = KeyD
- case 'e':
- ike.Code = KeyE
- case 'f':
- ike.Code = KeyF
- case 'g':
- ike.Code = KeyG
- case 'h':
- ike.Code = KeyH
- case 'i':
- ike.Code = KeyI
- case 'j':
- ike.Code = KeyJ
- case 'k':
- ike.Code = KeyK
- case 'l':
- ike.Code = KeyL
- case 'm':
- ike.Code = KeyM
- case 'n':
- ike.Code = KeyN
- case 'o':
- ike.Code = KeyO
- case 'p':
- ike.Code = KeyP
- case 'q':
- ike.Code = KeyQ
- case 'r':
- ike.Code = KeyR
- case 's':
- ike.Code = KeyS
- case 't':
- ike.Code = KeyT
- case 'u':
- ike.Code = KeyU
- case 'v':
- ike.Code = KeyV
- case 'w':
- ike.Code = KeyW
- case 'x':
- ike.Code = KeyX
- case 'y':
- ike.Code = KeyY
- case 'z':
- ike.Code = KeyZ
- case '0': // Numbers
- ike.Code = Key0
- case '1':
- ike.Code = Key1
- case '2':
- ike.Code = Key2
- case '3':
- ike.Code = Key3
- case '4':
- ike.Code = Key4
- case '5':
- ike.Code = Key5
- case '6':
- ike.Code = Key6
- case '7':
- ike.Code = Key7
- case '8':
- ike.Code = Key8
- case '9':
- ike.Code = Key9
- /*
- The following symbols are returned as an Key_UNKNOWN
- as they depend on the actual keyboard layout
- case '`': // Symbols
- ike.Code = KeyBacktick
- case '-':
- ike.Code = KeyMinus
- case '=':
- ike.Code = KeyEqual
- case '\\':
- ike.Code = KeyBackslash
- case ',':
- ike.Code = KeyComma
- case '.':
- ike.Code = KeyDot
- case '/':
- ike.Code = KeySlash
- case ';':
- ike.Code = KeySemiColon
- case '\'':
- ike.Code = KeyQuote
- case '#':
- ike.Code = KeyHash
- case ']':
- ike.Code = KeyBracketOpen
- case '[':
- ike.Code = KeyBracketClose
- */
- case 'A': // Letters:
- ike.Code = KeyA
- ike.Shift = true
- case 'B':
- ike.Code = KeyB
- ike.Shift = true
- case 'C':
- ike.Code = KeyC
- ike.Shift = true
- case 'D':
- ike.Code = KeyD
- ike.Shift = true
- case 'E':
- ike.Code = KeyE
- ike.Shift = true
- case 'F':
- ike.Code = KeyF
- ike.Shift = true
- case 'G':
- ike.Code = KeyG
- ike.Shift = true
- case 'H':
- ike.Code = KeyH
- ike.Shift = true
- case 'I':
- ike.Code = KeyI
- ike.Shift = true
- case 'J':
- ike.Code = KeyJ
- ike.Shift = true
- case 'K':
- ike.Code = KeyK
- ike.Shift = true
- case 'L':
- ike.Code = KeyL
- ike.Shift = true
- case 'M':
- ike.Code = KeyM
- ike.Shift = true
- case 'N':
- ike.Code = KeyN
- ike.Shift = true
- case 'O':
- ike.Code = KeyO
- ike.Shift = true
- case 'P':
- ike.Code = KeyP
- ike.Shift = true
- case 'Q':
- ike.Code = KeyQ
- ike.Shift = true
- case 'R':
- ike.Code = KeyR
- ike.Shift = true
- case 'S':
- ike.Code = KeyS
- ike.Shift = true
- case 'T':
- ike.Code = KeyT
- ike.Shift = true
- case 'U':
- ike.Code = KeyU
- ike.Shift = true
- case 'V':
- ike.Code = KeyV
- ike.Shift = true
- case 'W':
- ike.Code = KeyW
- ike.Shift = true
- case 'X':
- ike.Code = KeyX
- ike.Shift = true
- case 'Y':
- ike.Code = KeyY
- ike.Shift = true
- case 'Z':
- ike.Code = KeyZ
- ike.Shift = true
- case 0x01:
- ike.Code = KeyA
- ike.Ctrl = true
- case 0x02:
- ike.Code = KeyB
- ike.Ctrl = true
- case 0x03:
- ike.Code = KeyC
- ike.Ctrl = true
- case 0x04:
- ike.Code = KeyD
- ike.Ctrl = true
- case 0x05:
- ike.Code = KeyE
- ike.Ctrl = true
- case 0x06:
- ike.Code = KeyF
- ike.Ctrl = true
- case 0x07:
- ike.Code = KeyG
- ike.Ctrl = true
- case 0x08:
- ike.Code = KeyH
- ike.Ctrl = true
- case 0x09:
- ike.Code = KeyI
- ike.Ctrl = true
- case 0x0A:
- ike.Code = KeyJ
- ike.Ctrl = true
- case 0x0B:
- ike.Code = KeyK
- ike.Ctrl = true
- case 0x0C:
- ike.Code = KeyL
- ike.Ctrl = true
- case 0x0D:
- ike.Code = KeyM
- ike.Ctrl = true
- case 0x0E:
- ike.Code = KeyN
- ike.Ctrl = true
- case 0x0F:
- ike.Code = KeyO
- ike.Ctrl = true
- case 0x10:
- ike.Code = KeyP
- ike.Ctrl = true
- case 0x11:
- ike.Code = KeyQ
- ike.Ctrl = true
- case 0x12:
- ike.Code = KeyR
- ike.Ctrl = true
- case 0x13:
- ike.Code = KeyS
- ike.Ctrl = true
- case 0x14:
- ike.Code = KeyT
- ike.Ctrl = true
- case 0x15:
- ike.Code = KeyU
- ike.Ctrl = true
- case 0x16:
- ike.Code = KeyV
- ike.Ctrl = true
- case 0x17:
- ike.Code = KeyW
- ike.Ctrl = true
- case 0x18:
- ike.Code = KeyX
- ike.Ctrl = true
- case 0x19:
- ike.Code = KeyY
- ike.Ctrl = true
- case 0x1a:
- ike.Code = KeyZ
- ike.Ctrl = true
- default:
- ike.Code = KeyUnknown
- }
- // Check for non-printable keys
- // Note: KeyCommand was not recognised
- switch de {
- case '\t':
- ike.Code = KeyTab
- ike.Rune = 0x00
- case '\r':
- ike.Code = KeyEnter
- ike.Rune = 0x00
- case 127:
- ike.Code = KeyBackspace
- ike.Rune = 0x00
- case 27:
- ike.Code = KeyEsc
- ike.Rune = 0x00
- }
- }
- }
- return ike
- }
- // Escape sequences for common terminals
- // =====================================
- /*
- termKey models a linux terminal key
- */
- type termKey uint16
- /*
- Recognized escape sequences
- */
- const (
- termKeyF1 termKey = 0xFFFF - iota
- termKeyF2
- termKeyF3
- termKeyF4
- termKeyF5
- termKeyF6
- termKeyF7
- termKeyF8
- termKeyF9
- termKeyF10
- termKeyF11
- termKeyF12
- termKeyInsert
- termKeyDelete
- termKeyHome
- termKeyHome1 // Alternative key
- termKeyEnd
- termKeyEnd1 // Alternative key
- termKeyPgup
- termKeyPgdn
- termKeyArrowUp
- termKeyArrowUp1 // Alternative key
- termKeyArrowDown
- termKeyArrowDown1 // Alternative key
- termKeyArrowLeft
- termKeyArrowLeft1 // Alternative key
- termKeyArrowRight
- termKeyArrowRight1 // Alternative key
- )
- /*
- Mappings from terminal type to escape sequence for a particular termKey (see above).
- */
- var (
- etermKeys = []string{
- "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~",
- "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~",
- "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~",
- "\x1b[7~", "\x1b[8~", "\x1b[8~", "\x1b[5~", "\x1b[6~",
- "\x1b[A", "\x1bOA", "\x1b[B", "\x1bOB", "\x1b[D", "\x1bOD",
- "\x1b[C", "\x1bOC",
- }
- screenKeys = []string{
- "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~",
- "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~",
- "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[1~", "\x1b[4~",
- "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1bOA", "\x1b[B",
- "\x1bOB", "\x1b[D", "\x1bOD", "\x1b[C", "\x1bOC",
- }
- xtermKeys = []string{
- "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~",
- "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~",
- "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[H", "\x1bOH", "\x1b[F",
- "\x1bOF", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1bOA", "\x1b[B",
- "\x1bOB", "\x1b[D", "\x1bOD", "\x1b[C", "\x1bOC",
- }
- rxvtKeys = []string{
- "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~",
- "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~",
- "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~",
- "\x1b[7~", "\x1b[8~", "\x1b[8~", "\x1b[5~", "\x1b[6~",
- "\x1b[A", "\x1bOA", "\x1b[B", "\x1bOB", "\x1b[D", "\x1bOD",
- "\x1b[C", "\x1bOC",
- }
- linuxKeys = []string{
- "\x1b[[A", "\x1b[[B", "\x1b[[C", "\x1b[[D", "\x1b[[E",
- "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~",
- "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~",
- "\x1b[1~", "\x1b[4~", "\x1b[4~", "\x1b[5~", "\x1b[6~",
- "\x1b[A", "\x1bOA", "\x1b[B", "\x1bOB", "\x1b[D", "\x1bOD",
- "\x1b[C", "\x1bOC",
- }
- DefaultTermMappings = []struct {
- name string
- keys []string
- }{
- {"Eterm", etermKeys},
- {"screen", screenKeys},
- {"xterm", xtermKeys},
- {"xterm-256color", xtermKeys},
- {"rxvt-unicode", rxvtKeys},
- {"rxvt-256color", rxvtKeys},
- {"linux", linuxKeys},
- }
- CompatibilityTermMappings = []struct {
- namePart string
- keys []string
- }{
- {"xterm", xtermKeys},
- {"rxvt", rxvtKeys},
- {"linux", linuxKeys},
- {"Eterm", etermKeys},
- {"screen", screenKeys},
- {"cygwin", xtermKeys},
- {"st", xtermKeys},
- }
- )
- /*
- detectTerminal detects the current terminal and returns the appropriate escape
- sequence mapping for termKeys.
- */
- func detectTerminal() ([]string, error) {
- var keys []string
- name := os.Getenv("TERM")
- if name == "" {
- return nil, fmt.Errorf("Cannot determine terminal - TERM environment variable not set")
- }
- err := fmt.Errorf("Terminal %s is not supported", name)
- // Look for the right mapping for the current terminal
- for _, t := range DefaultTermMappings {
- if name == t.name {
- keys = t.keys
- err = nil
- break
- }
- }
- if keys == nil {
- // Try compatibility mappings if there was no direct match
- for _, t := range CompatibilityTermMappings {
- if strings.Contains(name, t.namePart) {
- keys = t.keys
- err = nil
- break
- }
- }
- }
- return keys, err
- }
- // Util functions
- // ==============
- /*
- fcntl does an OS system call of the same name to manipulate a file descriptor.
- */
- func fcntl(fd uintptr, cmd int, arg int) error {
- var err error
- _, _, e := syscall.Syscall(syscall.SYS_FCNTL, fd, uintptr(cmd),
- uintptr(arg))
- // Follow convention of syscall.Errno to convert from Errno to error
- if e != 0 {
- err = os.NewSyscallError("SYS_FCNTL", e)
- }
- return err
- }
- /*
- ioctl does an OS system call of the same name to manipulates the underlying
- device parameters of special files such as terminals.
- */
- func ioctl(fd uintptr, cmd int, termios *syscall.Termios) error {
- var err error
- r, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(cmd),
- uintptr(unsafe.Pointer(termios)))
- if r != 0 {
- err = os.NewSyscallError("SYS_IOCTL", e)
- }
- return err
- }
|