10

I have a REPL app in Go that should react to keyboard press events (distinct action for each key pressed key) but ReadString expects for return key to be pressed before reading os.Stdin:

import (
    "bufio"
    "os"
)

for {
    reader := bufio.NewReader(os.Stdin)
    key, _ := reader.ReadString('\n')
    deferKey(key)
}

How can I react to key press events in Go?

marcio
  • 10,002
  • 11
  • 54
  • 83
  • 3
    possible duplicate of [Golang function similar to getchar](http://stackoverflow.com/questions/14094190/golang-function-similar-to-getchar) – Linear Nov 29 '14 at 08:11
  • so I need to use ncurses? No way to do it with go built in packages? – marcio Nov 29 '14 at 13:23
  • 1
    https://github.com/zetamatta/nyagos or https://github.com/rocky/go-fish/blob/d44fd886cb8de9df818211c5692814deeadb0651/repl.go could give you some clues, but I am not sure there is a cross-platform way. – VonC Nov 29 '14 at 14:01
  • I had the same issue and I couldn't find a satisfying solution, so I wrote a [little package](https://github.com/daspoet/gowinkey) myself. Any feedback is greatly appreciated :). – daspoet Mar 06 '21 at 22:09

2 Answers2

5

Game engines commonly implement this kind of functionality. They are usually also pretty much platform agnostic (usually at least Windows, Linux, Mac OS X). Try for instance Azul3D's keyboard library.

The logic is off the top of my head something like

watcher := keyboard.NewWatcher()
// Query for the map containing information about all keys
status := watcher.States()
left := status[keyboard.ArrowLeft]
if left == keyboard.Down {
    // The arrow to left is being held down
    // Do something!
}

Getting a list of what keys are currently being pressed down is a matter of iterating the map through and listing the keys where value was Down.

Brian
  • 823
  • 6
  • 14
user918176
  • 1,770
  • 13
  • 34
  • I ended up using ncurses. But I'll try this because I don't really need all the bloat from a curses library. – marcio Dec 06 '14 at 20:36
  • 4
    yeah, but who's populating that map, something has to set the states, this library won't work for what you need. – leonardo Jan 11 '17 at 18:16
3

This answer for a similar question points to few options depending on which platform you need to implement this in.

I've personally used https://github.com/MarinX/keylogger.
It's well written and easy to understand. At the time, I had to write my own version of this library to listen to multiple keyboards, It was easy to adapt this code for that.
Note that this library is written for Linux only.

Example code:

import (
    "github.com/MarinX/keylogger"
    "github.com/sirupsen/logrus"
)

func main() {

    // find keyboard device, does not require a root permission
    keyboard := keylogger.FindKeyboardDevice()

    logrus.Println("Found a keyboard at", keyboard)
    // init keylogger with keyboard
    k, err := keylogger.New(keyboard)
    if err != nil {
        logrus.Error(err)
        return
    }
    defer k.Close()

    events := k.Read()

    // range of events
    for e := range events {
        switch e.Type {
        // EvKey is used to describe state changes of keyboards, buttons, or other key-like devices.
        // check the input_event.go for more events
        case keylogger.EvKey:

            // if the state of key is pressed
            if e.KeyPress() {
                logrus.Println("[event] press key ", e.KeyString())
            }

            // if the state of key is released
            if e.KeyRelease() {
                logrus.Println("[event] release key ", e.KeyString())
            }

            break
        }
    }
}
17xande
  • 2,430
  • 1
  • 24
  • 33