Mercurial > sdl-ios-xcode
comparison docs/html/guideinput.html @ 55:55f1f1b3e27d
Added new docs for SDL 1.2.1
author | Sam Lantinga <slouken@lokigames.com> |
---|---|
date | Sun, 10 Jun 2001 19:31:57 +0000 |
parents | 74212992fb08 |
children | e5bc29de3f0a |
comparison
equal
deleted
inserted
replaced
54:028447a8a758 | 55:55f1f1b3e27d |
---|---|
2 ><HEAD | 2 ><HEAD |
3 ><TITLE | 3 ><TITLE |
4 >Input handling</TITLE | 4 >Input handling</TITLE |
5 ><META | 5 ><META |
6 NAME="GENERATOR" | 6 NAME="GENERATOR" |
7 CONTENT="Modular DocBook HTML Stylesheet Version 1.61 | 7 CONTENT="Modular DocBook HTML Stylesheet Version 1.64 |
8 "><LINK | 8 "><LINK |
9 REL="HOME" | 9 REL="HOME" |
10 TITLE="SDL Library Documentation" | 10 TITLE="SDL Library Documentation" |
11 HREF="index.html"><LINK | 11 HREF="index.html"><LINK |
12 REL="UP" | 12 REL="UP" |
13 TITLE="SDL Guide" | 13 TITLE="SDL Guide" |
14 HREF="guide.html"><LINK | 14 HREF="guide.html"><LINK |
15 REL="PREVIOUS" | 15 REL="PREVIOUS" |
16 TITLE="Graphics and Video" | 16 TITLE="Using OpenGL With SDL" |
17 HREF="guidevideo.html"><LINK | 17 HREF="guidevideoopengl.html"><LINK |
18 REL="NEXT" | 18 REL="NEXT" |
19 TITLE="Handling the Keyboard" | 19 TITLE="Handling the Keyboard" |
20 HREF="guideinputkeyboard.html"></HEAD | 20 HREF="guideinputkeyboard.html"></HEAD |
21 ><BODY | 21 ><BODY |
22 CLASS="CHAPTER" | 22 CLASS="CHAPTER" |
42 ><TD | 42 ><TD |
43 WIDTH="10%" | 43 WIDTH="10%" |
44 ALIGN="left" | 44 ALIGN="left" |
45 VALIGN="bottom" | 45 VALIGN="bottom" |
46 ><A | 46 ><A |
47 HREF="guidevideo.html" | 47 HREF="guidevideoopengl.html" |
48 >Prev</A | 48 >Prev</A |
49 ></TD | 49 ></TD |
50 ><TD | 50 ><TD |
51 WIDTH="80%" | 51 WIDTH="80%" |
52 ALIGN="center" | 52 ALIGN="center" |
102 ><DIV | 102 ><DIV |
103 CLASS="SECT2" | 103 CLASS="SECT2" |
104 ><H2 | 104 ><H2 |
105 CLASS="SECT2" | 105 CLASS="SECT2" |
106 ><A | 106 ><A |
107 NAME="AEN94" | 107 NAME="AEN135" |
108 >Initialization</A | 108 >Initialization</A |
109 ></H2 | 109 ></H2 |
110 ><P | 110 ><P |
111 >The first step in using a joystick in a SDL program is to initialize the Joystick subsystems of SDL. This done by passing the <TT | 111 >The first step in using a joystick in a SDL program is to initialize the Joystick subsystems of SDL. This done by passing the <TT |
112 CLASS="LITERAL" | 112 CLASS="LITERAL" |
115 HREF="sdlinit.html" | 115 HREF="sdlinit.html" |
116 ><TT | 116 ><TT |
117 CLASS="FUNCTION" | 117 CLASS="FUNCTION" |
118 >SDL_Init</TT | 118 >SDL_Init</TT |
119 ></A | 119 ></A |
120 >. The joystick flag will usually be used in conjunction with other flags (like the video flag) because the joystick is usually used to control something. | 120 >. The joystick flag will usually be used in conjunction with other flags (like the video flag) because the joystick is usually used to control something.</P |
121 <PRE | 121 ><DIV |
122 CLASS="PROGRAMLISTING" | 122 CLASS="EXAMPLE" |
123 > if ( ! SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) ) | 123 ><A |
124 NAME="AEN141" | |
125 ></A | |
126 ><P | |
127 ><B | |
128 >Example 3-1. Initializing SDL with Joystick Support</B | |
129 ></P | |
130 ><PRE | |
131 CLASS="PROGRAMLISTING" | |
132 > if (SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0) | |
124 { | 133 { |
125 fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); | 134 fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); |
126 exit(1); | 135 exit(1); |
127 }</PRE | 136 }</PRE |
128 > | 137 ></DIV |
129 This will attempt to start SDL with both the video and the joystick subsystems activated.</P | 138 ><P |
139 >This will attempt to start SDL with both the video and the joystick subsystems activated.</P | |
130 ></DIV | 140 ></DIV |
131 ><DIV | 141 ><DIV |
132 CLASS="SECT2" | 142 CLASS="SECT2" |
133 ><H2 | 143 ><H2 |
134 CLASS="SECT2" | 144 CLASS="SECT2" |
135 ><A | 145 ><A |
136 NAME="AEN101" | 146 NAME="AEN145" |
137 >Querying</A | 147 >Querying</A |
138 ></H2 | 148 ></H2 |
139 ><P | 149 ><P |
140 >If we have reached this point then we can safely assume that the SDL library has been initialized and that the Joystick subsystem is active. We can now call some video and/or sound functions to get things going before we need the joystick. Eventually we have to make sure that there is actually a joystick to work with. It's wise to always check even if you know a joystick will be present on the system because it can also help detect when the joystick is unplugged. The function used to check for joysticks is <A | 150 >If we have reached this point then we can safely assume that the SDL library has been initialized and that the Joystick subsystem is active. We can now call some video and/or sound functions to get things going before we need the joystick. Eventually we have to make sure that there is actually a joystick to work with. It's wise to always check even if you know a joystick will be present on the system because it can also help detect when the joystick is unplugged. The function used to check for joysticks is <A |
141 HREF="sdlnumjoysticks.html" | 151 HREF="sdlnumjoysticks.html" |
152 >SDL_JoystickName</TT | 162 >SDL_JoystickName</TT |
153 ></A | 163 ></A |
154 >. The joystick is specified by an index where 0 is the first joystick and the last joystick is the number returned by <TT | 164 >. The joystick is specified by an index where 0 is the first joystick and the last joystick is the number returned by <TT |
155 CLASS="FUNCTION" | 165 CLASS="FUNCTION" |
156 >SDL_NumJoysticks</TT | 166 >SDL_NumJoysticks</TT |
157 > - 1. In the demonstration a list of all available joysticks is printed to stdout. | 167 > - 1. In the demonstration a list of all available joysticks is printed to stdout.</P |
158 <PRE | 168 ><DIV |
169 CLASS="EXAMPLE" | |
170 ><A | |
171 NAME="AEN154" | |
172 ></A | |
173 ><P | |
174 ><B | |
175 >Example 3-2. Querying the Number of Available Joysticks</B | |
176 ></P | |
177 ><PRE | |
159 CLASS="PROGRAMLISTING" | 178 CLASS="PROGRAMLISTING" |
160 > printf("%i joysticks were found.\n\n", SDL_NumJoysticks() ); | 179 > printf("%i joysticks were found.\n\n", SDL_NumJoysticks() ); |
161 printf("The names of the joysticks are:\n"); | 180 printf("The names of the joysticks are:\n"); |
162 | 181 |
163 for( i=0; i < SDL_NumJoysticks(); i++ ) | 182 for( i=0; i < SDL_NumJoysticks(); i++ ) |
164 { | 183 { |
165 printf(" %s\n", SDL_JoystickName(i)); | 184 printf(" %s\n", SDL_JoystickName(i)); |
166 }</PRE | 185 }</PRE |
167 ></P | 186 ></DIV |
168 ></DIV | 187 ></DIV |
169 ><DIV | 188 ><DIV |
170 CLASS="SECT2" | 189 CLASS="SECT2" |
171 ><H2 | 190 ><H2 |
172 CLASS="SECT2" | 191 CLASS="SECT2" |
173 ><A | 192 ><A |
174 NAME="AEN111" | 193 NAME="AEN157" |
175 >Opening a Joystick and Receiving Joystick Events</A | 194 >Opening a Joystick and Receiving Joystick Events</A |
176 ></H2 | 195 ></H2 |
177 ><P | 196 ><P |
178 >SDL's event driven architecture makes working with joysticks a snap. Joysticks can trigger 4 different types of events: | 197 >SDL's event driven architecture makes working with joysticks a snap. Joysticks can trigger 4 different types of events: |
179 <P | 198 <P |
248 HREF="sdljoystickopen.html" | 267 HREF="sdljoystickopen.html" |
249 ><TT | 268 ><TT |
250 CLASS="FUNCTION" | 269 CLASS="FUNCTION" |
251 >SDL_JoystickOpen</TT | 270 >SDL_JoystickOpen</TT |
252 ></A | 271 ></A |
253 > function. For the example we are only interested in events from the first joystick on the system, regardless of what it may be. To receive events from it we would do this: | 272 > function. For the example we are only interested in events from the first joystick on the system, regardless of what it may be. To receive events from it we would do this:</P |
254 <PRE | 273 ><DIV |
274 CLASS="EXAMPLE" | |
275 ><A | |
276 NAME="AEN183" | |
277 ></A | |
278 ><P | |
279 ><B | |
280 >Example 3-3. Opening a Joystick</B | |
281 ></P | |
282 ><PRE | |
255 CLASS="PROGRAMLISTING" | 283 CLASS="PROGRAMLISTING" |
256 > SDL_Joystick *joystick; | 284 > SDL_Joystick *joystick; |
257 | 285 |
258 SDL_JoystickEventState(SDL_ENABLE); | 286 SDL_JoystickEventState(SDL_ENABLE); |
259 joystick = SDL_JoystickOpen(0);</PRE | 287 joystick = SDL_JoystickOpen(0);</PRE |
260 > | 288 ></DIV |
261 If we wanted to receive events for other joysticks we would open them with calls to <TT | 289 ><P |
290 >If we wanted to receive events for other joysticks we would open them with calls to <TT | |
262 CLASS="FUNCTION" | 291 CLASS="FUNCTION" |
263 >SDL_JoystickOpen</TT | 292 >SDL_JoystickOpen</TT |
264 > just like we opened joystick 0, except we would store the <SPAN | 293 > just like we opened joystick 0, except we would store the <SPAN |
265 CLASS="STRUCTNAME" | 294 CLASS="STRUCTNAME" |
266 >SDL_Joystick</SPAN | 295 >SDL_Joystick</SPAN |
267 > structure they return in a different pointer. We only need the joystick pointer when we are querying the joysticks or when we are closing the joystick.</P | 296 > structure they return in a different pointer. We only need the joystick pointer when we are querying the joysticks or when we are closing the joystick.</P |
268 ><P | 297 ><P |
269 >Up to this point all the code we have is used just to initialize the joysticks in order to read values at run time. All we need now is an event loop, which is something that all SDL programs should have anyway to receive the systems quit events. We must now add code to check the event loop for at least some of the above mentioned events. Let's assume our event loop looks like this: | 298 >Up to this point all the code we have is used just to initialize the joysticks in order to read values at run time. All we need now is an event loop, which is something that all SDL programs should have anyway to receive the systems quit events. We must now add code to check the event loop for at least some of the above mentioned events. Let's assume our event loop looks like this: |
270 <PRE | 299 <PRE |
271 CLASS="PROGRAMLISTING" | 300 CLASS="PROGRAMLISTING" |
272 > SDL_Event *event; | 301 > SDL_Event event; |
273 /* Other initializtion code goes here */ | 302 /* Other initializtion code goes here */ |
274 | 303 |
275 /* Start main game loop here */ | 304 /* Start main game loop here */ |
276 | 305 |
277 while(SDL_PollEvent(&event)) | 306 while(SDL_PollEvent(&event)) |
289 } | 318 } |
290 } | 319 } |
291 | 320 |
292 /* End loop here */</PRE | 321 /* End loop here */</PRE |
293 > | 322 > |
294 To handle Joystick events we merely add cases for them, first we'll add axis handling code. Axis checks can get kinda of tricky because alot of the joystick events received are junk. Joystick axis have a tendency to vary just a little between polling due to the way they are designed. To compensate for this you have to set a threshold for changes and ignore the events that have'nt exceeded the threshold. 10% is usually a good threshold value. This sounds a lot more complicated than it is. Here is the Axis event handler: | 323 To handle Joystick events we merely add cases for them, first we'll add axis handling code. Axis checks can get kinda of tricky because alot of the joystick events received are junk. Joystick axis have a tendency to vary just a little between polling due to the way they are designed. To compensate for this you have to set a threshold for changes and ignore the events that have'nt exceeded the threshold. 10% is usually a good threshold value. This sounds a lot more complicated than it is. Here is the Axis event handler:</P |
295 <PRE | 324 ><DIV |
325 CLASS="EXAMPLE" | |
326 ><A | |
327 NAME="AEN191" | |
328 ></A | |
329 ><P | |
330 ><B | |
331 >Example 3-4. Joystick Axis Events</B | |
332 ></P | |
333 ><PRE | |
296 CLASS="PROGRAMLISTING" | 334 CLASS="PROGRAMLISTING" |
297 > case SDL_JOYAXISMOTION: /* Handle Joystick Motion */ | 335 > case SDL_JOYAXISMOTION: /* Handle Joystick Motion */ |
298 if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) ) | 336 if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) ) |
299 { | 337 { |
300 /* code goes here */ | 338 /* code goes here */ |
301 } | 339 } |
302 break;</PRE | 340 break;</PRE |
303 > | 341 ></DIV |
304 Another trick with axis events is that up-down and left-right movement are two different sets of axes. The most important axis is axis 0 (left-right) and axis 1 (up-down). To handle them seperatly in the code we do the following: | 342 ><P |
305 <PRE | 343 >Another trick with axis events is that up-down and left-right movement are two different sets of axes. The most important axis is axis 0 (left-right) and axis 1 (up-down). To handle them seperatly in the code we do the following:</P |
344 ><DIV | |
345 CLASS="EXAMPLE" | |
346 ><A | |
347 NAME="AEN195" | |
348 ></A | |
349 ><P | |
350 ><B | |
351 >Example 3-5. More Joystick Axis Events</B | |
352 ></P | |
353 ><PRE | |
306 CLASS="PROGRAMLISTING" | 354 CLASS="PROGRAMLISTING" |
307 > case SDL_JOYAXISMOTION: /* Handle Joystick Motion */ | 355 > case SDL_JOYAXISMOTION: /* Handle Joystick Motion */ |
308 if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) ) | 356 if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) ) |
309 { | 357 { |
310 if( event.jaxis.axis == 0) | 358 if( event.jaxis.axis == 0) |
316 { | 364 { |
317 /* Up-Down movement code goes here */ | 365 /* Up-Down movement code goes here */ |
318 } | 366 } |
319 } | 367 } |
320 break;</PRE | 368 break;</PRE |
321 > | 369 ></DIV |
322 Ideally the code here should use <TT | 370 ><P |
371 >Ideally the code here should use <TT | |
323 CLASS="STRUCTFIELD" | 372 CLASS="STRUCTFIELD" |
324 ><I | 373 ><I |
325 >event.jaxis.value</I | 374 >event.jaxis.value</I |
326 ></TT | 375 ></TT |
327 > to scale something. For example lets assume you are using the joystick to control the movement of a spaceship. If the user is using an analog joystick and they push the stick a little bit they expect to move less than if they pushed it a lot. Designing your code for this situation is preferred because it makes the experience for users of analog controls better and remains the same for users of digital controls.</P | 376 > to scale something. For example lets assume you are using the joystick to control the movement of a spaceship. If the user is using an analog joystick and they push the stick a little bit they expect to move less than if they pushed it a lot. Designing your code for this situation is preferred because it makes the experience for users of analog controls better and remains the same for users of digital controls.</P |
331 ><I | 380 ><I |
332 >event.jaxis.axis</I | 381 >event.jaxis.axis</I |
333 ></TT | 382 ></TT |
334 > values.</P | 383 > values.</P |
335 ><P | 384 ><P |
336 >Button handling is simple compared to the axis checking. | 385 >Button handling is simple compared to the axis checking.</P |
337 <PRE | 386 ><DIV |
387 CLASS="EXAMPLE" | |
388 ><A | |
389 NAME="AEN203" | |
390 ></A | |
391 ><P | |
392 ><B | |
393 >Example 3-6. Joystick Button Events</B | |
394 ></P | |
395 ><PRE | |
338 CLASS="PROGRAMLISTING" | 396 CLASS="PROGRAMLISTING" |
339 > case SDL_JOYBUTTONDOWN: /* Handle Joystick Button Presses */ | 397 > case SDL_JOYBUTTONDOWN: /* Handle Joystick Button Presses */ |
340 if ( event.jbutton.button == 0 ) | 398 if ( event.jbutton.button == 0 ) |
341 { | 399 { |
342 /* code goes here */ | 400 /* code goes here */ |
343 } | 401 } |
344 break;</PRE | 402 break;</PRE |
345 > | 403 ></DIV |
346 | 404 ><P |
347 Button checks are simpler than axis checks because a button can only be pressed or not pressed. The <TT | 405 >Button checks are simpler than axis checks because a button can only be pressed or not pressed. The <TT |
348 CLASS="LITERAL" | 406 CLASS="LITERAL" |
349 >SDL_JOYBUTTONDOWN</TT | 407 >SDL_JOYBUTTONDOWN</TT |
350 > event is triggered when a button is pressed and the <TT | 408 > event is triggered when a button is pressed and the <TT |
351 CLASS="LITERAL" | 409 CLASS="LITERAL" |
352 >SDL_JOYBUTTONUP</TT | 410 >SDL_JOYBUTTONUP</TT |
372 ><DIV | 430 ><DIV |
373 CLASS="SECT2" | 431 CLASS="SECT2" |
374 ><H2 | 432 ><H2 |
375 CLASS="SECT2" | 433 CLASS="SECT2" |
376 ><A | 434 ><A |
377 NAME="AEN156" | 435 NAME="AEN214" |
378 >Advanced Joystick Functions</A | 436 >Advanced Joystick Functions</A |
379 ></H2 | 437 ></H2 |
380 ><P | 438 ><P |
381 >That takes care of the controls that you can count on being on every joystick under the sun, but there are a few extra things that SDL can support. Joyballs are next on our list, they are alot like axis we a few minor differences. Joyballs store relative changes unlike the the absolute postion stored in a axis event. Also one trackball event contains both the change in x and they change in y. Our case for it is as follows: | 439 >That takes care of the controls that you can count on being on every joystick under the sun, but there are a few extra things that SDL can support. Joyballs are next on our list, they are alot like axis we a few minor differences. Joyballs store relative changes unlike the the absolute postion stored in a axis event. Also one trackball event contains both the change in x and they change in y. Our case for it is as follows:</P |
382 <PRE | 440 ><DIV |
441 CLASS="EXAMPLE" | |
442 ><A | |
443 NAME="AEN217" | |
444 ></A | |
445 ><P | |
446 ><B | |
447 >Example 3-7. Joystick Ball Events</B | |
448 ></P | |
449 ><PRE | |
383 CLASS="PROGRAMLISTING" | 450 CLASS="PROGRAMLISTING" |
384 > case SDL_JOYBALLMOTION: /* Handle Joyball Motion */ | 451 > case SDL_JOYBALLMOTION: /* Handle Joyball Motion */ |
385 if( event.jball.ball == 0 ) | 452 if( event.jball.ball == 0 ) |
386 { | 453 { |
387 /* ball handling */ | 454 /* ball handling */ |
388 } | 455 } |
389 break;</PRE | 456 break;</PRE |
390 > | 457 ></DIV |
391 The above checks the first joyball on the joystick. The change in position will be stored in <TT | 458 ><P |
459 >The above checks the first joyball on the joystick. The change in position will be stored in <TT | |
392 CLASS="STRUCTFIELD" | 460 CLASS="STRUCTFIELD" |
393 ><I | 461 ><I |
394 >event.jball.xrel</I | 462 >event.jball.xrel</I |
395 ></TT | 463 ></TT |
396 > and <TT | 464 > and <TT |
486 ></TABLE | 554 ></TABLE |
487 ><P | 555 ><P |
488 ></P | 556 ></P |
489 > | 557 > |
490 | 558 |
491 Our case for the hat may resemble the following: | 559 Our case for the hat may resemble the following:</P |
492 | 560 ><DIV |
493 <PRE | 561 CLASS="EXAMPLE" |
562 ><A | |
563 NAME="AEN244" | |
564 ></A | |
565 ><P | |
566 ><B | |
567 >Example 3-8. Joystick Hat Events</B | |
568 ></P | |
569 ><PRE | |
494 CLASS="PROGRAMLISTING" | 570 CLASS="PROGRAMLISTING" |
495 > case SDL_JOYHATMOTION: /* Handle Hat Motion */ | 571 > case SDL_JOYHATMOTION: /* Handle Hat Motion */ |
496 if ( event.jhat.hat | SDL_HAT_UP ) | 572 if ( event.jhat.hat | SDL_HAT_UP ) |
497 { | 573 { |
498 /* Do up stuff here */ | 574 /* Do up stuff here */ |
506 if ( event.jhat.hat | SDL_HAT_RIGHTDOWN ) | 582 if ( event.jhat.hat | SDL_HAT_RIGHTDOWN ) |
507 { | 583 { |
508 /* Do right and down together stuff here */ | 584 /* Do right and down together stuff here */ |
509 } | 585 } |
510 break;</PRE | 586 break;</PRE |
511 ></P | 587 ></DIV |
512 ><P | 588 ><P |
513 >In addition to the queries for number of joysticks on the system and their names there are additional functions to query the capabilities of attached joysticks: | 589 >In addition to the queries for number of joysticks on the system and their names there are additional functions to query the capabilities of attached joysticks: |
514 <P | 590 <P |
515 ></P | 591 ></P |
516 ><TABLE | 592 ><TABLE |
568 ></TABLE | 644 ></TABLE |
569 ><P | 645 ><P |
570 ></P | 646 ></P |
571 > | 647 > |
572 | 648 |
573 To use these functions we just have to pass in the joystick structure we got when we opened the joystick. For Example: | 649 To use these functions we just have to pass in the joystick structure we got when we opened the joystick. For Example:</P |
574 | 650 ><DIV |
575 <PRE | 651 CLASS="EXAMPLE" |
652 ><A | |
653 NAME="AEN265" | |
654 ></A | |
655 ><P | |
656 ><B | |
657 >Example 3-9. Querying Joystick Characteristics</B | |
658 ></P | |
659 ><PRE | |
576 CLASS="PROGRAMLISTING" | 660 CLASS="PROGRAMLISTING" |
577 > int number_of_buttons; | 661 > int number_of_buttons; |
578 SDL_Joystick *joystick; | 662 SDL_Joystick *joystick; |
579 | 663 |
580 joystick = SDL_JoystickOpen(0); | 664 joystick = SDL_JoystickOpen(0); |
581 number_of_buttons = SDL_JoystickNumButtons(joystick);</PRE | 665 number_of_buttons = SDL_JoystickNumButtons(joystick);</PRE |
582 > | 666 ></DIV |
583 | 667 ><P |
584 This block of code would get the number of buttons on the first joystick in the system. </P | 668 >This block of code would get the number of buttons on the first joystick in the system. </P |
585 ></DIV | 669 ></DIV |
586 ></DIV | 670 ></DIV |
587 ></DIV | 671 ></DIV |
588 ><DIV | 672 ><DIV |
589 CLASS="NAVFOOTER" | 673 CLASS="NAVFOOTER" |
598 ><TD | 682 ><TD |
599 WIDTH="33%" | 683 WIDTH="33%" |
600 ALIGN="left" | 684 ALIGN="left" |
601 VALIGN="top" | 685 VALIGN="top" |
602 ><A | 686 ><A |
603 HREF="guidevideo.html" | 687 HREF="guidevideoopengl.html" |
604 >Prev</A | 688 >Prev</A |
605 ></TD | 689 ></TD |
606 ><TD | 690 ><TD |
607 WIDTH="34%" | 691 WIDTH="34%" |
608 ALIGN="center" | 692 ALIGN="center" |
623 ><TR | 707 ><TR |
624 ><TD | 708 ><TD |
625 WIDTH="33%" | 709 WIDTH="33%" |
626 ALIGN="left" | 710 ALIGN="left" |
627 VALIGN="top" | 711 VALIGN="top" |
628 >Graphics and Video</TD | 712 >Using OpenGL With SDL</TD |
629 ><TD | 713 ><TD |
630 WIDTH="34%" | 714 WIDTH="34%" |
631 ALIGN="center" | 715 ALIGN="center" |
632 VALIGN="top" | 716 VALIGN="top" |
633 ><A | 717 ><A |