You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SDL2中以Locale无关方式捕获双引号按键事件

跨平台Locale无关捕获双引号按键的SDL2解决方案

问题分析

你遇到的核心问题是:SDLK_QUOTEDBL并非对应物理按键,而是字符映射后的抽象键码,因此无法通过扫描码或KEYDOWN事件的sym值直接捕获——不同键盘布局下,输入双引号需要的物理按键+修饰键组合不同,KEYDOWN返回的是基础按键的键码(美式键盘是',意式是2),而非最终生成的双引号字符。

最简解决方案:使用SDL_TEXTINPUT事件

SDL提供的SDL_TEXTINPUT事件是系统根据当前键盘布局、修饰键状态转换后的实际输入字符,完全Locale无关。无论用户用什么键盘布局,只要输入双引号,这个事件就会直接返回"字符,无需手动处理布局映射。

实现步骤

  1. 初始化SDL后,调用SDL_StartTextInput()启用文本输入功能(默认未开启)
  2. 在事件循环中添加SDL_TEXTINPUT事件的处理分支,检查事件中的文本是否为双引号

修改后的测试代码

#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 - SS_WIDTH_SPIXELS - 10*BORDERWIDTH)
#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);
   // 启用文本输入功能
   SDL_StartTextInput();

   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));
   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);
   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);
   SDL_RenderCopy (renPtr, GameWin.texPtr, NULL, &GameWin.rect);
   SDL_RenderPresent (renPtr);

   RunMainLoop = 1;
   while (RunMainLoop) {
      SDL_UpdateTexture (GameWin.texPtr, NULL, GameWin.framePtr, GameWin.pitch);
      SDL_RenderCopy (renPtr, GameWin.texPtr, NULL, &GameWin.rect);
      SDL_RenderPresent (renPtr);
      
      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'\n", CurrentPressedKey, CurrentPressedKey);
               SDL_Log("SDL_KEYDOWN mod:%u\n", CurrentPressedMod);
               break;
            case SDL_KEYUP:
               printf("key up ---\n");
               CurrentPressedKey = 0;
               break;
            // 处理文本输入事件,捕获双引号
            case SDL_TEXTINPUT:
               if (event.text.text[0] == '"') {
                  SDL_Log("捕获到双引号输入\n");
               }
               break;
         }
      }
      SDL_Delay (40);
   }

   // 关闭文本输入
   SDL_StopTextInput();
   
   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;
}

关键说明

  • SDL_TEXTINPUT事件会自动处理所有键盘布局、修饰键组合,直接返回最终输入的字符,完全不需要关心用户用的是美式还是意式键盘
  • 必须调用SDL_StartTextInput()启用该功能,程序退出前可调用SDL_StopTextInput()关闭
  • 该方案兼容Windows、macOS、Linux所有主流平台

内容的提问来源于stack exchange,提问作者Valerio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 03:09:14