From f1f24b173c4109bb32fd5d624a849408a624c19c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 25 Mar 2024 15:18:27 -0700 Subject: [PATCH] Added support for multiple distinct keyboards --- src/events/SDL_events.c | 4 +- src/events/SDL_keyboard.c | 3 - src/video/x11/SDL_x11events.c | 4 +- test/testcustomcursor.c | 1 - test/testmanymouse.c | 200 ++++++++++++++++++++++++++++++++-- 5 files changed, 196 insertions(+), 16 deletions(-) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 968f53c5d..93af8aa1f 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -335,8 +335,8 @@ static void SDL_LogEvent(const SDL_Event *event) #undef PRINT_KEYDEV_EVENT #define PRINT_KEY_EVENT(event) \ - (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u state=%s repeat=%s scancode=%u keycode=%u mod=%u)", \ - (uint)event->key.timestamp, (uint)event->key.windowID, \ + (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u state=%s repeat=%s scancode=%u keycode=%u mod=%u)", \ + (uint)event->key.timestamp, (uint)event->key.windowID, (uint)event->key.which, \ event->key.state == SDL_PRESSED ? "pressed" : "released", \ event->key.repeat ? "true" : "false", \ (uint)event->key.keysym.scancode, \ diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c index dd14e6c3d..f1b2cde89 100644 --- a/src/events/SDL_keyboard.c +++ b/src/events/SDL_keyboard.c @@ -945,9 +945,6 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_Keybo Uint8 repeat = SDL_FALSE; const Uint8 source = flags & KEYBOARD_SOURCE_MASK; - /* We currently don't have raw keyboard mode, so all key events are global */ - keyboardID = SDL_GLOBAL_KEYBOARD_ID; - if (scancode == SDL_SCANCODE_UNKNOWN || scancode >= SDL_NUM_SCANCODES) { return 0; } diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 9924c35b1..afff85370 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -908,7 +908,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ if (xevent->type == KeyPress) { /* Don't send the key if it looks like a duplicate of a filtered key sent by an IME */ if (xevent->xkey.keycode != videodata->filter_code || xevent->xkey.time != videodata->filter_time) { - SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, videodata->key_layout[keycode]); + SDL_SendKeyboardKey(0, keyboardID, SDL_PRESSED, videodata->key_layout[keycode]); } if (*text) { SDL_SendKeyboardText(text); @@ -918,7 +918,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ /* We're about to get a repeated key down, ignore the key up */ return; } - SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, videodata->key_layout[keycode]); + SDL_SendKeyboardKey(0, keyboardID, SDL_RELEASED, videodata->key_layout[keycode]); } } diff --git a/test/testcustomcursor.c b/test/testcustomcursor.c index 17fbc5faf..ba0cab050 100644 --- a/test/testcustomcursor.c +++ b/test/testcustomcursor.c @@ -74,7 +74,6 @@ static const char *cross[] = { ". c #ffffff", " c None", /* pixels */ - /* pixels */ " ", " ", " ", diff --git a/test/testmanymouse.c b/test/testmanymouse.c index ec4dbce5d..66b86a490 100644 --- a/test/testmanymouse.c +++ b/test/testmanymouse.c @@ -60,11 +60,56 @@ static const char *arrow[] = { "0,0" }; +static const char *cross[] = { + /* width height num_colors chars_per_pixel */ + " 32 32 3 1", + /* colors */ + "o c #ffffff", + ". c #000000", + " c None", + /* pixels */ + " ", + " ", + " ", + " ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oooooooooooooooooooooooo ", + " oooooooooooooooooooooooo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " oo ", + " ", + " ", + " ", + " ", + "0,0" +}; + static SDLTest_CommonState *state; static int done; -#define PROP_CURSOR_TEXTURE "cursor_texture" -#define MAX_MICE 3 +#define PROP_ARROW_CURSOR_TEXTURE "arrow_cursor_texture" +#define PROP_CROSS_CURSOR_TEXTURE "cross_cursor_texture" +#define MAX_MICE 3 +#define MAX_KEYBOARDS 3 #define CURSOR_SIZE 48.0f #define MAX_TRAIL 500 #define TRAIL_SIZE 8.0f @@ -74,7 +119,8 @@ static SDL_Color colors[] = { { 255, 0, 255, 255 }, /* mouse 2, magenta */ { 255, 255, 0, 255 }, /* mouse 3, yellow */ }; -SDL_COMPILE_TIME_ASSERT(colors, SDL_arraysize(colors) == MAX_MICE); +SDL_COMPILE_TIME_ASSERT(mouse_colors, SDL_arraysize(colors) == MAX_MICE); +SDL_COMPILE_TIME_ASSERT(keyboard_colors, SDL_arraysize(colors) == MAX_KEYBOARDS); typedef struct { @@ -89,7 +135,17 @@ typedef struct static MouseState mice[MAX_MICE]; -static SDL_Texture *CreateCursor(const char *image[], SDL_Renderer *renderer) +typedef struct +{ + SDL_KeyboardID instance_id; + SDL_bool active; + Uint8 button_state; + SDL_FPoint position; +} KeyboardState; + +static KeyboardState keyboards[MAX_KEYBOARDS]; + +static SDL_Texture *CreateTexture(const char *image[], SDL_Renderer *renderer) { SDL_Surface *surface; SDL_Palette *palette; @@ -109,6 +165,9 @@ static SDL_Texture *CreateCursor(const char *image[], SDL_Renderer *renderer) palette->colors['.'].r = 0xFF; palette->colors['.'].g = 0xFF; palette->colors['.'].b = 0xFF; + palette->colors['o'].r = 0xFF; + palette->colors['o'].g = 0xFF; + palette->colors['o'].b = 0xFF; palette->colors['X'].r = 0x00; palette->colors['X'].g = 0x00; palette->colors['X'].b = 0x00; @@ -271,6 +330,115 @@ static void DrawMouseState(SDL_Window *window, SDL_Renderer *renderer, MouseStat SDL_RenderTexture(renderer, cursor, NULL, &rect); } +static void HandleKeyboardAdded(SDL_KeyboardID instance_id) +{ + SDL_Window *window = state->windows[0]; + int i, w = 0, h = 0; + + SDL_GetWindowSize(window, &w, &h); + + for (i = 0; i < SDL_arraysize(keyboards); ++i) { + KeyboardState *keyboard_state = &keyboards[i]; + if (!keyboard_state->active) { + keyboard_state->instance_id = instance_id; + keyboard_state->active = SDL_TRUE; + keyboard_state->position.x = w * 0.5f; + keyboard_state->position.y = h * 0.5f; + return; + } + } +} + +static void HandleKeyboardRemoved(SDL_KeyboardID instance_id) +{ + int i; + + for (i = 0; i < SDL_arraysize(keyboards); ++i) { + KeyboardState *keyboard_state = &keyboards[i]; + if (instance_id == keyboard_state->instance_id) { + SDL_zerop(keyboard_state); + return; + } + } +} + +static void ActivateKeyboard(SDL_KeyboardID instance_id) +{ + int i; + + for (i = 0; i < SDL_arraysize(keyboards); ++i) { + KeyboardState *keyboard_state = &keyboards[i]; + if (keyboard_state->active && instance_id == keyboard_state->instance_id) { + return; + } + } + + HandleKeyboardAdded(instance_id); +} + +static void HandleKeyboardKeyDown(SDL_KeyboardEvent *event) +{ + SDL_Window *window = state->windows[0]; + int i, w = 0, h = 0; + + SDL_GetWindowSize(window, &w, &h); + + ActivateKeyboard(event->which); + + for (i = 0; i < SDL_arraysize(keyboards); ++i) { + KeyboardState *keyboard_state = &keyboards[i]; + if (!keyboard_state->active) { + continue; + } + if (event->which == keyboard_state->instance_id) { + switch (event->keysym.sym) { + case SDLK_LEFT: + keyboard_state->position.x -= CURSOR_SIZE; + if (keyboard_state->position.x < 0.0f) { + keyboard_state->position.x = 0.0f; + } + break; + case SDLK_RIGHT: + keyboard_state->position.x += CURSOR_SIZE; + if (keyboard_state->position.x > w) { + keyboard_state->position.x = w; + } + break; + case SDLK_UP: + keyboard_state->position.y -= CURSOR_SIZE; + if (keyboard_state->position.y < 0.0f) { + keyboard_state->position.y = 0.0f; + } + break; + case SDLK_DOWN: + keyboard_state->position.y += CURSOR_SIZE; + if (keyboard_state->position.y > h) { + keyboard_state->position.y = h; + } + break; + default: + break; + } + } + } +} + +static void DrawKeyboardState(SDL_Window *window, SDL_Renderer *renderer, KeyboardState *keyboard_state, SDL_Texture *cursor, SDL_Color *color) +{ + SDL_FRect rect; + + if (!keyboard_state->active) { + return; + } + + rect.x = keyboard_state->position.x - CURSOR_SIZE / 2; + rect.y = keyboard_state->position.y - CURSOR_SIZE / 2; + rect.w = CURSOR_SIZE; + rect.h = CURSOR_SIZE; + SDL_SetTextureColorMod(cursor, color->r, color->g, color->b); + SDL_RenderTexture(renderer, cursor, NULL, &rect); +} + static void loop(void) { int i, j; @@ -281,6 +449,15 @@ static void loop(void) SDLTest_CommonEvent(state, &event, &done); switch (event.type) { + case SDL_EVENT_KEYBOARD_ADDED: + /* Wait for events before activating this keyboard */ + break; + case SDL_EVENT_KEYBOARD_REMOVED: + HandleKeyboardRemoved(event.kdevice.which); + break; + case SDL_EVENT_KEY_DOWN: + HandleKeyboardKeyDown(&event.key); + break; case SDL_EVENT_MOUSE_ADDED: /* Wait for events before activating this mouse */ break; @@ -302,13 +479,18 @@ static void loop(void) for (i = 0; i < state->num_windows; ++i) { SDL_Window *window = state->windows[i]; SDL_Renderer *renderer = state->renderers[i]; - SDL_Texture *cursor = (SDL_Texture *)SDL_GetProperty(SDL_GetRendererProperties(renderer), PROP_CURSOR_TEXTURE, NULL); + SDL_Texture *arrow_cursor = (SDL_Texture *)SDL_GetProperty(SDL_GetRendererProperties(renderer), PROP_ARROW_CURSOR_TEXTURE, NULL); + SDL_Texture *cross_cursor = (SDL_Texture *)SDL_GetProperty(SDL_GetRendererProperties(renderer), PROP_CROSS_CURSOR_TEXTURE, NULL); SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255); SDL_RenderClear(renderer); for (j = 0; j < SDL_arraysize(mice); ++j) { - DrawMouseState(window, renderer, &mice[j], cursor, &colors[j]); + DrawMouseState(window, renderer, &mice[j], arrow_cursor, &colors[j]); + } + + for (j = 0; j < SDL_arraysize(keyboards); ++j) { + DrawKeyboardState(window, renderer, &keyboards[j], cross_cursor, &colors[j]); } SDL_RenderPresent(renderer); @@ -349,9 +531,11 @@ int main(int argc, char *argv[]) /* Create the cursor textures */ for (i = 0; i < state->num_windows; ++i) { SDL_Renderer *renderer = state->renderers[i]; + SDL_Texture *cursor_arrow = CreateTexture(arrow, renderer); + SDL_Texture *cursor_cross = CreateTexture(cross, renderer); - SDL_Texture *cursor = CreateCursor(arrow, renderer); - SDL_SetProperty(SDL_GetRendererProperties(renderer), PROP_CURSOR_TEXTURE, cursor); + SDL_SetProperty(SDL_GetRendererProperties(renderer), PROP_ARROW_CURSOR_TEXTURE, cursor_arrow); + SDL_SetProperty(SDL_GetRendererProperties(renderer), PROP_CROSS_CURSOR_TEXTURE, cursor_cross); } /* We only get mouse motion for distinct devices when relative mode is enabled */