1 <HTML 2 ><HEAD 3 ><TITLE 4 >Handling the Keyboard</TITLE 5 ><META 6 NAME="GENERATOR" 7 CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+ 8 "><LINK 9 REL="HOME" 10 TITLE="SDL Library Documentation" 11 HREF="index.html"><LINK 12 REL="UP" 13 TITLE="Input handling" 14 HREF="guideinput.html"><LINK 15 REL="PREVIOUS" 16 TITLE="Input handling" 17 HREF="guideinput.html"><LINK 18 REL="NEXT" 19 TITLE="Examples" 20 HREF="guideexamples.html"></HEAD 21 ><BODY 22 CLASS="SECT1" 23 BGCOLOR="#FFF8DC" 24 TEXT="#000000" 25 LINK="#0000ee" 26 VLINK="#551a8b" 27 ALINK="#ff0000" 28 ><DIV 29 CLASS="NAVHEADER" 30 ><TABLE 31 SUMMARY="Header navigation table" 32 WIDTH="100%" 33 BORDER="0" 34 CELLPADDING="0" 35 CELLSPACING="0" 36 ><TR 37 ><TH 38 COLSPAN="3" 39 ALIGN="center" 40 >SDL Library Documentation</TH 41 ></TR 42 ><TR 43 ><TD 44 WIDTH="10%" 45 ALIGN="left" 46 VALIGN="bottom" 47 ><A 48 HREF="guideinput.html" 49 ACCESSKEY="P" 50 >Prev</A 51 ></TD 52 ><TD 53 WIDTH="80%" 54 ALIGN="center" 55 VALIGN="bottom" 56 >Chapter 3. Input handling</TD 57 ><TD 58 WIDTH="10%" 59 ALIGN="right" 60 VALIGN="bottom" 61 ><A 62 HREF="guideexamples.html" 63 ACCESSKEY="N" 64 >Next</A 65 ></TD 66 ></TR 67 ></TABLE 68 ><HR 69 ALIGN="LEFT" 70 WIDTH="100%"></DIV 71 ><DIV 72 CLASS="SECT1" 73 ><H1 74 CLASS="SECT1" 75 ><A 76 NAME="GUIDEINPUTKEYBOARD" 77 ></A 78 >Handling the Keyboard</H1 79 ><DIV 80 CLASS="SECT2" 81 ><H2 82 CLASS="SECT2" 83 ><A 84 NAME="AEN271" 85 ></A 86 >Keyboard Related Structures</H2 87 ><P 88 >It should make it a lot easier to understand this tutorial is you are familiar with the data types involved in keyboard access, so I'll explain them first.</P 89 ><DIV 90 CLASS="SECT3" 91 ><H3 92 CLASS="SECT3" 93 ><A 94 NAME="AEN274" 95 ></A 96 >SDLKey</H3 97 ><P 98 ><SPAN 99 CLASS="STRUCTNAME" 100 >SDLKey</SPAN 101 > is an enumerated type defined in SDL/include/SDL_keysym.h and detailed <A 102 HREF="sdlkey.html" 103 >here</A 104 >. Each <SPAN 105 CLASS="STRUCTNAME" 106 >SDLKey</SPAN 107 > symbol represents a key, <TT 108 CLASS="LITERAL" 109 >SDLK_a</TT 110 > corresponds to the 'a' key on a keyboard, <TT 111 CLASS="LITERAL" 112 >SDLK_SPACE</TT 113 > corresponds to the space bar, and so on.</P 114 ></DIV 115 ><DIV 116 CLASS="SECT3" 117 ><H3 118 CLASS="SECT3" 119 ><A 120 NAME="AEN282" 121 ></A 122 >SDLMod</H3 123 ><P 124 >SDLMod is an enumerated type, similar to <SPAN 125 CLASS="STRUCTNAME" 126 >SDLKey</SPAN 127 >, however it enumerates keyboard modifiers (Control, Alt, Shift). The full list of modifier symbols is <A 128 HREF="sdlkey.html#SDLMOD" 129 >here</A 130 >. <SPAN 131 CLASS="STRUCTNAME" 132 >SDLMod</SPAN 133 > values can be AND'd together to represent several modifiers.</P 134 ></DIV 135 ><DIV 136 CLASS="SECT3" 137 ><H3 138 CLASS="SECT3" 139 ><A 140 NAME="AEN288" 141 ></A 142 >SDL_keysym</H3 143 ><PRE 144 CLASS="PROGRAMLISTING" 145 >typedef struct{ 146 Uint8 scancode; 147 SDLKey sym; 148 SDLMod mod; 149 Uint16 unicode; 150 } SDL_keysym;</PRE 151 ><P 152 >The <SPAN 153 CLASS="STRUCTNAME" 154 >SDL_keysym</SPAN 155 > structure describes a key press or a key release. The <TT 156 CLASS="STRUCTFIELD" 157 ><I 158 >scancode</I 159 ></TT 160 > field is hardware specific and should be ignored unless you know what your doing. The <TT 161 CLASS="STRUCTFIELD" 162 ><I 163 >sym</I 164 ></TT 165 > field is the <SPAN 166 CLASS="STRUCTNAME" 167 >SDLKey</SPAN 168 > value of the key being pressed or released. The <TT 169 CLASS="STRUCTFIELD" 170 ><I 171 >mod</I 172 ></TT 173 > field describes the state of the keyboard modifiers at the time the key press or release occurred. So a value of <TT 174 CLASS="LITERAL" 175 >KMOD_NUM | KMOD_CAPS | KMOD_LSHIFT</TT 176 > would mean that Numlock, Capslock and the left shift key were all press (or enabled in the case of the lock keys). Finally, the <TT 177 CLASS="STRUCTFIELD" 178 ><I 179 >unicode</I 180 ></TT 181 > field stores the 16-bit unicode value of the key.</P 182 ><DIV 183 CLASS="NOTE" 184 ><BLOCKQUOTE 185 CLASS="NOTE" 186 ><P 187 ><B 188 >Note: </B 189 >It should be noted and understood that this field is only valid when the <SPAN 190 CLASS="STRUCTNAME" 191 >SDL_keysym</SPAN 192 > is describing a key press, not a key release. Unicode values only make sense on a key press because the unicode value describes an international character and only key presses produce characters. More information on Unicode can be found at <A 193 HREF="http://www.unicode.org" 194 TARGET="_top" 195 >www.unicode.org</A 196 ></P 197 ></BLOCKQUOTE 198 ></DIV 199 ><DIV 200 CLASS="NOTE" 201 ><BLOCKQUOTE 202 CLASS="NOTE" 203 ><P 204 ><B 205 >Note: </B 206 >Unicode translation must be enabled using the <A 207 HREF="sdlenableunicode.html" 208 ><TT 209 CLASS="FUNCTION" 210 >SDL_EnableUNICODE</TT 211 ></A 212 > function.</P 213 ></BLOCKQUOTE 214 ></DIV 215 ></DIV 216 ><DIV 217 CLASS="SECT3" 218 ><H3 219 CLASS="SECT3" 220 ><A 221 NAME="AEN307" 222 ></A 223 >SDL_KeyboardEvent</H3 224 ><PRE 225 CLASS="PROGRAMLISTING" 226 >typedef struct{ 227 Uint8 type; 228 Uint8 state; 229 SDL_keysym keysym; 230 } SDL_KeyboardEvent;</PRE 231 ><P 232 >The <SPAN 233 CLASS="STRUCTNAME" 234 >SDL_KeyboardEvent</SPAN 235 > describes a keyboard event (obviously). The <TT 236 CLASS="STRUCTFIELD" 237 ><I 238 >key</I 239 ></TT 240 > member of the <A 241 HREF="sdlevent.html" 242 ><SPAN 243 CLASS="STRUCTNAME" 244 >SDL_Event</SPAN 245 ></A 246 > union is a <SPAN 247 CLASS="STRUCTNAME" 248 >SDL_KeyboardEvent</SPAN 249 > structure. The <TT 250 CLASS="STRUCTFIELD" 251 ><I 252 >type</I 253 ></TT 254 > field specifies whether the event is a key release (<TT 255 CLASS="LITERAL" 256 >SDL_KEYUP</TT 257 >) or a key press (<TT 258 CLASS="LITERAL" 259 >SDL_KEYDOWN</TT 260 >) event. The <TT 261 CLASS="STRUCTFIELD" 262 ><I 263 >state</I 264 ></TT 265 > is largely redundant, it reports the same information as the <TT 266 CLASS="STRUCTFIELD" 267 ><I 268 >type</I 269 ></TT 270 > field but uses different values (<TT 271 CLASS="LITERAL" 272 >SDL_RELEASED</TT 273 > and <TT 274 CLASS="LITERAL" 275 >SDL_PRESSED</TT 276 >). The <TT 277 CLASS="STRUCTFIELD" 278 ><I 279 >keysym</I 280 ></TT 281 > contains information of the key press or release that this event represents (see above).</P 282 ></DIV 283 ></DIV 284 ><DIV 285 CLASS="SECT2" 286 ><H2 287 CLASS="SECT2" 288 ><A 289 NAME="AEN324" 290 ></A 291 >Reading Keyboard Events</H2 292 ><P 293 >Reading keybaord events from the event queue is quite simple (the event queue and using it is described <A 294 HREF="sdlevent.html" 295 >here</A 296 >). We read events using <A 297 HREF="sdlpollevent.html" 298 ><TT 299 CLASS="FUNCTION" 300 >SDL_PollEvent</TT 301 ></A 302 > in a <TT 303 CLASS="LITERAL" 304 >while()</TT 305 > loop and check for <TT 306 CLASS="LITERAL" 307 >SDL_KEYUP</TT 308 > and <TT 309 CLASS="LITERAL" 310 >SDL_KEYDOWN</TT 311 > events using a <TT 312 CLASS="LITERAL" 313 >switch</TT 314 > statement, like so:</P 315 ><DIV 316 CLASS="EXAMPLE" 317 ><A 318 NAME="AEN334" 319 ></A 320 ><P 321 ><B 322 >Example 3-10. Reading Keyboard Events</B 323 ></P 324 ><PRE 325 CLASS="PROGRAMLISTING" 326 > SDL_Event event; 327 . 328 . 329 /* Poll for events. SDL_PollEvent() returns 0 when there are no */ 330 /* more events on the event queue, our while loop will exit when */ 331 /* that occurs. */ 332 while( SDL_PollEvent( &event ) ){ 333 /* We are only worried about SDL_KEYDOWN and SDL_KEYUP events */ 334 switch( event.type ){ 335 case SDL_KEYDOWN: 336 printf( "Key press detected\n" ); 337 break; 338 339 case SDL_KEYUP: 340 printf( "Key release detected\n" ); 341 break; 342 343 default: 344 break; 345 } 346 } 347 . 348 .</PRE 349 ></DIV 350 ><P 351 >This is a very basic example. No information about the key press or release is interpreted. We will explore the other extreme out our first full example below - reporting all available information about a keyboard event.</P 352 ></DIV 353 ><DIV 354 CLASS="SECT2" 355 ><H2 356 CLASS="SECT2" 357 ><A 358 NAME="AEN338" 359 ></A 360 >A More Detailed Look</H2 361 ><P 362 >Before we can read events SDL must be initialised with <A 363 HREF="sdlinit.html" 364 ><TT 365 CLASS="FUNCTION" 366 >SDL_Init</TT 367 ></A 368 > and a video mode must be set using <A 369 HREF="sdlsetvideomode.html" 370 ><TT 371 CLASS="FUNCTION" 372 >SDL_SetVideoMode</TT 373 ></A 374 >. There are, however, two other functions we must use to obtain all the information required. We must enable unicode translation by calling <TT 375 CLASS="FUNCTION" 376 >SDL_EnableUNICODE(1)</TT 377 > and we must convert <SPAN 378 CLASS="STRUCTNAME" 379 >SDLKey</SPAN 380 > values into something printable, using <A 381 HREF="sdlgetkeyname.html" 382 ><TT 383 CLASS="FUNCTION" 384 >SDL_GetKeyName</TT 385 ></A 386 ></P 387 ><DIV 388 CLASS="NOTE" 389 ><BLOCKQUOTE 390 CLASS="NOTE" 391 ><P 392 ><B 393 >Note: </B 394 >It is useful to note that unicode values < 0x80 translate directly a characters ASCII value. THis is used in the example below</P 395 ></BLOCKQUOTE 396 ></DIV 397 ><DIV 398 CLASS="EXAMPLE" 399 ><A 400 NAME="AEN351" 401 ></A 402 ><P 403 ><B 404 >Example 3-11. Interpreting Key Event Information</B 405 ></P 406 ><PRE 407 CLASS="PROGRAMLISTING" 408 > #include "SDL.h" 409 410 /* Function Prototypes */ 411 void PrintKeyInfo( SDL_KeyboardEvent *key ); 412 void PrintModifiers( SDLMod mod ); 413 414 /* main */ 415 int main( int argc, char *argv[] ){ 416 417 SDL_Event event; 418 int quit = 0; 419 420 /* Initialise SDL */ 421 if( SDL_Init( SDL_INIT_VIDEO ) < 0){ 422 fprintf( stderr, "Could not initialise SDL: %s\n", SDL_GetError() ); 423 exit( -1 ); 424 } 425 426 /* Set a video mode */ 427 if( !SDL_SetVideoMode( 320, 200, 0, 0 ) ){ 428 fprintf( stderr, "Could not set video mode: %s\n", SDL_GetError() ); 429 SDL_Quit(); 430 exit( -1 ); 431 } 432 433 /* Enable Unicode translation */ 434 SDL_EnableUNICODE( 1 ); 435 436 /* Loop until an SDL_QUIT event is found */ 437 while( !quit ){ 438 439 /* Poll for events */ 440 while( SDL_PollEvent( &event ) ){ 441 442 switch( event.type ){ 443 /* Keyboard event */ 444 /* Pass the event data onto PrintKeyInfo() */ 445 case SDL_KEYDOWN: 446 case SDL_KEYUP: 447 PrintKeyInfo( &event.key ); 448 break; 449 450 /* SDL_QUIT event (window close) */ 451 case SDL_QUIT: 452 quit = 1; 453 break; 454 455 default: 456 break; 457 } 458 459 } 460 461 } 462 463 /* Clean up */ 464 SDL_Quit(); 465 exit( 0 ); 466 } 467 468 /* Print all information about a key event */ 469 void PrintKeyInfo( SDL_KeyboardEvent *key ){ 470 /* Is it a release or a press? */ 471 if( key->type == SDL_KEYUP ) 472 printf( "Release:- " ); 473 else 474 printf( "Press:- " ); 475 476 /* Print the hardware scancode first */ 477 printf( "Scancode: 0x%02X", key->keysym.scancode ); 478 /* Print the name of the key */ 479 printf( ", Name: %s", SDL_GetKeyName( key->keysym.sym ) ); 480 /* We want to print the unicode info, but we need to make */ 481 /* sure its a press event first (remember, release events */ 482 /* don't have unicode info */ 483 if( key->type == SDL_KEYDOWN ){ 484 /* If the Unicode value is less than 0x80 then the */ 485 /* unicode value can be used to get a printable */ 486 /* representation of the key, using (char)unicode. */ 487 printf(", Unicode: " ); 488 if( key->keysym.unicode < 0x80 && key->keysym.unicode > 0 ){ 489 printf( "%c (0x%04X)", (char)key->keysym.unicode, 490 key->keysym.unicode ); 491 } 492 else{ 493 printf( "? (0x%04X)", key->keysym.unicode ); 494 } 495 } 496 printf( "\n" ); 497 /* Print modifier info */ 498 PrintModifiers( key->keysym.mod ); 499 } 500 501 /* Print modifier info */ 502 void PrintModifiers( SDLMod mod ){ 503 printf( "Modifers: " ); 504 505 /* If there are none then say so and return */ 506 if( mod == KMOD_NONE ){ 507 printf( "None\n" ); 508 return; 509 } 510 511 /* Check for the presence of each SDLMod value */ 512 /* This looks messy, but there really isn't */ 513 /* a clearer way. */ 514 if( mod & KMOD_NUM ) printf( "NUMLOCK " ); 515 if( mod & KMOD_CAPS ) printf( "CAPSLOCK " ); 516 if( mod & KMOD_LCTRL ) printf( "LCTRL " ); 517 if( mod & KMOD_RCTRL ) printf( "RCTRL " ); 518 if( mod & KMOD_RSHIFT ) printf( "RSHIFT " ); 519 if( mod & KMOD_LSHIFT ) printf( "LSHIFT " ); 520 if( mod & KMOD_RALT ) printf( "RALT " ); 521 if( mod & KMOD_LALT ) printf( "LALT " ); 522 if( mod & KMOD_CTRL ) printf( "CTRL " ); 523 if( mod & KMOD_SHIFT ) printf( "SHIFT " ); 524 if( mod & KMOD_ALT ) printf( "ALT " ); 525 printf( "\n" ); 526 }</PRE 527 ></DIV 528 ></DIV 529 ><DIV 530 CLASS="SECT2" 531 ><H2 532 CLASS="SECT2" 533 ><A 534 NAME="AEN354" 535 ></A 536 >Game-type Input</H2 537 ><P 538 >I have found that people using keyboard events for games and other interactive applications don't always understand one fundemental point.</P 539 ><A 540 NAME="AEN357" 541 ></A 542 ><BLOCKQUOTE 543 CLASS="BLOCKQUOTE" 544 ><P 545 >Keyboard events <SPAN 546 CLASS="emphasis" 547 ><I 548 CLASS="EMPHASIS" 549 >only</I 550 ></SPAN 551 > take place when a keys state changes from being unpressed to pressed, and vice versa.</P 552 ></BLOCKQUOTE 553 ><P 554 >Imagine you have an image of an alien that you wish to move around using the cursor keys: when you pressed the left arrow key you want him to slide over to the left, and when you press the down key you want him to slide down the screen. Examine the following code; it highlights an error that many people have made. 555 <PRE 556 CLASS="PROGRAMLISTING" 557 > /* Alien screen coordinates */ 558 int alien_x=0, alien_y=0; 559 . 560 . 561 /* Initialise SDL and video modes and all that */ 562 . 563 /* Main game loop */ 564 /* Check for events */ 565 while( SDL_PollEvent( &event ) ){ 566 switch( event.type ){ 567 /* Look for a keypress */ 568 case SDL_KEYDOWN: 569 /* Check the SDLKey values and move change the coords */ 570 switch( event.key.keysym.sym ){ 571 case SDLK_LEFT: 572 alien_x -= 1; 573 break; 574 case SDLK_RIGHT: 575 alien_x += 1; 576 break; 577 case SDLK_UP: 578 alien_y -= 1; 579 break; 580 case SDLK_DOWN: 581 alien_y += 1; 582 break; 583 default: 584 break; 585 } 586 } 587 } 588 } 589 . 590 .</PRE 591 > 592 At first glance you may think this is a perfectly reasonable piece of code for the task, but it isn't. Like I said keyboard events only occur when a key changes state, so the user would have to press and release the left cursor key 100 times to move the alien 100 pixels to the left.</P 593 ><P 594 >To get around this problem we must not use the events to change the position of the alien, we use the events to set flags which are then used in a seperate section of code to move the alien. Something like this:</P 595 ><DIV 596 CLASS="EXAMPLE" 597 ><A 598 NAME="AEN363" 599 ></A 600 ><P 601 ><B 602 >Example 3-12. Proper Game Movement</B 603 ></P 604 ><PRE 605 CLASS="PROGRAMLISTING" 606 > /* Alien screen coordinates */ 607 int alien_x=0, alien_y=0; 608 int alien_xvel=0, alien_yvel=0; 609 . 610 . 611 /* Initialise SDL and video modes and all that */ 612 . 613 /* Main game loop */ 614 /* Check for events */ 615 while( SDL_PollEvent( &event ) ){ 616 switch( event.type ){ 617 /* Look for a keypress */ 618 case SDL_KEYDOWN: 619 /* Check the SDLKey values and move change the coords */ 620 switch( event.key.keysym.sym ){ 621 case SDLK_LEFT: 622 alien_xvel = -1; 623 break; 624 case SDLK_RIGHT: 625 alien_xvel = 1; 626 break; 627 case SDLK_UP: 628 alien_yvel = -1; 629 break; 630 case SDLK_DOWN: 631 alien_yvel = 1; 632 break; 633 default: 634 break; 635 } 636 break; 637 /* We must also use the SDL_KEYUP events to zero the x */ 638 /* and y velocity variables. But we must also be */ 639 /* careful not to zero the velocities when we shouldn't*/ 640 case SDL_KEYUP: 641 switch( event.key.keysym.sym ){ 642 case SDLK_LEFT: 643 /* We check to make sure the alien is moving */ 644 /* to the left. If it is then we zero the */ 645 /* velocity. If the alien is moving to the */ 646 /* right then the right key is still press */ 647 /* so we don't tocuh the velocity */ 648 if( alien_xvel < 0 ) 649 alien_xvel = 0; 650 break; 651 case SDLK_RIGHT: 652 if( alien_xvel > 0 ) 653 alien_xvel = 0; 654 break; 655 case SDLK_UP: 656 if( alien_yvel < 0 ) 657 alien_yvel = 0; 658 break; 659 case SDLK_DOWN: 660 if( alien_yvel > 0 ) 661 alien_yvel = 0; 662 break; 663 default: 664 break; 665 } 666 break; 667 668 default: 669 break; 670 } 671 } 672 . 673 . 674 /* Update the alien position */ 675 alien_x += alien_xvel; 676 alien_y += alien_yvel;</PRE 677 ></DIV 678 ><P 679 >As can be seen, we use two extra variables, alien_xvel and alien_yvel, which represent the motion of the ship, it is these variables that we update when we detect keypresses and releases.</P 680 ></DIV 681 ></DIV 682 ><DIV 683 CLASS="NAVFOOTER" 684 ><HR 685 ALIGN="LEFT" 686 WIDTH="100%"><TABLE 687 SUMMARY="Footer navigation table" 688 WIDTH="100%" 689 BORDER="0" 690 CELLPADDING="0" 691 CELLSPACING="0" 692 ><TR 693 ><TD 694 WIDTH="33%" 695 ALIGN="left" 696 VALIGN="top" 697 ><A 698 HREF="guideinput.html" 699 ACCESSKEY="P" 700 >Prev</A 701 ></TD 702 ><TD 703 WIDTH="34%" 704 ALIGN="center" 705 VALIGN="top" 706 ><A 707 HREF="index.html" 708 ACCESSKEY="H" 709 >Home</A 710 ></TD 711 ><TD 712 WIDTH="33%" 713 ALIGN="right" 714 VALIGN="top" 715 ><A 716 HREF="guideexamples.html" 717 ACCESSKEY="N" 718 >Next</A 719 ></TD 720 ></TR 721 ><TR 722 ><TD 723 WIDTH="33%" 724 ALIGN="left" 725 VALIGN="top" 726 >Input handling</TD 727 ><TD 728 WIDTH="34%" 729 ALIGN="center" 730 VALIGN="top" 731 ><A 732 HREF="guideinput.html" 733 ACCESSKEY="U" 734 >Up</A 735 ></TD 736 ><TD 737 WIDTH="33%" 738 ALIGN="right" 739 VALIGN="top" 740 >Examples</TD 741 ></TR 742 ></TABLE 743 ></DIV 744 ></BODY 745 ></HTML 746 >