Search code examples
keyboardlocalesdl-2

capture double quote on KEYDOWN event with SDL2 in a locale independent way


I'm on Linux, but solution should work on Win and macOS too.
I want to capture 'double quote' on KEYDOWN event with SDL2.
I want to capture it in a locale independent way.
For example " is SHIFT+' on US keyboard but is SHIFT+2 in IT keyvboard.

After SDL_Init() function SDL_GetScancodeFromKey(SDLK_QUOTEDBL) always return 0, this seems confirmed from table: http://wiki.libsdl.org/SDL_Keycode

Worse seems that event.key.keysym.sym is never equal to SDLK_QUOTEDBL, while pressing 'double quote', event.key.keysym.sym is equal to "'" on US locale and "2" on IT locale.

I saw that event.key.keysym.mod is 4096 without SHIFT and 4098 with SHIFT pressed, so knowing the locale and keyboard layout is possible to catch the double quote, capturing '2' or ''' (or other key on keybords), but I had to discover the active locale and map myself all keyboard layouts.

Is there a better/easy way to read 'double quote' SDL2 event in a locale independent manner?

Here below a code example I used to test it:

#include <SDL2/SDL.h>

#define MAINWINWIDTH  1280
#define MAINWINHEIGHT 1024
#define COLORDEPTH    32
#define BYTESPERPIXEL (COLORDEPTH/8) // 4
#define SC_BLACK          0x00000000ul
#define SC_CYAN           0x0000C0C0ul
#define SP_WIDTH  256
#define SP_HEIGHT 192
#define SP_ZOOM 2
#define SS_WIDTH_SPIXELS  (SP_WIDTH*SP_ZOOM)
#define SS_HEIGHT_SPIXELS (SP_HEIGHT*SP_ZOOM)
#define BORDERWIDTH   4
#define OBJWINWIDTH   (56*8) // 448: 56 column, 8 pixel/char
#define LOGWINWIDTH   (MAINWINWIDTH - OBJWINWIDTH - GAMEWINWIDTH - 10*BORDERWIDTH) // 280
#define GAMEWINPOSX   (LOGWINWIDTH + 5*BORDERWIDTH) // 300
#define GAMEWINPOSY   (2*BORDERWIDTH) // 8
#define GAMEWINWIDTH  SS_WIDTH_SPIXELS  // 512
#define GAMEWINHEIGHT SS_HEIGHT_SPIXELS // 384

#define compA(color) ((color>>24)&0xFF)
#define compR(color) ((color>>16)&0xFF)
#define compG(color) ((color>>8)&0xFF)
#define compB(color) ((color>>0)&0xFF)
#define components(color) compR(color),compG(color),compB(color),compA(color)

struct TextWindowStruct {
    SDL_Texture* texPtr;
    Uint32* framePtr;
    int frameSize;
    SDL_Rect rect;
    int pitch;
    int CurrentPrintPosX;
    int CurrentPrintPosY;
};

int main (int argNum, char* argVect[]) {
   /* ...*/
   SDL_Window*   winPtr;
   SDL_Renderer* renPtr;
   struct TextWindowStruct GameWin;
   int WinMode;
   SDL_Event event;
   SDL_Scancode CurrentPressedCod;
   SDL_Keycode  CurrentPressedKey=0;
   Uint16       CurrentPressedMod;
   int RunMainLoop;

   WinMode = 0; // SDL_WINDOW_FULLSCREEN_DESKTOP;
   SDL_Init(SDL_INIT_VIDEO);
   winPtr = SDL_CreateWindow("Game", 110, 25, MAINWINWIDTH, MAINWINHEIGHT, WinMode | SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL|SDL_WINDOW_ALLOW_HIGHDPI);
   renPtr = SDL_CreateRenderer (winPtr, -1, SDL_RENDERER_ACCELERATED);
   SDL_SetHint (SDL_HINT_RENDER_SCALE_QUALITY, "linear");
   SDL_RenderSetLogicalSize (renPtr, MAINWINWIDTH, MAINWINHEIGHT);
   SDL_SetWindowSize (winPtr, MAINWINWIDTH, MAINWINHEIGHT);
   SDL_SetRenderDrawColor(renPtr, components(SDL_ALPHA_OPAQUE<<24|SC_CYAN)); // White. Black:R,G,B=0, Alpha=full. SDL_ALPHA_OPAQUE
   SDL_RenderClear(renPtr);
   
   GameWin.texPtr = SDL_CreateTexture (renPtr, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, GAMEWINWIDTH, GAMEWINHEIGHT);
   if (GameWin.texPtr == NULL) {
      fprintf (stderr, "SDL_CreateTexture Error: %s\n", SDL_GetError());
      SDL_DestroyRenderer (renPtr);
      SDL_DestroyWindow (winPtr);
      SDL_Quit ();
      return 1;
   }
   GameWin.pitch = GAMEWINWIDTH * BYTESPERPIXEL;
   GameWin.frameSize = GameWin.pitch * GAMEWINHEIGHT;
   GameWin.framePtr = malloc (GameWin.frameSize);
   if (GameWin.framePtr == NULL) {
      fprintf (stderr, "malloc Error\n");
      SDL_DestroyRenderer (renPtr);
      SDL_DestroyWindow (winPtr);
      SDL_Quit ();
      return 1;
   }
   memset (GameWin.framePtr, SC_BLACK, GameWin.frameSize); // Black
   GameWin.rect.x = GAMEWINPOSX;
   GameWin.rect.y = GAMEWINPOSY;
   GameWin.rect.w = GAMEWINWIDTH;
   GameWin.rect.h = GAMEWINHEIGHT;
   GameWin.CurrentPrintPosX = 0;
   GameWin.CurrentPrintPosY = 0;
   SDL_UpdateTexture (GameWin.texPtr, NULL, GameWin.framePtr, GameWin.pitch); // copy Frame to Texture
   SDL_RenderCopy (renPtr, GameWin.texPtr, NULL, &GameWin.rect); // copy Texture to Renderer
   SDL_RenderPresent (renPtr); // update screen
   /* ...*/

   CurrentPressedCod = SDL_GetScancodeFromKey(SDLK_QUOTEDBL);
   SDL_Log("Double quote key:0x%02X has scancode:0x%02X\n", SDLK_QUOTEDBL, CurrentPressedCod);
   /* ...*/
   RunMainLoop = 1;
   while (RunMainLoop) {
      /* ...*/
      SDL_UpdateTexture (GameWin.texPtr, NULL, GameWin.framePtr, GameWin.pitch); // copy Frame to Texture
      SDL_RenderCopy (renPtr, GameWin.texPtr, NULL, &GameWin.rect); // GAMEWIN texture to all Renderer
      SDL_RenderPresent (renPtr); // update screen
      if (SDL_PollEvent(&event)) {
         switch (event.type) {
            case SDL_QUIT:
               RunMainLoop = 0;
               break;
            case SDL_KEYDOWN:
               CurrentPressedCod = event.key.keysym.scancode;
               CurrentPressedKey = event.key.keysym.sym;
               CurrentPressedMod = event.key.keysym.mod;
               SDL_Log("SDL_KEYDOWN cod:0x%02X\n", CurrentPressedCod);
               SDL_Log("SDL_KEYDOWN key:0x%02X='%s'\n", CurrentPressedKey , SDL_GetKeyName(CurrentPressedKey));
               SDL_Log("SDL_KEYDOWN key:%u='%c'", CurrentPressedKey, CurrentPressedKey);
               SDL_Log("SDL_KEYDOWN mod:%u", CurrentPressedMod);
               if (CurrentPressedKey) {
                  switch (CurrentPressedKey) {
                     case SDLK_QUOTEDBL: // '"'
                        SDL_Log("Double Quote Key:%u\n", CurrentPressedKey);
                        break;
                     case 'a':
                     case 'q':
                        /* ...*/
                        break;
                     //default:
                        //SDL_Log("ERR: unsupported key:%u\n", CurrentPressedKey);
                        //goto Error;
                  }
               }
               break;
            case SDL_KEYUP:
               printf("key up ---\n");
               CurrentPressedKey = 0;
               break;
            //default:
               //SDL_Log("INFO: unsupported event:%u\n", event.type);
         }
      }
      SDL_Delay (40); // ms
   }
   /* ...*/
   if (GameWin.framePtr) free(GameWin.framePtr);
   if (GameWin.texPtr) SDL_DestroyTexture(GameWin.texPtr);
   if (renPtr) SDL_DestroyRenderer(renPtr);
   if (winPtr) SDL_DestroyWindow(winPtr);
   SDL_Quit();
   return 0;
   //Error:
   return (0xFF);
}

Solution

  • What you describe looks a lot more like text input than keyboard keys. You could use SDL_TextInputEvent for that. E.g.:

    int main(int argc, char **argv) {
        // ... init
    
        SDL_StartTextInput();   // enable SDL_TEXTINPUT handling
        while(in_main_loop) {
            SDL_Event event;
            while(SDL_PollEvent(&event)) {  // 'while', not 'if'!
                if(event.type == SDL_TEXTINPUT) {
                    // check each character for doublequotes. 'text.text' is utf8
                    for(int i = 0; event.text.text && event.text.text[i]; ++i) {
                        if(event.text.text[i] == '\"') {
                            printf("got doublequote\n");
                        }
                    }
                } else if(event.type == SDL_QUIT) {
                    in_main_loop = false;
                }
            }
    
            // ... render
        }
    }