Title: Linux game programming
1Linux game programming
- An introduction to the use of interval timers and
asynchronous input notifications
2Our 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 thats 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?
3Decoupling 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 gt 0 )
- So ball movement is delayed by vertical retraces
4Linux provides interval timers
- include ltsys/time.hgt
- 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)
5structs timeval and itimerval
struct timeval
tv_sec
tv_usec
struct itimerval
tv_sec
tv_usec
it_interval
tv_sec
tv_usec
it_value
It_itimerval next delay value, it_timeval
current delay value
6SIGALRM
- 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
7Our 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)
8Main 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 gt 0 )
- Ball-movement is managed by signal-handler
9draw versus move
Signal-handler
Program-loop
The code in this signal-handler takes care of
all movements whenever its 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
10Giving 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
11The 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 ltENTERgt-key signals an end-of-line
- Programs receive input one-line-at-a-time
12tty customization
- Sometimes canonical mode isnt suitable (an
example animated computer games) - The terminals behavior can be modified!
- UNIX provides a convenient interface
- include lttermios.hgt
- struct termios tty
- int tcgetattr( int fd, struct termios tty )
- int tcsetattr( int fd, int flag, struct termios
tty )
13How does the tty work?
application
User space
tty_driver c_lflag
Kernel space
input handling c_iflag c_cc
output handling c_oflag
SOFTWARE
struct tty c_iflag c_oflag
c_cflag c_lflag c_line
c_cc
terminal_driver c_cflag
TeleTYpe display device
HARDWARE
14The 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 UNIXs names
- Though actual symbol values may differ
- Your C/C program should use
- include lttermios.hgt
- for portability to other UNIX environments
15ICANON 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
16The 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
17How to setup raw terminal-mode
- Step 1 Use tcgetattr() to get a copy of the
current ttys 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
18raw 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
19Demo 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 ltCONTROLgt-C from being used to
abort the program the user must quit by
hitting the ltESCAPEgt-key so default
terminal-settings will get reinstalled
20Noncanonical terminal i/o
- Weve now learned how to reprogram the terminal
to allow raw keyboard input - include lttermios.hgt
- 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
21Handling a key-press
- Heres a simple signal-handler that lets a user
decide to terminate our program (by hitting the
ltESCAPEgt-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
-
22Enabling 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 )
23animate2.cpp
main_loop
on_input
on_alarm
Handles redrawing whenever Vertical Retrace Is
active
Handles Users keystrokes (SIGIO)
Handles Timers expirations (SIGALRM)
The Signal-Handlers
Waiting and Drawing
24Program-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
25Enhancment 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
26In-class exercise 1
- Before you remove the game-courts lower wall,
see if you can implement the paddle-movements
(left or right) in response to a users 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!
27In-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 balls speed by
pressing keys while the program is running (for
example, the key and the - key)?