linux game programming an introduction to the use of interval timers and asynchronous input...

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

Post on 22-Dec-2015

225 views

Category:

Documents


2 download

TRANSCRIPT

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)?