lesson8.ppt

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 itimerval itval, 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
struct timeval
tv_sec
tv_usec
struct itimerval
it_interval
tv_sec
tv_usec
it_value
tv_sec
tv_usec
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 variables
ball_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’
Signal-handler
Program-loop
The code in this
signal-handler
takes care of
all movements
whenever it’s
time for them
to occur
The code in this
loop handles
all the actual
re-drawing
in sync with the
vertical retrace
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?
SOFTWARE
application
User space
tty_driver
c_lflag
Kernel space
input handling
c_iflag
c_cc
output handling
c_oflag
terminal_driver
c_cflag
HARDWARE
TeleTYpe display device
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 echo
tty.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;
• tty.c_lflag &= ~ICANON;
// no echoing
// 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 settings
tty.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’
on_input
Handles
User’s
keystrokes
(SIGIO)
on_alarm
Handles
Timer’s
expirations
(SIGALRM)
main_loop
Handles
redrawing
whenever
Vertical Retrace
Is active
The Signal-Handlers
Waiting and Drawing
Program-loop revised again
done = 0;
do {
oldx = xloc, oldy = yloc; // remember these
draw_ball();
// use current location
vsync();
// await next retrace
hide_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 paddlemovements (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)?