From e8dea58c92a962597939bcc17e18b0db558af9f0 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Fri, 9 Feb 2024 22:50:21 +0300 Subject: [PATCH] ogc: add support APIs for virtual keyboards This commit adds a new public header (SDL_ogcsupport.h) which provides a couple of APIs so that an application would be able to hook into the On-Screen Keyboard (OSK) calls and provide its own implementation of an OSK. The usefulness of this is that the API is designed in such a way that the OSK implementation can reside in a separate project which would only provide a static library to be linked against the application. In this way, SDL applications only need change a couple of lines in order to get the OSK functionality. --- include/SDL_ogcsupport.h | 114 +++++++++++++++++++++++ src/render/ogc/SDL_render_ogc.c | 5 +- src/video/ogc/SDL_ogcgxcommon.c | 22 ++++- src/video/ogc/SDL_ogcgxcommon.h | 5 +- src/video/ogc/SDL_ogcosk.c | 157 ++++++++++++++++++++++++++++++++ src/video/ogc/SDL_ogcosk.h | 48 ++++++++++ src/video/ogc/SDL_ogcvideo.c | 34 +++++++ 7 files changed, 381 insertions(+), 4 deletions(-) create mode 100644 include/SDL_ogcsupport.h create mode 100644 src/video/ogc/SDL_ogcosk.c create mode 100644 src/video/ogc/SDL_ogcosk.h diff --git a/include/SDL_ogcsupport.h b/include/SDL_ogcsupport.h new file mode 100644 index 0000000000000..74a7464a53f69 --- /dev/null +++ b/include/SDL_ogcsupport.h @@ -0,0 +1,114 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_ogcsupport_h_ +#define SDL_ogcsupport_h_ + +/** + * \file SDL_ogcsupport.h + * + * Header for the Wii/GameCube support routines. + */ + +#include "SDL_stdinc.h" +#include "SDL_events.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SDL_OGC_DriverData SDL_OGC_DriverData; + +typedef struct SDL_OGC_VkContext +{ + size_t struct_size; + + SDL_OGC_DriverData *driverdata; + + SDL_bool is_open; + SDL_Window *window; + SDL_Rect input_rect; + int screen_pan_y; +} SDL_OGC_VkContext; + +typedef struct SDL_OGC_VkPlugin +{ + size_t struct_size; + + void (*Init)(SDL_OGC_VkContext *context); + void (*RenderKeyboard)(SDL_OGC_VkContext *context); + SDL_bool (*ProcessEvent)(SDL_OGC_VkContext *context, SDL_Event *event); + void (*StartTextInput)(SDL_OGC_VkContext *context); + void (*StopTextInput)(SDL_OGC_VkContext *context); + void (*SetTextInputRect)(SDL_OGC_VkContext *context, const SDL_Rect *rect); + void (*ShowScreenKeyboard)(SDL_OGC_VkContext *context); + void (*HideScreenKeyboard)(SDL_OGC_VkContext *context); +} SDL_OGC_VkPlugin; + +extern DECLSPEC const SDL_OGC_VkPlugin * +SDL_OGC_RegisterVkPlugin(const SDL_OGC_VkPlugin *plugin); + +/** + * Processes the given \a event: if a virtual keyboard is active, input events + * will be passed over to the keyboard module and should not be processed by + * the application. + * + * \param event The SDL_Event to be processed + * \returns SDL_TRUE if the event was processed and must be ignored by the + * application. + */ +extern DECLSPEC SDL_bool SDL_OGC_ProcessEvent(SDL_Event *event); + +/** + * A SDL_PollEvent() wrapper which invokes SDL_OGC_ProcessEvent() for every + * received event. + */ +SDL_FORCE_INLINE int SDL_OGC_PollEvent(SDL_Event *event) +{ + while (SDL_PollEvent(event)) { + if (!SDL_OGC_ProcessEvent(event)) { + return 1; + } + } + return 0; +} + +/* Should we add some preprocessor conditions to this? */ +#ifndef SDL_PollEvent +#define SDL_PollEvent SDL_OGC_PollEvent +#endif + +/* Functions for OSK plugin implementations */ +extern DECLSPEC int SDL_OGC_SendKeyboardText(const char *text); +extern DECLSPEC int SDL_OGC_SendVirtualKeyboardKey(Uint8 state, + SDL_Scancode scancode); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* SDL_ogcsupport_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 3aa86c1c0500c..d3a821881e610 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -340,7 +340,10 @@ static int OGC_RenderSetViewPort(SDL_Renderer *renderer, SDL_RenderCommand *cmd) { const SDL_Rect *viewport = &cmd->data.viewport.rect; - OGC_set_viewport(viewport->x, viewport->y, viewport->w, viewport->h); + /* If rendering to the screen, pan the viewport to account for the OSK */ + bool honour_screen_pan = renderer->target == NULL; + OGC_set_viewport(viewport->x, viewport->y, viewport->w, viewport->h, + honour_screen_pan); return 0; } diff --git a/src/video/ogc/SDL_ogcgxcommon.c b/src/video/ogc/SDL_ogcgxcommon.c index 56c017f271763..790747078d6bf 100644 --- a/src/video/ogc/SDL_ogcgxcommon.c +++ b/src/video/ogc/SDL_ogcgxcommon.c @@ -40,10 +40,28 @@ static const f32 tex_pos[] __attribute__((aligned(32))) = { 1.0, }; -void OGC_set_viewport(int x, int y, int w, int h) +static int s_screen_pan_y = 0; + +void OGC_set_screen_pan_y(int y) +{ + if (y != s_screen_pan_y) { + s_screen_pan_y = y; + } +} + +int OGC_get_screen_pan_y() +{ + return s_screen_pan_y; +} + +void OGC_set_viewport(int x, int y, int w, int h, bool honour_panning) { Mtx44 proj; + if (honour_panning) { + y += s_screen_pan_y; + } + GX_SetViewport(x, y, w, h, 0, 1); GX_SetScissor(x, y, w, h); @@ -86,7 +104,7 @@ void OGC_draw_init(int w, int h) GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE); GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); - OGC_set_viewport(0, 0, w, h); + OGC_set_viewport(0, 0, w, h, false); GX_InvVtxCache(); // update vertex cache } diff --git a/src/video/ogc/SDL_ogcgxcommon.h b/src/video/ogc/SDL_ogcgxcommon.h index 1b2ed5b321e71..c24265f0b8d1f 100644 --- a/src/video/ogc/SDL_ogcgxcommon.h +++ b/src/video/ogc/SDL_ogcgxcommon.h @@ -24,13 +24,16 @@ #define SDL_ogcgxcommon_h_ #include "SDL_render.h" +#include "SDL_ogcvideo.h" #include #define GX_COLOR_AS_U32(c) *((u32*)&c) void OGC_draw_init(int w, int h); -void OGC_set_viewport(int x, int y, int w, int h); +void OGC_set_screen_pan_y(int y); +int OGC_get_screen_pan_y(); +void OGC_set_viewport(int x, int y, int w, int h, bool honour_panning); void OGC_load_texture(void *texels, int w, int h, u8 gx_format, SDL_ScaleMode scale_mode); diff --git a/src/video/ogc/SDL_ogcosk.c b/src/video/ogc/SDL_ogcosk.c new file mode 100644 index 0000000000000..0303d2d2de5ce --- /dev/null +++ b/src/video/ogc/SDL_ogcosk.c @@ -0,0 +1,157 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifdef SDL_VIDEO_DRIVER_OGC + +#include "SDL.h" +#include "../../events/SDL_keyboard_c.h" + +#include "SDL_ogcosk.h" +#include "SDL_ogcgxcommon.h" +#include "SDL_ogcvideo.h" + +/* The active Virtual Keyboard plugin (if any) */ +const SDL_OGC_VkPlugin *ogc_vk_plugin = NULL; + +static SDL_OGC_VkContext *ogc_vk_context = NULL; + +static void init_context() +{ + if (ogc_vk_context) return; + + ogc_vk_context = SDL_calloc(1, sizeof(SDL_OGC_VkContext)); + if (!ogc_vk_context) { + SDL_OutOfMemory(); + return; + } + + ogc_vk_context->struct_size = sizeof(SDL_OGC_VkContext); + if (ogc_vk_plugin) { + ogc_vk_plugin->Init(ogc_vk_context); + } +} + +void OGC_StartTextInput(_THIS) +{ + if (!ogc_vk_plugin) return; + ogc_vk_plugin->StartTextInput(ogc_vk_context); +} + +void OGC_StopTextInput(_THIS) +{ + if (!ogc_vk_plugin) return; + ogc_vk_plugin->StopTextInput(ogc_vk_context); +} + +void OGC_SetTextInputRect(_THIS, const SDL_Rect *rect) +{ + if (!ogc_vk_plugin || !ogc_vk_context) { + return; + } + + ogc_vk_plugin->SetTextInputRect(ogc_vk_context, rect); +} + +void OGC_ClearComposition(_THIS) +{ +} + +SDL_bool OGC_IsTextInputShown(_THIS) +{ + return ogc_vk_context && ogc_vk_context->is_open; +} + + +SDL_bool OGC_HasScreenKeyboardSupport(_THIS) +{ + return ogc_vk_plugin != NULL; +} + +void OGC_ShowScreenKeyboard(_THIS, SDL_Window *window) +{ + if (!ogc_vk_plugin) return; + + ogc_vk_context->window = window; + ogc_vk_plugin->ShowScreenKeyboard(ogc_vk_context); +} + +void OGC_HideScreenKeyboard(_THIS, SDL_Window *window) +{ + if (!ogc_vk_plugin) return; + ogc_vk_plugin->HideScreenKeyboard(ogc_vk_context); +} + +SDL_bool OGC_IsScreenKeyboardShown(_THIS, SDL_Window *window) +{ + return OGC_IsTextInputShown(_this); +} + +const SDL_OGC_VkPlugin * +SDL_OGC_RegisterVkPlugin(const SDL_OGC_VkPlugin *plugin) +{ + const SDL_OGC_VkPlugin *old_plugin = ogc_vk_plugin; + ogc_vk_plugin = plugin; + init_context(); + return old_plugin; +} + +SDL_bool SDL_OGC_ProcessEvent(SDL_Event *event) +{ + if (!ogc_vk_plugin || !ogc_vk_context || !ogc_vk_context->is_open) { + return SDL_FALSE; + } + + return ogc_vk_plugin->ProcessEvent(ogc_vk_context, event); +} + +SDL_bool OGC_keyboard_render(_THIS) +{ + if (!ogc_vk_plugin || !ogc_vk_context || !ogc_vk_context->is_open) { + return SDL_FALSE; + } + + ogc_vk_plugin->RenderKeyboard(ogc_vk_context); + return SDL_TRUE; +} + +int OGC_keyboard_get_pan_y(_THIS) +{ + if (!ogc_vk_plugin || !ogc_vk_context) { + return 0; + } + + return ogc_vk_context->screen_pan_y; +} + +int SDL_OGC_SendKeyboardText(const char *text) +{ + return SDL_SendKeyboardText(text); +} + +int SDL_OGC_SendVirtualKeyboardKey(Uint8 state, SDL_Scancode scancode) +{ + return SDL_SendVirtualKeyboardKey(state, scancode); +} + +#endif /* SDL_VIDEO_DRIVER_OGC */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/ogc/SDL_ogcosk.h b/src/video/ogc/SDL_ogcosk.h new file mode 100644 index 0000000000000..5640a8af6e8ec --- /dev/null +++ b/src/video/ogc/SDL_ogcosk.h @@ -0,0 +1,48 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_ogcosk_c_h_ +#define SDL_ogcosk_c_h_ + +#include "../../SDL_internal.h" +#include "../SDL_sysvideo.h" + +#include "SDL_ogcsupport.h" + +extern const SDL_OGC_VkPlugin *OGC_VkPlugin; + +void OGC_StartTextInput(_THIS); +void OGC_StopTextInput(_THIS); +void OGC_SetTextInputRect(_THIS, const SDL_Rect *rect); +void OGC_ClearComposition(_THIS); +SDL_bool OGC_IsTextInputShown(_THIS); + +SDL_bool OGC_HasScreenKeyboardSupport(_THIS); +void OGC_ShowScreenKeyboard(_THIS, SDL_Window *window); +void OGC_HideScreenKeyboard(_THIS, SDL_Window *window); +SDL_bool OGC_IsScreenKeyboardShown(_THIS, SDL_Window *window); + +SDL_bool OGC_keyboard_render(_THIS); +int OGC_keyboard_get_pan_y(_THIS); + +#endif /* SDL_ogcosk_c_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/ogc/SDL_ogcvideo.c b/src/video/ogc/SDL_ogcvideo.c index e03446f890ddc..acbdea7ea0839 100644 --- a/src/video/ogc/SDL_ogcvideo.c +++ b/src/video/ogc/SDL_ogcvideo.c @@ -33,6 +33,7 @@ #include "SDL_ogcframebuffer_c.h" #include "SDL_ogcgxcommon.h" #include "SDL_ogcmouse.h" +#include "SDL_ogcosk.h" #include "SDL_ogcvideo.h" #include @@ -91,6 +92,14 @@ static SDL_VideoDevice *OGC_CreateDevice(void) device->UpdateWindowFramebuffer = SDL_OGC_UpdateWindowFramebuffer; device->DestroyWindowFramebuffer = SDL_OGC_DestroyWindowFramebuffer; + device->StartTextInput = OGC_StartTextInput; + device->StopTextInput = OGC_StopTextInput; + device->SetTextInputRect = OGC_SetTextInputRect; + device->HasScreenKeyboardSupport = OGC_HasScreenKeyboardSupport; + device->ShowScreenKeyboard = OGC_ShowScreenKeyboard; + device->HideScreenKeyboard = OGC_HideScreenKeyboard; + device->IsScreenKeyboardShown = OGC_IsScreenKeyboardShown; + device->free = OGC_DeleteDevice; return device; @@ -191,6 +200,20 @@ void OGC_video_flip(_THIS, bool vsync) { SDL_VideoData *videodata = _this->driverdata; void *xfb = OGC_video_get_xfb(_this); + int screen_w, screen_h, pan_y; + bool must_restore_viewport = false; + + /* While drawing the OSK and the mouse cursor, we want to reset the + * viewport to match the whole screen. */ + screen_w = _this->displays[0].current_mode.w; + screen_h = _this->displays[0].current_mode.h; + if (OGC_get_screen_pan_y() != 0) { + bool honour_panning = false; + OGC_set_screen_pan_y(0); + OGC_set_viewport(0, 0, screen_w, screen_h, honour_panning); + must_restore_viewport = true; + } + OGC_keyboard_render(_this); #ifdef __wii__ OGC_draw_cursor(_this); @@ -206,6 +229,17 @@ void OGC_video_flip(_THIS, bool vsync) } videodata->fb_index ^= 1; + + /* Set the viewport for the next frame: if the OSK is shown, we need to + * check if the application's window must be moved (so that the input rect + * is visible even with the OSK open) and, if so, update the viewport + * accordingly. */ + pan_y = OGC_keyboard_get_pan_y(_this); + if (must_restore_viewport || pan_y != 0) { + bool honour_panning = true; + OGC_set_screen_pan_y(pan_y); + OGC_set_viewport(0, 0, screen_w, screen_h, honour_panning); + } } #endif /* SDL_VIDEO_DRIVER_OGC */