27
Linux game programming An introduction to the use of interval timers and asynchronous input notifications

Linux game programming An introduction to the use of interval timers and asynchronous input notifications

  • View
    226

  • Download
    2

Embed Size (px)

Citation preview

Linux game programming

An introduction to the use of interval timers and asynchronous

input notifications

Our prior animation demo

• We achieved the illusion of “smooth” and “flicker-free” animation, by synchronizing drawing-operations with Vertical Retrace

• But more is needed in a game that’s “fun”

• Some deficiencies in our ‘animate1’ demo:– Ball movements tied to Vertical Retrace– The viewer lacked any real-time control

• How can we overcome these limitations?

Decoupling ‘move’ from ‘draw’

• Our program-loop had used logic like this:do {

vsync(); // delay until start of the next retrace

hide_ball(); // “erase” the ball

move_ball(); // adjust its location

show_ball(); // “redraw” the ball

--count; // decrement a counter }

while ( count > 0 );

• So ball movement is delayed by vertical retraces

Linux provides ‘interval timers’

• #include <sys/time.h>• struct itimervalitval, itold;• itval.it_value.tv_sec = 2; • itval.it_value.tv_usec = 0;• itval.it_interval.tv_sec = 0;• itval.it_interval.tv_usec = 10000;• setitimer( ITIMER_REAL, &itval, &itold );• (See the ‘man’ page for additional details)

structs timeval and itimerval

tv_sec tv_usec

struct timeval

tv_sec tv_usec

tv_sec tv_usec

it_interval

it_value

struct itimerval

It_itimerval = next delay value, it_timeval = current delay value

SIGALRM

• When timer “expires” our application gets notified, by being sent a signal from Linux

• Normally an application gets terminated if the SIGALRM signal is delivered to it

• But we can alter that default behavior, by installing a ‘signal-handler’ that we design

• We can ‘move-the-ball’ when SIGALRM is received, regardless of Vertical Retrace

Our signal-handler

void on_alarm( int signum ){

// modify these global variablesball_xcoordinate += xincrement;ball_ycoordinate += yincrement;

}// The ‘signal()’ function “installs” our handler

signal( SIGALRM, on_alarm);

Main program-loop “revised”

• We can now omit ball-movement in main loop:do {

vsync(); // delay until start of the next retrace

hide_ball(); // “erase” the old ball

oldx = newx; oldy = newy; // remember new position

show_ball(); // “redraw” the new ball

--count; // decrement a counter}

while ( count > 0 );

• Ball-movement is managed by ‘signal-handler’

‘draw’ versus ‘move’

The code in this signal-handler takes care of

all movementswhenever it’s time for them

to occur

The code in thisloop handlesall the actual re-drawing

in sync with thevertical retrace

Signal-handler Program-loop

The Operating System takes care of switching the CPU between these two separate threads-of-control

Giving the user control

• Linux supports “asynchronous” terminal i/o

• We can reprogram the terminal console so a SIGIO signal will be sent to our program whenever the user decides to press a key

• And we can install a ‘signal-handler’ of our own design that executes if SIGIO arrives

• This will allow a user to “control” our game

The ‘tty’ interface

• ‘tty’ is an acronyn for ‘TeleTYpe’ terminal• Such devices have a keyboard and screen• Behavior emulates technology from 1950s• Usually a tty operates in ‘canonical’ mode:

– Each user-keystroke is ‘echoed’ to screen– Some editing is allowed (e.g., backspace)– The keyboard-input is internally buffered– The <ENTER>-key signals an ‘end-of-line’ – Programs receive input one-line-at-a-time

‘tty’ customization

• Sometimes canonical mode isn’t suitable (an example: animated computer games)

• The terminal’s behavior can be modified!

• UNIX provides a convenient interface:– #include <termios.h>– struct termios tty;– int tcgetattr( int fd, struct termios *tty );– int tcsetattr( int fd, int flag, struct termios *tty );

How does the ‘tty’ work?

TeleTYpe display deviceHARDWARE

SOFTWARE

application

tty_driverc_lflag

input handlingc_iflagc_cc

output handlingc_oflag

terminal_driverc_cflag

User space

Kernel space

struct tty { c_iflag; c_oflag; c_cflag; c_lflag; c_line; c_cc[ ]; };

The ‘c_lflag’ field

• This field is just an array of flag bits• Individual bits have symbolic names• Names conform to a POSIX standard• Linux names match other UNIX’s names• Though actual symbol values may differ• Your C/C++ program should use:

#include <termios.h>for portability to other UNIX environments

ICANON and ECHO

• Normally the ‘c_lflag’ field has these set

• They can be cleared using bitwise logic:tty.c_lflag &= ~ECHO; // inhibit echotty.c_lflag &= ~ICANON; // no buffering

The ‘c_cc[ ]’ array

• ‘struct termios’ objects include an array

• The array-indices have symbolic names

• Symbol-names are standardized in UNIX

• Array entries are ‘tty’ operating parameters

• Two useful ones for our purposes are:tty.c_cc[ VMIN ] and tty.c_cc[ VTIME ]

How to setup ‘raw’ terminal-mode

• Step 1: Use ‘tcgetattr()’ to get a copy of the current tty’s ‘struct termios’ settings

• Step 2: Make a working copy of that object

• Step 3: Modify its flags and control-codes

• Step 4: Use ‘tcsetattr()’ to install changes

• Step 5: Perform desired ‘raw’ mode input

• Step 6: Use ‘tcsetattr()’ to restore the terminal to its original default settings

‘raw’ mode needs four changes

• tty.c_cc[ VMIN ] = 1;– so the ‘read()’ function will return as soon as at

least one new input-character is available

• tty.c_cc[ VTIME ] = 0;– so there will be no time-delay after each new

key pressed until the ‘read()’ function returns

• tty.c_lflag &= ~ECHO; // no echoing• tty.c_lflag &= ~ICANON; // no buffering

Demo program: ‘rawtty.cpp’

• This program may soon prove useful

• It shows the keyboard scancode values

• It demonstrates ‘noncanonical’ tty mode

• It clears the ISIG bit (in ‘c_lflags’ field)

• This prevents <CONTROL>-C from being used to abort the program: the user must ‘quit’ by hitting the <ESCAPE>-key; so default terminal-settings will get reinstalled

‘Noncanonical’ terminal i/o

• We’ve now learned how to reprogram the terminal to allow “raw” keyboard input#include <termios.h>struct termios tty;tcgetattr( 0, &tty ); // get tty settingstty.c_lflag &= ~( ICANON | ECHO | ISIG );tty.c_cc[ VMIN ] = 1; tty.c_cc[ VTIME ] = 0;tcsetattr( 0, TCSAFLUSH, &tty ); // install

Handling a key-press

• Here’s a ‘simple’ signal-handler that lets a user decide to terminate our program (by hitting the <ESCAPE>-key) instead of the program itself deciding to quit when a counter reaches zero

void on_input( int signum ){

int inch = 0;read( 0, &inch, 4 );if ( inch == ESCAPE_KEY ) done = 1;

}

Enabling ‘asynchronous’ I/O

• Now we need to install our signal-handler, specify which program will receive SIGIO, then enable the delivery of these signals

• signal( SIGIO, on_input );

• fcntl( 0, F_SETOWN, getpid() );

• int flagval = fcntl( 0, F_GETFL, NULL );

• flagval |= O_ASYNC; // turn on flag-bit

• fcntl( 0, F_SETFL, flagval );

‘animate2.cpp’

HandlesUser’s

keystrokes(SIGIO)

HandlesTimer’s

expirations(SIGALRM)

on_input on_alarm

Handlesredrawingwhenever

Vertical RetraceIs active

main_loop

The Signal-HandlersWaiting and Drawing

Program-loop revised again

done = 0;do {

oldx = xloc, oldy = yloc; // remember thesedraw_ball(); // use current locationvsync(); // await next retracehide_ball(); // erase previous ball}

while ( !done );// ‘xloc’, ‘yloc’, and ‘done’ get changed by handlers

Enhancment: more user-control

• In ‘pong’ game the user moves a ‘paddle’

• The paddle can only be moved left or right

• Lower wall of the playing-court is removed

• Ball will “escape” unless it hits the paddle

• Keyboard has left and right “arrow keys”

• Our input signal-handler could “move” the paddle whenever a user hits an arrow-key

In-class exercise #1

• Before you remove the game-court’s lower wall, see if you can implement the paddle-movements (left or right) in response to a user’s hitting the left-arrow or right-arrow

• You will need to investigate the numerical code-sequence that Linux generates when a user hits one of these arrow-keys

• Our ‘rawtty.cpp’ application may be useful!

In-class exercise #2

• For brevity, our ‘animate2’ demo-program removed the capability of users controlling the speed of the ball-movements (with an argument supplied on the command-line)

• Can you devise a way for users to exert “run-time” control over the ball’s speed by pressing keys while the program is running (for example, the ‘+’ key and the ‘-’ key)?