Pārlūkot izejas kodu

The keycode in key events is affected by modifiers by default.

This behavior can be customized with SDL_HINT_KEYCODE_OPTIONS.
Sam Lantinga 1 gadu atpakaļ
vecāks
revīzija
90034b16dc

+ 2 - 0
docs/README-migration.md

@@ -362,6 +362,8 @@ now looks like this:
     SDL_Keymod mod = event.key.mod;
 ```
 
+The keycode in key events is affected by modifiers by default. e.g. pressing the A key would generate the keycode `SDLK_a`, or 'a', and pressing it while holding the shift key would generate the keycode `SDLK_A`, or 'A'. This behavior can be customized with `SDL_HINT_KEYCODE_OPTIONS`.
+
 The gamepad event structures caxis, cbutton, cdevice, ctouchpad, and csensor have been renamed gaxis, gbutton, gdevice, gtouchpad, and gsensor.
 
 The mouseX and mouseY fields of SDL_MouseWheelEvent have been renamed mouse_x and mouse_y.

+ 22 - 0
include/SDL3/SDL_hints.h

@@ -2003,6 +2003,28 @@ extern "C" {
  */
 #define SDL_HINT_JOYSTICK_ZERO_CENTERED_DEVICES "SDL_JOYSTICK_ZERO_CENTERED_DEVICES"
 
+/**
+ * A variable that controls keycode representation in keyboard events.
+ *
+ * This variable is a comma separated set of options for translating keycodes in events:
+ *
+ * - "unmodified": The keycode is the symbol generated by pressing the key without any modifiers applied. e.g. Shift+A would yield the keycode SDLK_a, or 'a'.
+ * - "modified": The keycode is the symbol generated by pressing the key with modifiers applied. e.g. Shift+A would yield the keycode SDLK_A, or 'A'.
+ * - "french_numbers": The number row on French keyboards is inverted, so pressing the 1 key would yield the keycode SDLK_1, or '1', instead of SDLK_AMPERSAND, or '&'
+ * - "latin_letters": For keyboards using non-Latin letters, such as Russian or Thai, the letter keys generate keycodes as though it had an en_US layout. e.g. pressing the key associated with SDL_SCANCODE_A on a Russian keyboard would yield 'a' instead of 'ф'.
+ *
+ * The default value for this hint is equivalent to "modified,french_numbers"
+ *
+ * Some platforms like Emscripten only provide modified keycodes and the options are not used.
+ *
+ * These options do not affect the return value of SDL_GetKeyFromScancode() or SDL_GetScancodeFromKey(), they just apply to the keycode included in key events.
+ *
+ * This hint can be set anytime.
+ *
+ * \since This hint is available since SDL 3.0.0.
+ */
+#define SDL_HINT_KEYCODE_OPTIONS "SDL_KEYCODE_OPTIONS"
+
 /**
  * A variable that controls what KMSDRM device to use.
  *

+ 93 - 8
src/events/SDL_keyboard.c

@@ -30,16 +30,18 @@
 
 /* Global keyboard information */
 
-typedef enum
-{
-    KEYBOARD_HARDWARE = 0x01,
-    KEYBOARD_VIRTUAL = 0x02,
-    KEYBOARD_AUTORELEASE = 0x04,
-    KEYBOARD_IGNOREMODIFIERS = 0x08
-} SDL_KeyboardFlags;
+#define KEYBOARD_HARDWARE   0x01
+#define KEYBOARD_VIRTUAL    0x02
+#define KEYBOARD_AUTORELEASE    0x04
+#define KEYBOARD_IGNOREMODIFIERS    0x0
 
 #define KEYBOARD_SOURCE_MASK (KEYBOARD_HARDWARE | KEYBOARD_AUTORELEASE)
 
+#define KEYCODE_OPTION_APPLY_MODIFIERS  0x01
+#define KEYCODE_OPTION_FRENCH_NUMBERS   0x02
+#define KEYCODE_OPTION_LATIN_LETTERS    0x04
+#define DEFAULT_KEYCODE_OPTIONS (KEYCODE_OPTION_APPLY_MODIFIERS | KEYCODE_OPTION_FRENCH_NUMBERS)
+
 typedef struct SDL_KeyboardInstance
 {
     SDL_KeyboardID instance_id;
@@ -54,6 +56,9 @@ typedef struct SDL_Keyboard
     Uint8 keysource[SDL_NUM_SCANCODES];
     Uint8 keystate[SDL_NUM_SCANCODES];
     SDL_Keymap *keymap;
+    SDL_bool french_numbers;
+    SDL_bool non_latin_letters;
+    Uint32 keycode_options;
     SDL_bool autorelease_pending;
     Uint64 hardware_timestamp;
 } SDL_Keyboard;
@@ -62,9 +67,33 @@ static SDL_Keyboard SDL_keyboard;
 static int SDL_keyboard_count;
 static SDL_KeyboardInstance *SDL_keyboards;
 
+static void SDLCALL SDL_KeycodeOptionsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_Keyboard *keyboard = (SDL_Keyboard *)userdata;
+
+    if (hint && *hint) {
+        keyboard->keycode_options = 0;
+        if (SDL_strstr(hint, "unmodified")) {
+            keyboard->keycode_options &= ~KEYCODE_OPTION_APPLY_MODIFIERS;
+        } else if (SDL_strstr(hint, "modified")) {
+            keyboard->keycode_options |= KEYCODE_OPTION_APPLY_MODIFIERS;
+        }
+        if (SDL_strstr(hint, "french_numbers")) {
+            keyboard->keycode_options |= KEYCODE_OPTION_FRENCH_NUMBERS;
+        }
+        if (SDL_strstr(hint, "latin_letters")) {
+            keyboard->keycode_options |= KEYCODE_OPTION_LATIN_LETTERS;
+        }
+    } else {
+        keyboard->keycode_options = DEFAULT_KEYCODE_OPTIONS;
+    }
+}
+
 /* Public functions */
 int SDL_InitKeyboard(void)
 {
+    SDL_AddHintCallback(SDL_HINT_KEYCODE_OPTIONS,
+                        SDL_KeycodeOptionsChanged, &SDL_keyboard);
     return 0;
 }
 
@@ -205,6 +234,25 @@ void SDL_SetKeymap(SDL_Keymap *keymap, SDL_bool send_event)
 
     keyboard->keymap = keymap;
 
+    // Detect French number row (all symbols)
+    keyboard->french_numbers = SDL_TRUE;
+    for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_0; ++i) {
+        if (SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE)) ||
+            !SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_SHIFT))) {
+            keyboard->french_numbers = SDL_FALSE;
+            break;
+        }
+    }
+
+    // Detect non-Latin keymap
+    keyboard->non_latin_letters = SDL_TRUE;
+    for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_D; ++i) {
+        if (SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE) <= 0xFF) {
+            keyboard->non_latin_letters = SDL_FALSE;
+            break;
+        }
+    }
+
     if (send_event) {
         SDL_SendKeymapChangedEvent();
     }
@@ -276,6 +324,40 @@ int SDL_SetKeyboardFocus(SDL_Window *window)
     return 0;
 }
 
+static SDL_Keycode SDL_GetEventKeycode(SDL_Keyboard *keyboard, SDL_Scancode scancode, SDL_Keymod modstate)
+{
+    SDL_Keycode keycode;
+
+    if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
+        if (keyboard->non_latin_letters && (keyboard->keycode_options & KEYCODE_OPTION_LATIN_LETTERS)) {
+            if (keyboard->keycode_options & KEYCODE_OPTION_APPLY_MODIFIERS) {
+                keycode = SDL_GetDefaultKeyFromScancode(scancode, modstate);
+            } else {
+                keycode = SDL_GetDefaultKeyFromScancode(scancode, SDL_KMOD_NONE);
+            }
+            return keycode;
+        }
+    }
+
+    if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0) {
+        if (keyboard->french_numbers && (keyboard->keycode_options & KEYCODE_OPTION_FRENCH_NUMBERS)) {
+            // Invert the shift state to generate the correct keycode
+            if (modstate & SDL_KMOD_SHIFT) {
+                modstate &= ~SDL_KMOD_SHIFT;
+            } else {
+                modstate |= SDL_KMOD_SHIFT;
+            }
+        }
+    }
+
+    if (keyboard->keycode_options & KEYCODE_OPTION_APPLY_MODIFIERS) {
+        keycode = SDL_GetKeyFromScancode(scancode, modstate);
+    } else {
+        keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE);
+    }
+    return keycode;
+}
+
 static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, SDL_Keycode keycode, Uint8 state)
 {
     SDL_Keyboard *keyboard = &SDL_keyboard;
@@ -325,7 +407,7 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_Keybo
         keyboard->keystate[scancode] = state;
 
         if (keycode == SDLK_UNKNOWN) {
-            keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE);
+            keycode = SDL_GetEventKeycode(keyboard, scancode, keyboard->modstate);
         }
 
     } else if (keycode == SDLK_UNKNOWN && rawcode == 0) {
@@ -589,6 +671,9 @@ void SDL_QuitKeyboard(void)
         SDL_DestroyKeymap(SDL_keyboard.keymap);
         SDL_keyboard.keymap = NULL;
     }
+
+    SDL_DelHintCallback(SDL_HINT_KEYCODE_OPTIONS,
+                        SDL_KeycodeOptionsChanged, &SDL_keyboard);
 }
 
 const Uint8 *SDL_GetKeyboardState(int *numkeys)

+ 13 - 0
src/test/SDL_test_common.c

@@ -2198,6 +2198,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_O:
         case SDLK_o:
             if (withControl) {
                 /* Ctrl-O (or Ctrl-Shift-O) changes window opacity. */
@@ -2215,6 +2216,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_H:
         case SDLK_h:
             if (withControl) {
                 /* Ctrl-H changes cursor visibility. */
@@ -2225,6 +2227,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_C:
         case SDLK_c:
             if (withAlt) {
                 /* Alt-C copy awesome text to the primary selection! */
@@ -2250,6 +2253,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 break;
             }
             break;
+        case SDLK_V:
         case SDLK_v:
             if (withAlt) {
                 /* Alt-V paste awesome text from the primary selection! */
@@ -2277,6 +2281,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_F:
         case SDLK_f:
             if (withControl) {
                 /* Ctrl-F flash the window */
@@ -2286,6 +2291,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_G:
         case SDLK_g:
             if (withControl) {
                 /* Ctrl-G toggle mouse grab */
@@ -2295,6 +2301,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_K:
         case SDLK_k:
             if (withControl) {
                 /* Ctrl-K toggle keyboard grab */
@@ -2304,6 +2311,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_M:
         case SDLK_m:
             if (withControl) {
                 /* Ctrl-M maximize */
@@ -2326,12 +2334,14 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_R:
         case SDLK_r:
             if (withControl) {
                 /* Ctrl-R toggle mouse relative mode */
                 SDL_SetRelativeMouseMode(!SDL_GetRelativeMouseMode());
             }
             break;
+        case SDLK_T:
         case SDLK_t:
             if (withControl) {
                 /* Ctrl-T toggle topmost mode */
@@ -2346,6 +2356,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_Z:
         case SDLK_z:
             if (withControl) {
                 /* Ctrl-Z minimize */
@@ -2385,6 +2396,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
             }
 
             break;
+        case SDLK_B:
         case SDLK_b:
             if (withControl) {
                 /* Ctrl-B toggle window border */
@@ -2396,6 +2408,7 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
                 }
             }
             break;
+        case SDLK_A:
         case SDLK_a:
             if (withControl) {
                 /* Ctrl-A toggle aspect ratio */

+ 1 - 1
test/testaudiostreamdynamicresample.c

@@ -234,7 +234,7 @@ static void loop(void)
                 SDL_Log("Cleared audio stream");
             } else if (sym == SDLK_s) {
                 queue_audio();
-            } else if (sym == SDLK_d) {
+            } else if (sym == SDLK_d || sym == SDLK_D) {
                 float amount = 1.0f;
                 amount *= (e.key.mod & SDL_KMOD_CTRL) ? 10.0f : 1.0f;
                 amount *= (e.key.mod & SDL_KMOD_SHIFT) ? 10.0f : 1.0f;

+ 18 - 20
test/testintersections.c

@@ -226,27 +226,25 @@ static void loop(void *arg)
             break;
         case SDL_EVENT_KEY_DOWN:
             switch (event.key.key) {
-            case 'l':
-                if (event.key.mod & SDL_KMOD_SHIFT) {
-                    num_lines = 0;
-                } else {
-                    add_line(
-                        (float)SDL_rand_n(640),
-                        (float)SDL_rand_n(480),
-                        (float)SDL_rand_n(640),
-                        (float)SDL_rand_n(480));
-                }
+            case SDLK_L:
+                num_lines = 0;
                 break;
-            case 'r':
-                if (event.key.mod & SDL_KMOD_SHIFT) {
-                    num_rects = 0;
-                } else {
-                    add_rect(
-                        (float)SDL_rand_n(640),
-                        (float)SDL_rand_n(480),
-                        (float)SDL_rand_n(640),
-                        (float)SDL_rand_n(480));
-                }
+            case SDLK_l:
+                add_line(
+                    (float)SDL_rand_n(640),
+                    (float)SDL_rand_n(480),
+                    (float)SDL_rand_n(640),
+                    (float)SDL_rand_n(480));
+                break;
+            case SDLK_R:
+                num_rects = 0;
+                break;
+            case SDLK_r:
+                add_rect(
+                    (float)SDL_rand_n(640),
+                    (float)SDL_rand_n(480),
+                    (float)SDL_rand_n(640),
+                    (float)SDL_rand_n(480));
                 break;
             default:
                 break;