5

I want to handle user input, but in the background, like in a new thread.

For example, show a progress bar, and when the user hits R, the progress bar resets, or if the user hits Q, the script exits.

I don't want the script to wait for user input. Just render everything and if the user hits any key do something.

Is it posible in bash?

Thanks in advance.

EDIT: I need the script ALWAYS read user input but do not interrupt the execution of main loop.Complicated I make myself understood in English

_handle_keys()
{
    read -sn1 a
    test "$a" == `echo -en "\e"` || continue
    read -sn1 a
    test "$a" == "[" || break
    read -sn1 a

    case "$a" in
        C) # Derecha
            if [ $PALETTE_X -lt $(($COLUMNS-$PALETTE_SIZE)) ] ; then
                PALETTE_X=$(($PALETTE_X+1))
            fi
        ;; 
        D) # Izquierda
            if [ $PALETTE_X -gt 0 ] ; then
                PALETTE_X=$(($PALETTE_X-1))
            fi
        ;;
    esac
}
render()
{
    clear
    printf "\033[2;0f BALL (X:${BALL_X} | Y:${BALL_Y})"
    _palette_render # Actualiza la paleta
    _ball_render
}

while true
do
    LINES=`tput lines`
    COLUMNS=`tput cols`

    render
    _handle_keys
done

In my script, the ball moves (render>_ball_render) only when a key is pressed because _handle_keys wait for user input.

I made a ugly solution with read -t0.1 but don't like this

PD: Sorry for my last comment, the time edit finish in the middle of my editing

jotapdiez
  • 1,456
  • 13
  • 28
  • @user719946 you may need to clarify what you're trying to do. Background jobs will get suspended if they try to read from the terminal. I got some results googling for "bash non-blocking io", is that what you're trying to do? Also, no need to put [bash] in the subject, the tag is sufficient. – Andy Apr 22 '11 at 03:19
  • @andy thx for your answer. I'm doing some script like a pong game. The ball update movement is in a main loop (while true do). So, the read input (move the palette) block the ball movements because wait until the user hit any key. Hope you understand me. Sorry for [bash] and no username; this is my first question. – jotapdiez Apr 22 '11 at 04:21

2 Answers2

9

Here is a technique that seems to work. I am basing this on Sam Hocevar's answer to Bash: How to end infinite loop with any key pressed?.

#!/bin/bash

if [ ! -t 0 ]; then
  echo "This script must be run from a terminal"
  exit 1
fi

stty -echo -icanon time 0 min 0

count=0
keypress=''
while true; do
  let count+=1
  echo -ne $count'\r'

  # This stuff goes in _handle_keys
  read keypress
  case $keypress in
  # This case is for no keypress
  "")
    ;;
  $'\e[C')
    echo "derecha"
    ;;
  $'\e[D')
    echo "izquierda"
    ;;
  # If you want to do something for unknown keys, otherwise leave this out
  *)
    echo "unknown input $keypress"
    ;;
  esac
  # End _handle_keys
done

stty sane

If the stty sane is missed (e.g. because the script gets killed with Ctrl-C), the terminal will be left in a weird state. You may want to look at the trap statement to address this.

Community
  • 1
  • 1
Andy
  • 4,789
  • 23
  • 20
  • In your script, the loop ends when a key is pressed. I been update my first post to show you my script. – jotapdiez Apr 22 '11 at 18:36
  • @jotapdiez I updated the example. I'm not sure how well this will work on a slow terminal, the multi-byte sequences may get broken up. But this should be close. – Andy Apr 22 '11 at 19:26
  • Works!! I made some others things to make it work better but your answer work fine. Thanks. (I can't vote up because needs more rep, sorry) – jotapdiez Apr 22 '11 at 21:21
1

You might also add "reset" to the end of the script to reset the terminal into original state, or it might look locked. It will clear the screen as well, so one might want to add a pause before executing the command.

Rado
  • 66
  • 3