如何在SDL2中以Locale无关方式捕获双引号按键事件
跨平台Locale无关捕获双引号按键的SDL2解决方案
问题分析
你遇到的核心问题是:SDLK_QUOTEDBL并非对应物理按键,而是字符映射后的抽象键码,因此无法通过扫描码或KEYDOWN事件的sym值直接捕获——不同键盘布局下,输入双引号需要的物理按键+修饰键组合不同,KEYDOWN返回的是基础按键的键码(美式键盘是',意式是2),而非最终生成的双引号字符。
最简解决方案:使用SDL_TEXTINPUT事件
SDL提供的SDL_TEXTINPUT事件是系统根据当前键盘布局、修饰键状态转换后的实际输入字符,完全Locale无关。无论用户用什么键盘布局,只要输入双引号,这个事件就会直接返回"字符,无需手动处理布局映射。
实现步骤
- 初始化SDL后,调用
SDL_StartTextInput()启用文本输入功能(默认未开启) - 在事件循环中添加
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
相关产品推荐
相关产品推荐

