5

I want to know which keys are pressed on my keyboard and print the information to stdout.

A tool that can do this is showkey. However, if I want to pass the data of showkey to read:

while read line; do
  echo "$line" | otherprog
done <`showkey -a`

OR

showkey -a | while read line; do
  echo "$line" | otherprog
done

Then showkey waits until a sum of 140 characters is typed in and then sends the buffered information to read.

showkey -a prints the pressed keys line by line, without any buffering.

  1. Why does it buffer?
  2. How do I avoid this buffering, so that I can read showkey's output truly line by line?
  3. Is there an alternative to showkey?
  4. Is there a file I can read the pressed keys directly from?
  5. What is the correct way to pass data to read?

Solution:

I've used lornix's solution and included it into my simple keyboard keyboard :D!

stdbuf -o0 showkey -a | while read line; do
  perl -e 'print sprintf "%030s\n",shift' "$line" | aplay &> /dev/null &
done

Lasership version:

#!/bin/bash
MP3=(); for i in mp3/*.mp3; do MP3+=("$i"); done
NMP3=${#MP3[@]}
stdbuf -o0 showkey -a 2>/dev/null | while read line; do
    [ -z "$line" ] || ! [[ $line =~ ^[0-9] ]] && continue
    NUM="$(echo "$line" | awk '{print $2}')"
    mplayer "${MP3[$(($NUM % $NMP3))]}" &>/dev/null &
done

In the same folder, download some laser mp3 files into a folder called mp3.

polym
  • 10,852

2 Answers2

5

Try setting showkey output to non-buffering with the stdbuf command:

stdbuf -o0 showkey -a | cat -

Will show the output as keys are pressed, rather than buffering a line.

stdbuf can adjust the buffering of stdin, stdout and stderr, setting them to none, line buffered, or block buffered, with a choosable block size. Very handy.

lornix
  • 3,482
4

It buffers because your terminal is set to a line-oriented line-discipline. You need stty raw. Try this:

state=$(stty -g) 
key=$( (stty raw ; dd bs=1 count=1; stty $state) </dev/tty 2>/dev/null) 

But that will only work for single-byte keypresses. It might be a good idea to doLC_ALL=C first if there's a chance that the input could contain multi-byte kepresses. A more sophisticated example might look more like this:

{   exit=$(printf '\003')
    tty_state=$(stty -g)
    stty raw istrip
    while key=$(
        dd bs=1 count=1
    ) ; do : "${key:=
}";     printf " %03o %03d %#x\n\r" \
            "'$key" "'$key" "'$key"
        [ -z "${key#"$exit"}" ] && {
            stty "$tty_state"
            break
        }
    done 2>/dev/null
} </dev/tty
mikeserv
  • 58,310