diff --git a/.clang-format b/.clang-format index e98e5cd70af8d..3105e5c0f756d 100644 --- a/.clang-format +++ b/.clang-format @@ -34,6 +34,7 @@ BraceWrapping: AfterStruct: true AfterUnion: true AfterExternBlock: false + BeforeCatch: true BeforeElse: false BeforeWhile: false IndentBraces: false @@ -62,6 +63,8 @@ IndentGotoLabels: true IndentPPDirectives: None IndentExternBlock: NoIndent +NamespaceIndentation: All + PointerAlignment: Right SpaceAfterCStyleCast: false SpacesInCStyleCastParentheses: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 013dab9ee2e42..5a686e7ea5e2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,6 +240,9 @@ endif() if(WIIU) # Prefer coreinit atomics on Wii U due to a hardware bug in load-exclusive and store-exclusive instructions set(OPT_DEF_GCC_ATOMICS OFF) + # Needed for nn::swkbd API + set(LINKER_LANGUAGE CXX) + set (CMAKE_CXX_STANDARD 23) endif() # Default option knobs @@ -2920,14 +2923,14 @@ elseif(WIIU) if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_WIIU 1) set(SDL_VIDEO_RENDER_WIIU 1) - file(GLOB WIIU_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/wiiu/*.c) + file(GLOB WIIU_VIDEO_SOURCES + ${SDL2_SOURCE_DIR}/src/video/wiiu/*.c + ${SDL2_SOURCE_DIR}/src/video/wiiu/*.cpp) set(SOURCE_FILES ${SOURCE_FILES} ${WIIU_VIDEO_SOURCES}) set(HAVE_SDL_VIDEO TRUE) endif() - - list(APPEND EXTRA_LIBS - wut - ) + list(APPEND EXTRA_CXXFLAGS "-fno-exceptions") + list(APPEND EXTRA_LIBS wut) endif() if(HAVE_VULKAN AND NOT SDL_LOADSO) @@ -3063,8 +3066,13 @@ endif() if(EXTRA_CFLAGS) list(REMOVE_DUPLICATES EXTRA_CFLAGS) endif() +if(EXTRA_CXXFLAGS) + list(REMOVE_DUPLICATES EXTRA_CXXFLAGS) +endif() listtostr(EXTRA_CFLAGS _EXTRA_CFLAGS) set(EXTRA_CFLAGS ${_EXTRA_CFLAGS}) +listtostr(EXTRA_CXXFLAGS _EXTRA_CXXFLAGS) +set(EXTRA_CXXFLAGS ${_EXTRA_CXXFLAGS}) # Compat helpers for the configuration files @@ -3309,10 +3317,11 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") message(STATUS " CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}") endif() message(STATUS "") -message(STATUS " CFLAGS: ${CMAKE_C_FLAGS}") -message(STATUS " EXTRA_CFLAGS: ${EXTRA_CFLAGS}") -message(STATUS " EXTRA_LDFLAGS: ${EXTRA_LDFLAGS} ${EXTRA_LDFLAGS_BUILD}") -message(STATUS " EXTRA_LIBS: ${EXTRA_LIBS}") +message(STATUS " CFLAGS: ${CMAKE_C_FLAGS}") +message(STATUS " EXTRA_CFLAGS: ${EXTRA_CFLAGS}") +message(STATUS " EXTRA_CXXFLAGS: ${EXTRA_CXXFLAGS}") +message(STATUS " EXTRA_LDFLAGS: ${EXTRA_LDFLAGS} ${EXTRA_LDFLAGS_BUILD}") +message(STATUS " EXTRA_LIBS: ${EXTRA_LIBS}") message(STATUS "") message(STATUS " Build Shared Library: ${SDL_SHARED}") message(STATUS " Build Static Library: ${SDL_STATIC}") @@ -3347,6 +3356,8 @@ endif() # Ensure that the extra cflags are used at compile time set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} ${EXTRA_CFLAGS_BUILD}") +# Same for cxxflags +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS} ${EXTRA_CXXFLAGS_BUILD}") if(NOT WINDOWS_STORE AND NOT SDL2_DISABLE_SDL2MAIN) # Build SDLmain diff --git a/include/SDL_system.h b/include/SDL_system.h index 4b7eaddcc0ed9..03867316646f9 100644 --- a/include/SDL_system.h +++ b/include/SDL_system.h @@ -612,6 +612,212 @@ extern DECLSPEC int SDLCALL SDL_GDKGetTaskQueue(XTaskQueueHandle * outTaskQueue) #endif +/* Platform specific functions for Wii U */ +#if defined(__WIIU__) +typedef enum SDL_WiiUSysWMEventType { + /** Sent before any text input event. */ + SDL_WIIU_SYSWM_SWKBD_OK_START_EVENT = 1, + /** Sent after all text input events. */ + SDL_WIIU_SYSWM_SWKBD_OK_FINISH_EVENT, + /** Sent after the swkbd was canceled. */ + SDL_WIIU_SYSWM_SWKBD_CANCEL_EVENT +} SDL_WiiUSysWMEventType; + +/** + * Disable the swkbd. + * + * Use this function if you only want text input from a physical USB keyboard. + * + * \param enabled `SDL_FALSE` if you do not want the swkbd to show up after calling + * `SDL_StartTextInput()`. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDEnabled(SDL_bool enabled); + +/** + * Select the swkbd keyboard mode. + * + * \sa SDL_WiiUSetSWKBDKeyboardMode + */ +typedef enum SDL_WiiUSWKBDKeyboardMode { + /** Full keyboard. */ + SDL_WIIU_SWKBD_KEYBOARD_MODE_FULL, + /** Numeric keyboard. */ + SDL_WIIU_SWKBD_KEYBOARD_MODE_NUMPAD, + /** Restricted keyboard (only letters, numbers and symbols.) */ + SDL_WIIU_SWKBD_KEYBOARD_MODE_RESTRICTED, + /** NNID keyboard. */ + SDL_WIIU_SWKBD_KEYBOARD_MODE_NNID +} SDL_WiiUSWKBDKeyboardMode; + +/** + * Sets the swkbd's keyboard mode. + * + * \param mode One of SDL_WiiUSWKBDKeyboardMode. The default is + * `SDL_WIIU_SWKBD_KEYBOARD_MODE_FULL`. + * + * \note + * This option is reset to the default value after the swkbd is shown. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDKeyboardMode(SDL_WiiUSWKBDKeyboardMode mode); + +/** + * Sets the label for the swkbd's "OK" button. + * + * \param label String for the "OK" button, encoded in UTF-8, or `NULL` to use the + * default. + * + * \note + * This option is reset to the default value after the swkbd is shown. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDOKLabel(const char * label); + +/** + * Sets the swkbd's word suggestions option. + * + * \param show `SDL_TRUE` to enable word suggestions. The default is `SDL_TRUE`. + * + * \note + * This option is reset to the default value after the swkbd is shown. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDShowWordSuggestions(SDL_bool show); + +/** + * Sets the swkbd's initial text. + * + * \param text String for the initial text, encoded in UTF-8, or `NULL` to use the + * default (no initial text.) + * + * \note + * This option is reset to the default value after the swkbd is shown. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDInitialText(const char * text); + +/** + * Sets the swkbd's hint text. + * + * \param text String for the hint text, encoded in UTF-8, or `NULL` to use the default + * (no hint.) + * + * \note + * This option is reset to the default value after the swkbd is shown. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDHintText(const char * text); + +/** + * Controls how to display passwwords in the swkbd when in password mode. + */ +typedef enum SDL_WiiUSWKBDPasswordMode { + SDL_WIIU_SWKBD_PASSWORD_MODE_SHOW, /**< Show password. */ + SDL_WIIU_SWKBD_PASSWORD_MODE_HIDE, /**< Hide password. */ + SDL_WIIU_SWKBD_PASSWORD_MODE_FADE /**< Hide password after 1 second. */ +} SDL_WiiUSWKBDPasswordMode; + +/** + * Sets the swkbd's password mode. + * + * \param mode One of of the SDL_WiiUSWKBDPasswordMode values. The default is + * `SDL_WIIU_SWKBD_PASSWORD_MODE_SHOW`. + * + * \note + * This option is reset to the default value after the swkbd is shown. + * + * \sa SDL_WiiUSetSWKBDKeyboardMode + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDPasswordMode(SDL_WiiUSWKBDPasswordMode mode); + +/** + * Sets whether to highlight (select) the swkbd's initial text. + * + * \param highlight `SDL_TRUE` to highlight the initial text. The default is `SDL_FALSE`. + + * \note + * This option is reset to the default value after the swkbd is shown. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDHighlightInitialText(SDL_bool highlight); + +/** + * Sets the swkbd's copy-paste button option. + * + * \param show `SDL_TRUE` to show copy-paste buttons. The default is `SDL_FALSE`. + * + * \note + * This option is reset to the default value after the swkbd is shown. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDShowCopyPasteButtons(SDL_bool show); + +/** + * Sets the swkbd's built-in rendering of the Wii remote pointer. + * + * \param draw `SDL_TRUE` to let the swkbd draw its own Wii remote pointer. The default is + * `SDL_TRUE`. + * + * This option is reset to the default value after the swkbd is shown. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDDrawWiiPointer(SDL_bool draw); + +/** + * Sets the swkbd's locale (region and language.) + * + * \param locale String representing the intended keyboard region and language, using + * Unix-style locale format (e.g. `"en_US"`, `"fr_CA"`, `ja_JP`, `"en"`.) Set to `NULL` to + * use the system region and language. The default is `NULL`. + * + * The recognized languages are: + * - ja: Japanese + * - en: English + * - fr: French + * - de: German + * - it: Italian + * - es: Spanish + * - nl: Dutch + * - pt: Portuguese + * - ru: Russian + * + * The recognized regions are: + * - US, CA, MX, BR: mapped to the "USA" console region. + * - DE, ES, FR, GB, IT, NL, PT, RU: mapped to the "EUR" console region. + * - JP: mapped to the "JPN" console region. + * + * When no country is specified, "jp" will map to the "JPN" region, "en" to "USA" region, + * and everything else will map to "EUR" region. + * + * By default the locale is obtained from the system configuration (cafe.language). The + * empty locale string is equivalent to the system configuration. + * + * \note + * The swkbd will be re-initialized after calling this function. + */ +extern DECLSPEC void SDLCALL SDL_WiiUSetSWKBDLocale(const char * locale); + +/** + * Sends VPAD input to the swkbd. + * + * Call this function at every frame if your application calls `VPADRead()` instead of + * using the SDL game controller subsystem. + * + * \param vpad Pointer to a `VPADStatus` object. + * \returns `SDL_TRUE` if the swkbd is visible and is going to use the input. + * + * \note + * The touch point in `tpNormal` should contain a calibrated point, by calling + * `VPADGetTPCalibratedPoint()`, prior to calling this function. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_WiiUSetSWKBDVPAD(const void * vpad); + +/** + * Sends KPAD input to the swkbd. + * + * Call this function at every frame if you call `KPADRead()` or `KPADReadEx()` in your + * application, instead of using the SDL game controller subsystem. + * + * \param channel Number of channel. + * \param kpad Pointer to a `KPADStatus` object. + * \returns `SDL_TRUE` if the swkbd is visible and is going to use the input. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_WiiUSetSWKBDKPAD(int channel, const void * kpad); + +#endif /* Wii U */ + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/include/SDL_syswm.h b/include/SDL_syswm.h index b35734deb3345..818ce505757a5 100644 --- a/include/SDL_syswm.h +++ b/include/SDL_syswm.h @@ -148,7 +148,8 @@ typedef enum SDL_SYSWM_OS2, SDL_SYSWM_HAIKU, SDL_SYSWM_KMSDRM, - SDL_SYSWM_RISCOS + SDL_SYSWM_RISCOS, + SDL_SYSWM_WIIU } SDL_SYSWM_TYPE; /** @@ -211,6 +212,11 @@ struct SDL_SysWMmsg MPARAM mp1; /**< The first first message parameter */ MPARAM mp2; /**< The second first message parameter */ } os2; +#endif +#if defined(SDL_VIDEO_DRIVER_WIIU) + struct { + unsigned event; + } wiiu; #endif /* Can't have an empty union */ int dummy; diff --git a/src/events/SDL_events_c.h b/src/events/SDL_events_c.h index 7f27b1a2fd3c9..0699d0c480b2a 100644 --- a/src/events/SDL_events_c.h +++ b/src/events/SDL_events_c.h @@ -24,6 +24,10 @@ #include "../SDL_internal.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Useful functions and variables from SDL_events.c */ #include "SDL_events.h" #include "SDL_thread.h" @@ -58,6 +62,11 @@ extern void SDL_SendPendingSignalEvents(void); extern int SDL_QuitInit(void); extern void SDL_QuitQuit(void); +#ifdef __cplusplus +} +#endif + + #endif /* SDL_events_c_h_ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/events/SDL_keyboard_c.h b/src/events/SDL_keyboard_c.h index 16a5fad2e0e72..d0733dfff8f06 100644 --- a/src/events/SDL_keyboard_c.h +++ b/src/events/SDL_keyboard_c.h @@ -26,6 +26,10 @@ #include "SDL_keycode.h" #include "SDL_events.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Initialize the keyboard subsystem */ extern int SDL_KeyboardInit(void); @@ -84,6 +88,10 @@ extern char *SDL_UCS4ToUTF8(Uint32 ch, char *dst); /* Toggle on or off pieces of the keyboard mod state. */ extern void SDL_ToggleModState(const SDL_Keymod modstate, const SDL_bool toggle); +#ifdef __cplusplus +} +#endif + #endif /* SDL_keyboard_c_h_ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/wiiu/SDL_wiiujoystick.c b/src/joystick/wiiu/SDL_wiiujoystick.c index e7a6d18cfdfd7..f6de2a8d74565 100644 --- a/src/joystick/wiiu/SDL_wiiujoystick.c +++ b/src/joystick/wiiu/SDL_wiiujoystick.c @@ -37,9 +37,11 @@ #include "SDL_log.h" #include "SDL_assert.h" #include "SDL_events.h" +#include "SDL_system.h" #include "SDL_wiiujoystick.h" #include "../../video/SDL_sysvideo.h" +#include "../../video/wiiu/SDL_wiiuswkbd.h" //index with device_index, get WIIU_DEVICE* static int deviceMap[MAX_CONTROLLERS]; @@ -152,7 +154,7 @@ static int WIIU_JoystickGetCount(void) static void WIIU_JoystickDetect(void) { /* Make sure there are no dangling instances or device indexes - These checks *should* be unneccesary, remove once battle-tested */ + These checks *should* be unneccesary, remove once battle-tested */ for (int i = 0; i < MAX_CONTROLLERS; i++) { if (deviceMap[i] == WIIU_DEVICE_INVALID && instanceMap[i] != -1) { @@ -464,37 +466,44 @@ static void WIIU_JoystickUpdate(SDL_Joystick *joystick) VPADStatus vpad; VPADReadError error; - VPADTouchData tpdata; - VPADRead(VPAD_CHAN_0, &vpad, 1, &error); + int32_t read; + + read = VPADRead(VPAD_CHAN_0, &vpad, 1, &error); if (error == VPAD_READ_INVALID_CONTROLLER) { /* Gamepad disconnected! */ SDL_PrivateJoystickRemoved(joystick->instance_id); /* Unlink Gamepad, device_index, instance_id */ WIIU_RemoveDevice(WIIU_DEVICE_GAMEPAD); return; - } else if (error != VPAD_READ_SUCCESS) { + } else if (read != 1 || error != VPAD_READ_SUCCESS) { return; } /* touchscreen */ - VPADGetTPCalibratedPoint(VPAD_CHAN_0, &tpdata, &vpad.tpNormal); - if (tpdata.touched) { + VPADGetTPCalibratedPoint(VPAD_CHAN_0, &vpad.tpNormal, &vpad.tpNormal); + + if (SDL_WiiUSetSWKBDVPAD(&vpad)) { + /* Do not generate SDL events when the swkbd is consuming the input. */ + return; + } + + if (vpad.tpNormal.touched) { SDL_Window *window = WIIU_GetGamepadWindow(); if (!last_touched) { /* Send an initial touch */ SDL_SendTouch(0, 0, window, SDL_TRUE, - (float) tpdata.x / 1280.0f, - (float) tpdata.y / 720.0f, 1); + (float) vpad.tpNormal.x / 1280.0f, + (float) vpad.tpNormal.y / 720.0f, 1); } /* Always send the motion */ SDL_SendTouchMotion(0, 0, window, - (float) tpdata.x / 1280.0f, - (float) tpdata.y / 720.0f, 1); + (float) vpad.tpNormal.x / 1280.0f, + (float) vpad.tpNormal.y / 720.0f, 1); /* Update old values */ - last_touch_x = tpdata.x; - last_touch_y = tpdata.y; + last_touch_x = vpad.tpNormal.x; + last_touch_y = vpad.tpNormal.y; last_touched = 1; } else if (last_touched) { SDL_Window *window = WIIU_GetGamepadWindow(); @@ -544,14 +553,21 @@ static void WIIU_JoystickUpdate(SDL_Joystick *joystick) WPADExtensionType ext; KPADStatus kpad; int32_t err; + int32_t read; if (WPADProbe(WIIU_WPAD_CHAN(wiiu_device), &ext) != 0) { /* Do nothing, we'll catch it in Detect() */ return; } - KPADReadEx(WIIU_WPAD_CHAN(wiiu_device), &kpad, 1, &err); - if (err != KPAD_ERROR_OK) return; + read = KPADReadEx(WIIU_WPAD_CHAN(wiiu_device), &kpad, 1, &err); + if (read != 1 || err != KPAD_ERROR_OK) + return; + + if (SDL_WiiUSetSWKBDKPAD(WIIU_WPAD_CHAN(wiiu_device), &kpad)) { + /* Do not generate SDL events when the swkbd is consuming the input. */ + return; + } switch (ext) { case WPAD_EXT_CORE: diff --git a/src/render/wiiu/SDL_rpresent_wiiu.c b/src/render/wiiu/SDL_rpresent_wiiu.c index 849d2bcad67f1..a04d049de7453 100644 --- a/src/render/wiiu/SDL_rpresent_wiiu.c +++ b/src/render/wiiu/SDL_rpresent_wiiu.c @@ -26,6 +26,7 @@ #include "../SDL_sysrender.h" #include "SDL_render_wiiu.h" +#include "../../video/wiiu/SDL_wiiuswkbd.h" #include #include @@ -60,12 +61,35 @@ int WIIU_SDL_SetVSync(SDL_Renderer * renderer, const int vsync) return 0; } +#define WIIU_FIX_SWKBD_GAMMA + int WIIU_SDL_RenderPresent(SDL_Renderer * renderer) { WIIU_RenderData *data = (WIIU_RenderData *) renderer->driverdata; WIIU_TextureData *tdata = (WIIU_TextureData *) data->windowTex.driverdata; Uint32 flags = SDL_GetWindowFlags(renderer->window); + if (WIIU_SWKBD_IsScreenKeyboardShown(NULL, renderer->window)) { + GX2SetContextState(NULL); +#ifdef WIIU_FIX_SWKBD_GAMMA + if (tdata->cbuf.surface.format == GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8) { + GX2SurfaceFormat old_format = tdata->cbuf.surface.format; + tdata->cbuf.surface.format = GX2_SURFACE_FORMAT_SRGB_R8_G8_B8_A8; + GX2InitColorBufferRegs(&tdata->cbuf); + GX2SetColorBuffer(&tdata->cbuf, GX2_RENDER_TARGET_0); + WIIU_SWKBD_Draw(renderer->window); + tdata->cbuf.surface.format = old_format; + GX2InitColorBufferRegs(&tdata->cbuf); + GX2SetColorBuffer(&tdata->cbuf, GX2_RENDER_TARGET_0); + } else { + WIIU_SWKBD_Draw(renderer->window); + } +#else + WIIU_SWKBD_Draw(renderer->window); +#endif + + } + /* Only render to TV if the window is *not* drc-only */ if (!(flags & SDL_WINDOW_WIIU_GAMEPAD_ONLY)) { GX2CopyColorBufferToScanBuffer(&tdata->cbuf, GX2_SCAN_TARGET_TV); diff --git a/src/video/wiiu/SDL_wiiukeyboard.c b/src/video/wiiu/SDL_wiiukeyboard.c index f3e39ffb6c420..6eb6f21abebdb 100644 --- a/src/video/wiiu/SDL_wiiukeyboard.c +++ b/src/video/wiiu/SDL_wiiukeyboard.c @@ -21,6 +21,8 @@ #include "SDL_wiiukeyboard.h" +#include "SDL_wiiuswkbd.h" + #include "../../SDL_internal.h" #include "../../events/SDL_keyboard_c.h" @@ -79,7 +81,7 @@ static void WIIU_SendKeyEventText(KBDKeyEvent *e) utf8[1] = 0x80 | ((symbol >> 6) & 0x3F); utf8[2] = 0x80 | (symbol & 0x3F); } - + SDL_SendKeyboardText((char *)utf8); } @@ -89,17 +91,20 @@ void SDL_WIIU_PumpKeyboardEvents(_THIS) SDL_LockMutex(event_buffer_mutex); - /* process each key event */ - for (i = 0; i < event_buffer.current; i++) { - SDL_SendKeyboardKey( - event_buffer.events[i].isPressedDown ? SDL_PRESSED : SDL_RELEASED, - (SDL_Scancode)event_buffer.events[i].hidCode - ); - - if (event_buffer.events[i].isPressedDown) - WIIU_SendKeyEventText(&event_buffer.events[i]); + /* only generate keyboard and text events if swkbd is not visible */ + if (!WIIU_SWKBD_IsScreenKeyboardShown(NULL, NULL)) { + /* process each key event */ + for (i = 0; i < event_buffer.current; i++) { + SDL_SendKeyboardKey( + event_buffer.events[i].isPressedDown ? SDL_PRESSED : SDL_RELEASED, + (SDL_Scancode)event_buffer.events[i].hidCode + ); + + if (event_buffer.events[i].isPressedDown) + WIIU_SendKeyEventText(&event_buffer.events[i]); + } } - + /* reset the buffer */ event_buffer.current = 0; diff --git a/src/video/wiiu/SDL_wiiuswkbd.cpp b/src/video/wiiu/SDL_wiiuswkbd.cpp new file mode 100644 index 0000000000000..7593a6b102997 --- /dev/null +++ b/src/video/wiiu/SDL_wiiuswkbd.cpp @@ -0,0 +1,821 @@ +/* + Simple DirectMedia Layer + Copyright (C) 2025 Daniel K. O. + + 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 +#include +#include +#include + +#include "SDL_wiiuswkbd.h" + +#include +#include +#include +#include + +#include "SDL_log.h" +#include "SDL_stdinc.h" +#include "SDL_system.h" +#include "SDL_syswm.h" + +#include "../../events/SDL_events_c.h" +#include "../../events/SDL_keyboard_c.h" + +using nn::swkbd::LanguageType; +using nn::swkbd::RegionType; + +namespace +{ + + namespace detail + { + template + struct SDL_Deleter + { + void + operator()(T *ptr) + const + { + if (ptr) { + std::destroy_at(ptr); + SDL_free(ptr); + } + } + }; + + template + using unique_ptr = std::unique_ptr>; + + template + unique_ptr + make_unique(Args &&...args) + { + T *ptr = reinterpret_cast(SDL_malloc(sizeof(T))); + if (!ptr) + return {}; + std::construct_at(ptr, std::forward(args)...); + return unique_ptr{ ptr }; + } + + // Note: we allow null pointers for these. + using raw_string = unique_ptr; + using raw_u8string = unique_ptr; + using raw_u16string = unique_ptr; + + bool + operator==(const raw_string &a, + const char *b) + { + if (a.get() == b) + return true; + if (!a.get() || !b) + return false; + return SDL_strcmp(a.get(), b) == 0; + } + + // RAII class to keep the FS module initialized. + struct FSLib + { + FSLib() + + { + FSInit(); + } + + ~FSLib() + + { + FSShutdown(); + } + }; + std::optional fsLib; + + struct FSClientWrapper : FSClient + { + bool valid; + + FSClientWrapper() + { + auto status = FSAddClient(this, FS_ERROR_FLAG_ALL); + valid = status == FS_STATUS_OK; + } + + // disallow moving + FSClientWrapper(FSClientWrapper &&) = delete; + + ~FSClientWrapper() + + { + FSDelClient(this, FS_ERROR_FLAG_NONE); + } + }; + + namespace create + { + // We cannot call nn::swkbd functions unless a keyboard is created, so we keep + // track of it here. + bool created = false; + + unique_ptr fsClient; + unique_ptr workMemory; + std::optional region; // store region used to create keyboard + + void + cleanup() + { + /* + * Free all memory allocated for the swkbd creation. + * + * Normally we'd reuse the memory in case we need to re-create it in + * another region. But if swkbd is manually disabled, we better actually + * free up all memory. + */ + region.reset(); + fsClient.reset(); + workMemory.reset(); + } + + } // namespace create + + namespace appear + { + + nn::swkbd::AppearArg theArg; + // Keep track of wich window has the swkbd. + SDL_Window *window = nullptr; + + // keyboard config options + nn::swkbd::KeyboardMode keyboardMode = nn::swkbd::KeyboardMode::Full; + raw_u16string okText; + bool showWordSuggestions = true; + // TODO: control disabled inputs, needs to fix nn::swkbd::ConfigArg + // input form options + raw_u16string initialText; + raw_u16string hintText; + nn::swkbd::PasswordMode passwordMode = nn::swkbd::PasswordMode::Clear; + bool highlightInitialText = false; + bool showCopyPasteButtons = false; + bool drawWiiPointer = true; + + void + reset() + { + // Reset all customization after the keyboard is shown. + keyboardMode = nn::swkbd::KeyboardMode::Full; + okText.reset(); + showWordSuggestions = true; + initialText.reset(); + hintText.reset(); + passwordMode = nn::swkbd::PasswordMode::Clear; + highlightInitialText = false; + showCopyPasteButtons = false; + drawWiiPointer = true; + } + + } // namespace appear + + bool enabled = true; + + raw_string swkbdLocale; + + nn::swkbd::ControllerInfo controllerInfo; + + VPADStatus vpad; + std::array kpad; + + SDL_SysWMmsg wmMsgStart; + SDL_SysWMmsg wmMsgFinish; + + // return language, country pair + std::pair + parse_locale(const raw_string &locale) + { + if (!locale) + return {}; + raw_string language{ reinterpret_cast(SDL_calloc(3, 1)) }; + raw_string country{ reinterpret_cast(SDL_calloc(3, 1)) }; + int r = SDL_sscanf(locale.get(), + "%2[Ca-z]_%2[A-Z]", + language.get(), + country.get()); + if (r == 2) + return { std::move(language), std::move(country) }; + if (r == 1) + return { std::move(language), {} }; + return {}; + } + + std::optional + to_language(const raw_string &language, + const raw_string &country) + { + // two-letter codes, from ISO 639 + if (language == "ja") + return LanguageType::Japanese; + if (language == "en") + return LanguageType::English; + if (language == "fr") + return LanguageType::French; + if (language == "de") + return LanguageType::German; + if (language == "it") + return LanguageType::Italian; + if (language == "es") + return LanguageType::Spanish; +#if 0 + // Chinese and Korean languages seem to crash the swkbd. + if (language == "zh") { + if (country == "TW") + return LanguageType::TraditionalChinese; + if (country == "CN") + return LanguageType::SimplifiedChinese; + return LanguageType::TraditionalChinese; + } + if (language == "ko") + return LanguageType::Korean; +#endif + if (language == "nl") + return LanguageType::Dutch; + if (language == "pt") + return LanguageType::Portuguese; + if (language == "ru") + return LanguageType::Russian; + return {}; + } + + std::optional + to_region(const raw_string &language, + const raw_string &country) + { + static const std::array usa_countries = { + "US", + "CA", + "MX", + "BR", + }; + static const std::array eur_countries = { + "DE", + "ES", + "FR", + "GB", + "IT", + "NL", + "PT", + "RU", + }; + static const std::array eur_languages = { + "de", + "es", + "fr", + "it", + "nl", + "pt", + "ru", + }; + + if (country == "JP") + return RegionType::Japan; + + for (auto c : usa_countries) + if (country == c) + return RegionType::USA; + + for (auto c : eur_countries) + if (country == c) + return RegionType::Europe; + +#if 0 + // China, Korea and Taiwan seem to crash the swkbd. + if (country == "CN") + return RegionType::China; + + if (country == "KR" || country == "KP") + return RegionType::Korea; + + if (country == "TW") + return RegionType::Taiwan; +#endif + + // If country doesn't match, return a compatible region based on language alone + if (language == "ja") + return RegionType::Japan; + if (language == "en") + return RegionType::USA; + for (auto lang : eur_languages) + if (language == lang) + return RegionType::Europe; +#if 0 + // China and Korea seem to crash the swkbd. + if (language == "zh") + return RegionType::China; + if (language == "ko") + return RegionType::Korea; +#endif + + return {}; + } + + Uint32 + read_system_config_u32(const char *key) + { + UCHandle handle = UCOpen(); + if (handle < 0) { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, + "UCOpen() returned: %d\n", handle); + return -1; + } + unsigned result; + alignas(0x40) UCSysConfig arg{}; + SDL_strlcpy(arg.name, key, sizeof arg.name); + arg.dataType = UC_DATATYPE_UNSIGNED_INT; + arg.dataSize = sizeof result; + arg.data = &result; + auto status = UCReadSysConfig(handle, 1, &arg); + UCClose(handle); + if (status == UC_ERROR_OK) + return result; + else + return -1; + } + + LanguageType + read_system_language() + { + auto lang = read_system_config_u32("cafe.language"); + if (lang <= 11) + return static_cast(lang); + return LanguageType::English; + } + + std::optional cached_system_language; + + LanguageType + get_language_from_system() + { + if (!cached_system_language) + cached_system_language = read_system_language(); + return *cached_system_language; + } + + RegionType + read_system_region() + { + alignas(64) MCPSysProdSettings settings{}; + MCPError status = 0; + int handle = MCP_Open(); + if (handle < 0) + goto error; + status = MCP_GetSysProdSettings(handle, &settings); + MCP_Close(handle); + if (status) + goto error; + + if (settings.product_area & MCP_REGION_JAPAN) + return RegionType::Japan; + if (settings.product_area & MCP_REGION_USA) + return RegionType::USA; + if (settings.product_area & MCP_REGION_EUROPE) + return RegionType::Europe; + if (settings.product_area & MCP_REGION_CHINA) + return RegionType::China; + if (settings.product_area & MCP_REGION_KOREA) + return RegionType::Korea; + if (settings.product_area & MCP_REGION_TAIWAN) + return RegionType::Taiwan; + + error: + return RegionType::Europe; + } + + std::optional cached_system_region; + + RegionType + get_region_from_system() + { + if (!cached_system_region) + cached_system_region = read_system_region(); + return *cached_system_region; + } + + std::size_t + strlen_16(const char16_t *s) + { + if (!s) + return 0; + std::size_t result = 0; + while (*s++) + ++result; + return result; + } + + raw_u8string + to_utf8(const char16_t *input) + { + if (!input) + return {}; + auto output = SDL_iconv_string("UTF-8", + "UTF-16BE", + reinterpret_cast(input), + 2 * (strlen_16(input) + 1)); + return raw_u8string{ reinterpret_cast(output) }; + } + + raw_u16string + to_utf16(const char *input) + { + if (!input) + return {}; + auto output = SDL_iconv_string("UTF-16BE", + "UTF-8", + input, + SDL_strlen(input) + 1); + return raw_u16string{ reinterpret_cast(output) }; + } + + } // namespace detail +} // namespace + +void WIIU_SWKBD_Initialize(void) +{ + if (detail::create::created) + return; + + if (!detail::enabled) + return; + + if (!detail::fsLib) + detail::fsLib.emplace(); + + nn::swkbd::CreateArg arg; + + auto [language, country] = detail::parse_locale(detail::swkbdLocale); + if (auto region = detail::to_region(language, country)) + arg.regionType = *region; + else + arg.regionType = detail::get_region_from_system(); + + auto local_fsClient = std::move(detail::create::fsClient); + if (!arg.fsClient) { + if (!local_fsClient) + local_fsClient = detail::make_unique(); + if (!local_fsClient) { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not create FSClientfor nn::swkbd\n"); + return; + } + arg.fsClient = local_fsClient.get(); + } else { + // user provided their own fsClient, so destroy the internal one + local_fsClient.reset(); + } + + auto local_workMemory = std::move(detail::create::workMemory); + if (!arg.workMemory) { + local_workMemory.reset(reinterpret_cast(SDL_malloc(nn::swkbd::GetWorkMemorySize(0)))); + if (!local_workMemory) { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not allocate memory for nn::swkbd\n"); + return; + } + arg.workMemory = local_workMemory.get(); + } else { + // user provided their own workMemory, so destroy the internal one + local_workMemory.reset(); + } + + if (!nn::swkbd::Create(arg)) { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "nn::swkbd::Create() failed\n"); + return; + } + + detail::create::workMemory = std::move(local_workMemory); + detail::create::fsClient = std::move(local_fsClient); + detail::create::region = arg.regionType; + detail::create::created = true; +} + +void WIIU_SWKBD_Finalize(void) +{ + if (!detail::create::created) + return; + + nn::swkbd::Destroy(); + detail::appear::window = nullptr; + detail::create::region.reset(); + detail::create::created = false; +} + +void WIIU_SWKBD_Calc(void) +{ + if (!detail::create::created) + return; + + if (!detail::enabled) + return; + + nn::swkbd::Calc(detail::controllerInfo); + detail::controllerInfo = {}; + + // TODO: these could go into a background thread + if (nn::swkbd::IsNeedCalcSubThreadFont()) + nn::swkbd::CalcSubThreadFont(); + if (nn::swkbd::IsNeedCalcSubThreadPredict()) + nn::swkbd::CalcSubThreadPredict(); + + if (detail::appear::window) { + nn::swkbd::State state = nn::swkbd::GetStateInputForm(); + if (state == nn::swkbd::State::Hidden) + detail::appear::window = nullptr; + } + + // Check if user confirmed input. + if (nn::swkbd::IsDecideOkButton(nullptr)) { + // Send an event before we send out the string. + SDL_VERSION(&detail::wmMsgStart.version); + detail::wmMsgStart.subsystem = SDL_SYSWM_WIIU; + detail::wmMsgStart.msg.wiiu.event = SDL_WIIU_SYSWM_SWKBD_OK_START_EVENT; + SDL_SendSysWMEvent(&detail::wmMsgStart); + + auto str16 = nn::swkbd::GetInputFormString(); + if (str16) { + auto str8 = detail::to_utf8(str16); + if (str8) + SDL_SendKeyboardText(reinterpret_cast(str8.get())); + else + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "could not convert utf-16 to utf-8\n"); + } + + // Send an event after the string. + SDL_VERSION(&detail::wmMsgFinish.version); + detail::wmMsgFinish.subsystem = SDL_SYSWM_WIIU; + detail::wmMsgFinish.msg.wiiu.event = SDL_WIIU_SYSWM_SWKBD_OK_FINISH_EVENT; + SDL_SendSysWMEvent(&detail::wmMsgFinish); + + // WIIU_SWKBD_HideScreenKeyboard(nullptr, nullptr); + } + + if (nn::swkbd::IsDecideCancelButton(nullptr)) { + WIIU_SWKBD_HideScreenKeyboard(nullptr, nullptr); + SDL_VERSION(&detail::wmMsgFinish.version); + detail::wmMsgFinish.subsystem = SDL_SYSWM_WIIU; + detail::wmMsgFinish.msg.wiiu.event = SDL_WIIU_SYSWM_SWKBD_CANCEL_EVENT; + SDL_SendSysWMEvent(&detail::wmMsgFinish); + + // WIIU_SWKBD_HideScreenKeyboard(nullptr, nullptr); + } +} + +void WIIU_SWKBD_Draw(SDL_Window *window) +{ + if (window != detail::appear::window) + return; + + if (!detail::enabled) + return; + + nn::swkbd::State state = nn::swkbd::GetStateInputForm(); + if (state == nn::swkbd::State::Hidden) + return; + + if (window->flags & SDL_WINDOW_WIIU_TV_ONLY) + nn::swkbd::DrawTV(); + else + nn::swkbd::DrawDRC(); +} + +SDL_bool WIIU_SWKBD_HasScreenKeyboardSupport(_THIS) +{ + if (!detail::enabled) + return SDL_FALSE; + return SDL_TRUE; +} + +void WIIU_SWKBD_ShowScreenKeyboard(_THIS, SDL_Window *window) +{ + if (!detail::enabled) + return; + + WIIU_SWKBD_Initialize(); + + if (!detail::appear::window) + detail::appear::window = window; + + nn::swkbd::AppearArg &arg = detail::appear::theArg; + arg = nn::swkbd::AppearArg{}; // reset all values to default + // Set language + auto [language, country] = detail::parse_locale(detail::swkbdLocale); + if (auto lang = detail::to_language(language, country)) + arg.keyboardArg.configArg.languageType = *lang; + else + arg.keyboardArg.configArg.languageType = detail::get_language_from_system(); + + // Set keyboard mode + arg.keyboardArg.configArg.keyboardMode = detail::appear::keyboardMode; + + // Set OK text + if (detail::appear::okText) + arg.keyboardArg.configArg.okString = detail::appear::okText.get(); + + // Set word suggestions + arg.keyboardArg.configArg.showWordSuggestions = detail::appear::showWordSuggestions; + + // Set show Wii pointer + arg.keyboardArg.configArg.drawSysWiiPointer = detail::appear::drawWiiPointer; + + // Set initial text + if (detail::appear::initialText) + arg.inputFormArg.initialText = detail::appear::initialText.get(); + + // Set hint text + if (detail::appear::hintText) + arg.inputFormArg.hintText = detail::appear::hintText.get(); + + // Set password mode + arg.inputFormArg.passwordMode = detail::appear::passwordMode; + + // Set highlight initial text + // Note the typo, must fix this after we fix WUT + arg.inputFormArg.higlightInitialText = detail::appear::highlightInitialText; + + // Set show copy paste buttons + arg.inputFormArg.showCopyPasteButtons = detail::appear::showCopyPasteButtons; + + if (window->flags & SDL_WINDOW_WIIU_TV_ONLY) + arg.keyboardArg.configArg.controllerType = nn::swkbd::ControllerType::WiiRemote0; + else + arg.keyboardArg.configArg.controllerType = nn::swkbd::ControllerType::DrcGamepad; + + nn::swkbd::AppearInputForm(arg); + detail::appear::reset(); +} + +void WIIU_SWKBD_HideScreenKeyboard(_THIS, SDL_Window *) +{ + if (!detail::create::created) + return; + nn::swkbd::DisappearInputForm(); + // detail::appear::window = nullptr; +} + +SDL_bool WIIU_SWKBD_IsScreenKeyboardShown(_THIS, SDL_Window *window) +{ + if (!detail::create::created) + return SDL_FALSE; + + if (window != detail::appear::window) + return SDL_FALSE; + + nn::swkbd::State state = nn::swkbd::GetStateInputForm(); + if (state != nn::swkbd::State::Hidden) + return SDL_TRUE; + + return SDL_FALSE; +} + +void SDL_WiiUSetSWKBDEnabled(SDL_bool enabled) +{ + if (detail::enabled != !!enabled) { + detail::enabled = enabled; + if (!detail::enabled) { + // If application is turning swkbd off, we better free up all memory too. + WIIU_SWKBD_Finalize(); + detail::create::cleanup(); + } + } +} + +void SDL_WiiUSetSWKBDKeyboardMode(SDL_WiiUSWKBDKeyboardMode mode) +{ + switch (mode) { + case SDL_WIIU_SWKBD_KEYBOARD_MODE_FULL: + detail::appear::keyboardMode = nn::swkbd::KeyboardMode::Full; + break; + case SDL_WIIU_SWKBD_KEYBOARD_MODE_NUMPAD: + detail::appear::keyboardMode = nn::swkbd::KeyboardMode::Numpad; + break; + case SDL_WIIU_SWKBD_KEYBOARD_MODE_RESTRICTED: + detail::appear::keyboardMode = nn::swkbd::KeyboardMode::Utf8; + break; + case SDL_WIIU_SWKBD_KEYBOARD_MODE_NNID: + detail::appear::keyboardMode = nn::swkbd::KeyboardMode::NNID; + break; + default: + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, + "set swkbd keyboard mode failed: invalid mode %d\n", mode); + } +} + +void SDL_WiiUSetSWKBDOKLabel(const char *label) +{ + detail::appear::okText = detail::to_utf16(label); +} + +void SDL_WiiUSetSWKBDShowWordSuggestions(SDL_bool show) +{ + detail::appear::showWordSuggestions = show; +} + +void SDL_WiiUSetSWKBDInitialText(const char *text) +{ + detail::appear::initialText = detail::to_utf16(text); +} + +void SDL_WiiUSetSWKBDHintText(const char *text) +{ + detail::appear::hintText = detail::to_utf16(text); +} + +void SDL_WiiUSetSWKBDPasswordMode(SDL_WiiUSWKBDPasswordMode mode) +{ + switch (mode) { + case SDL_WIIU_SWKBD_PASSWORD_MODE_SHOW: + detail::appear::passwordMode = nn::swkbd::PasswordMode::Clear; + break; + case SDL_WIIU_SWKBD_PASSWORD_MODE_HIDE: + detail::appear::passwordMode = nn::swkbd::PasswordMode::Hide; + break; + case SDL_WIIU_SWKBD_PASSWORD_MODE_FADE: + detail::appear::passwordMode = nn::swkbd::PasswordMode::Fade; + break; + default: + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, + "set swkbd password failed: invalid mode %d\n", mode); + } +} + +void SDL_WiiUSetSWKBDHighlightInitialText(SDL_bool highlight) +{ + detail::appear::highlightInitialText = highlight; +} + +void SDL_WiiUSetSWKBDShowCopyPasteButtons(SDL_bool show) +{ + detail::appear::showCopyPasteButtons = show; +} + +void SDL_WiiUSetSWKBDDrawWiiPointer(SDL_bool draw) +{ + detail::appear::drawWiiPointer = draw; +} + +void SDL_WiiUSetSWKBDLocale(const char *locale) +{ + // Don't do anything if the locale didn't change. + if (detail::swkbdLocale == locale) + return; + WIIU_SWKBD_Finalize(); + if (locale) + detail::swkbdLocale.reset(SDL_strdup(locale)); + else + detail::swkbdLocale.reset(); +} + +SDL_bool SDL_WiiUSetSWKBDVPAD(const void *vpad) +{ + if (!detail::create::created) + return SDL_FALSE; + + nn::swkbd::State state = nn::swkbd::GetStateInputForm(); + if (state != nn::swkbd::State::Visible) + return SDL_FALSE; + + // printf("swkbd is consuming vpad input, window=%p\n", detail::appear::window); + detail::vpad = *reinterpret_cast(vpad); + detail::controllerInfo.vpad = &detail::vpad; + return SDL_TRUE; +} + +SDL_bool SDL_WiiUSetSWKBDKPAD(int channel, const void *kpad) +{ + if (!detail::create::created) + return SDL_FALSE; + + if (channel < 0 || channel > 3) + return SDL_FALSE; + + nn::swkbd::State state = nn::swkbd::GetStateInputForm(); + if (state != nn::swkbd::State::Visible) + return SDL_FALSE; + + // printf("swkbd is consuming kpad input\n"); + detail::kpad[channel] = *reinterpret_cast(kpad); + detail::controllerInfo.kpad[channel] = &detail::kpad[channel]; + return SDL_TRUE; +} diff --git a/src/video/wiiu/SDL_wiiuswkbd.h b/src/video/wiiu/SDL_wiiuswkbd.h new file mode 100644 index 0000000000000..31ff92e195765 --- /dev/null +++ b/src/video/wiiu/SDL_wiiuswkbd.h @@ -0,0 +1,55 @@ +/* + Simple DirectMedia Layer + Copyright (C) 2025 Daniel K. O. + + 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_wiiuswkbd_h +#define SDL_wiiuswkbd_h + +#include "../../SDL_internal.h" +#include "../SDL_sysvideo.h" + +#include +#include + +#if SDL_VIDEO_DRIVER_WIIU + +#ifdef __cplusplus +extern "C" { +#endif + +void WIIU_SWKBD_Initialize(void); +void WIIU_SWKBD_Finalize(void); + +void WIIU_SWKBD_Calc(void); + +void WIIU_SWKBD_Draw(SDL_Window *window); + +SDL_bool WIIU_SWKBD_HasScreenKeyboardSupport(_THIS); +void WIIU_SWKBD_ShowScreenKeyboard(_THIS, SDL_Window *window); +void WIIU_SWKBD_HideScreenKeyboard(_THIS, SDL_Window *window); +SDL_bool WIIU_SWKBD_IsScreenKeyboardShown(_THIS, SDL_Window *window); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SDL_VIDEO_DRIVER_WIIU */ + +#endif /* SDL_wiiu_swkbd_h */ diff --git a/src/video/wiiu/SDL_wiiuvideo.c b/src/video/wiiu/SDL_wiiuvideo.c index 1a428c0432c49..b2fa44eafdee2 100644 --- a/src/video/wiiu/SDL_wiiuvideo.c +++ b/src/video/wiiu/SDL_wiiuvideo.c @@ -38,6 +38,7 @@ #include "SDL_wiiuvideo.h" #include "SDL_wiiukeyboard.h" #include "SDL_wiiu_gfx_heap.h" +#include "SDL_wiiuswkbd.h" #include "../../render/wiiu/SDL_render_wiiu.h" @@ -291,6 +292,8 @@ static void WIIU_VideoQuit(_THIS) { WIIU_VideoData *videodata = (WIIU_VideoData *) _this->driverdata; + WIIU_SWKBD_Finalize(); + if (videodata->handleProcUI) { // Put ProcUI into EXIT/shutdown state if user stopped processing events // before SDL_QUIT was generated. @@ -407,6 +410,7 @@ static void WIIU_PumpEvents(_THIS) } SDL_WIIU_PumpKeyboardEvents(_this); + WIIU_SWKBD_Calc(); } static void WIIU_DeleteDevice(_THIS) @@ -446,6 +450,11 @@ static SDL_VideoDevice *WIIU_CreateDevice(void) device->SetDisplayMode = WIIU_SetDisplayMode; device->PumpEvents = WIIU_PumpEvents; + device->HasScreenKeyboardSupport = WIIU_SWKBD_HasScreenKeyboardSupport; + device->ShowScreenKeyboard = WIIU_SWKBD_ShowScreenKeyboard; + device->HideScreenKeyboard = WIIU_SWKBD_HideScreenKeyboard; + device->IsScreenKeyboardShown = WIIU_SWKBD_IsScreenKeyboardShown; + device->free = WIIU_DeleteDevice; return device;