mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-05-03 23:00:14 +02:00
Most SDL functions used to indicate success or failure using an int return code. These functions have been changed to return SDL_bool. Here is a coccinelle patch to change code that previously compared the return value to 0 and changes it to a boolean test: @ bool_return_type @ identifier func =~ "^(SDL_AddEventWatch|SDL_AddHintCallback|SDL_AddSurfaceAlternateImage|SDL_AddVulkanRenderSemaphores|SDL_BindAudioStream|SDL_BindAudioStreams|SDL_BlitSurface|SDL_BlitSurface9Grid|SDL_BlitSurfaceScaled|SDL_BlitSurfaceTiled|SDL_BlitSurfaceTiledWithScale|SDL_BlitSurfaceUnchecked|SDL_BlitSurfaceUncheckedScaled|SDL_CaptureMouse|SDL_ClearAudioStream|SDL_ClearClipboardData|SDL_ClearComposition|SDL_ClearError|SDL_ClearProperty|SDL_ClearSurface|SDL_CloseIO|SDL_CloseStorage|SDL_ConvertAudioSamples|SDL_ConvertEventToRenderCoordinates|SDL_ConvertPixels|SDL_ConvertPixelsAndColorspace|SDL_CopyFile|SDL_CopyProperties|SDL_CopyStorageFile|SDL_CreateDirectory|SDL_CreateStorageDirectory|SDL_CreateWindowAndRenderer|SDL_DateTimeToTime|SDL_DestroyWindowSurface|SDL_DetachVirtualJoystick|SDL_DisableScreenSaver|SDL_EnableScreenSaver|SDL_EnumerateDirectory|SDL_EnumerateProperties|SDL_EnumerateStorageDirectory|SDL_FillSurfaceRect|SDL_FillSurfaceRects|SDL_FlashWindow|SDL_FlipSurface|SDL_FlushAudioStream|SDL_FlushRenderer|SDL_GL_DestroyContext|SDL_GL_GetAttribute|SDL_GL_GetSwapInterval|SDL_GL_LoadLibrary|SDL_GL_MakeCurrent|SDL_GL_SetAttribute|SDL_GL_SetSwapInterval|SDL_GL_SwapWindow|SDL_GetAudioDeviceFormat|SDL_GetAudioStreamFormat|SDL_GetCameraFormat|SDL_GetClosestFullscreenDisplayMode|SDL_GetCurrentRenderOutputSize|SDL_GetCurrentTime|SDL_GetDXGIOutputInfo|SDL_GetDateTimeLocalePreferences|SDL_GetDisplayBounds|SDL_GetDisplayUsableBounds|SDL_GetGDKDefaultUser|SDL_GetGDKTaskQueue|SDL_GetGamepadSensorData|SDL_GetGamepadTouchpadFinger|SDL_GetHapticEffectStatus|SDL_GetJoystickBall|SDL_GetMasksForPixelFormat|SDL_GetPathInfo|SDL_GetRectUnion|SDL_GetRectUnionFloat|SDL_GetRenderClipRect|SDL_GetRenderColorScale|SDL_GetRenderDrawBlendMode|SDL_GetRenderDrawColor|SDL_GetRenderDrawColorFloat|SDL_GetRenderLogicalPresentation|SDL_GetRenderLogicalPresentationRect|SDL_GetRenderOutputSize|SDL_GetRenderSafeArea|SDL_GetRenderScale|SDL_GetRenderVSync|SDL_GetRenderViewport|SDL_GetSensorData|SDL_GetStorageFileSize|SDL_GetStoragePathInfo|SDL_GetSurfaceAlphaMod|SDL_GetSurfaceBlendMode|SDL_GetSurfaceClipRect|SDL_GetSurfaceColorKey|SDL_GetSurfaceColorMod|SDL_GetTextInputArea|SDL_GetTextureAlphaMod|SDL_GetTextureAlphaModFloat|SDL_GetTextureBlendMode|SDL_GetTextureColorMod|SDL_GetTextureColorModFloat|SDL_GetTextureScaleMode|SDL_GetTextureSize|SDL_GetWindowAspectRatio|SDL_GetWindowBordersSize|SDL_GetWindowMaximumSize|SDL_GetWindowMinimumSize|SDL_GetWindowPosition|SDL_GetWindowRelativeMouseMode|SDL_GetWindowSafeArea|SDL_GetWindowSize|SDL_GetWindowSizeInPixels|SDL_GetWindowSurfaceVSync|SDL_HideCursor|SDL_HideWindow|SDL_Init|SDL_InitHapticRumble|SDL_InitSubSystem|SDL_LoadWAV|SDL_LoadWAV_IO|SDL_LockAudioStream|SDL_LockProperties|SDL_LockSurface|SDL_LockTexture|SDL_LockTextureToSurface|SDL_MaximizeWindow|SDL_MinimizeWindow|SDL_MixAudio|SDL_OpenURL|SDL_OutOfMemory|SDL_PauseAudioDevice|SDL_PauseAudioStreamDevice|SDL_PauseHaptic|SDL_PlayHapticRumble|SDL_PremultiplyAlpha|SDL_PremultiplySurfaceAlpha|SDL_PushEvent|SDL_PutAudioStreamData|SDL_RaiseWindow|SDL_ReadStorageFile|SDL_ReadSurfacePixel|SDL_ReadSurfacePixelFloat|SDL_RegisterApp|SDL_ReloadGamepadMappings|SDL_RemovePath|SDL_RemoveStoragePath|SDL_RemoveTimer|SDL_RenamePath|SDL_RenameStoragePath|SDL_RenderClear|SDL_RenderCoordinatesFromWindow|SDL_RenderCoordinatesToWindow|SDL_RenderFillRect|SDL_RenderFillRects|SDL_RenderGeometry|SDL_RenderGeometryRaw|SDL_RenderLine|SDL_RenderLines|SDL_RenderPoint|SDL_RenderPoints|SDL_RenderPresent|SDL_RenderRect|SDL_RenderRects|SDL_RenderTexture|SDL_RenderTexture9Grid|SDL_RenderTextureRotated|SDL_RenderTextureTiled|SDL_RequestAndroidPermission|SDL_RestoreWindow|SDL_ResumeAudioDevice|SDL_ResumeAudioStreamDevice|SDL_ResumeHaptic|SDL_RumbleGamepad|SDL_RumbleGamepadTriggers|SDL_RumbleJoystick|SDL_RumbleJoystickTriggers|SDL_RunHapticEffect|SDL_SaveBMP|SDL_SaveBMP_IO|SDL_SendAndroidMessage|SDL_SendGamepadEffect|SDL_SendJoystickEffect|SDL_SendJoystickVirtualSensorData|SDL_SetAppMetadata|SDL_SetAppMetadataProperty|SDL_SetAudioDeviceGain|SDL_SetAudioPostmixCallback|SDL_SetAudioStreamFormat|SDL_SetAudioStreamFrequencyRatio|SDL_SetAudioStreamGain|SDL_SetAudioStreamGetCallback|SDL_SetAudioStreamInputChannelMap|SDL_SetAudioStreamOutputChannelMap|SDL_SetAudioStreamPutCallback|SDL_SetBooleanProperty|SDL_SetClipboardData|SDL_SetClipboardText|SDL_SetCursor|SDL_SetFloatProperty|SDL_SetGamepadLED|SDL_SetGamepadMapping|SDL_SetGamepadPlayerIndex|SDL_SetGamepadSensorEnabled|SDL_SetHapticAutocenter|SDL_SetHapticGain|SDL_SetJoystickLED|SDL_SetJoystickPlayerIndex|SDL_SetJoystickVirtualAxis|SDL_SetJoystickVirtualBall|SDL_SetJoystickVirtualButton|SDL_SetJoystickVirtualHat|SDL_SetJoystickVirtualTouchpad|SDL_SetLinuxThreadPriority|SDL_SetLinuxThreadPriorityAndPolicy|SDL_SetLogPriorityPrefix|SDL_SetMemoryFunctions|SDL_SetNumberProperty|SDL_SetPaletteColors|SDL_SetPointerProperty|SDL_SetPointerPropertyWithCleanup|SDL_SetPrimarySelectionText|SDL_SetRenderClipRect|SDL_SetRenderColorScale|SDL_SetRenderDrawBlendMode|SDL_SetRenderDrawColor|SDL_SetRenderDrawColorFloat|SDL_SetRenderLogicalPresentation|SDL_SetRenderScale|SDL_SetRenderTarget|SDL_SetRenderVSync|SDL_SetRenderViewport|SDL_SetScancodeName|SDL_SetStringProperty|SDL_SetSurfaceAlphaMod|SDL_SetSurfaceBlendMode|SDL_SetSurfaceColorKey|SDL_SetSurfaceColorMod|SDL_SetSurfaceColorspace|SDL_SetSurfacePalette|SDL_SetSurfaceRLE|SDL_SetTLS|SDL_SetTextInputArea|SDL_SetTextureAlphaMod|SDL_SetTextureAlphaModFloat|SDL_SetTextureBlendMode|SDL_SetTextureColorMod|SDL_SetTextureColorModFloat|SDL_SetTextureScaleMode|SDL_SetThreadPriority|SDL_SetWindowAlwaysOnTop|SDL_SetWindowAspectRatio|SDL_SetWindowBordered|SDL_SetWindowFocusable|SDL_SetWindowFullscreen|SDL_SetWindowFullscreenMode|SDL_SetWindowHitTest|SDL_SetWindowIcon|SDL_SetWindowKeyboardGrab|SDL_SetWindowMaximumSize|SDL_SetWindowMinimumSize|SDL_SetWindowModalFor|SDL_SetWindowMouseGrab|SDL_SetWindowMouseRect|SDL_SetWindowOpacity|SDL_SetWindowPosition|SDL_SetWindowRelativeMouseMode|SDL_SetWindowResizable|SDL_SetWindowShape|SDL_SetWindowSize|SDL_SetWindowSurfaceVSync|SDL_SetWindowTitle|SDL_SetiOSAnimationCallback|SDL_ShowAndroidToast|SDL_ShowCursor|SDL_ShowMessageBox|SDL_ShowSimpleMessageBox|SDL_ShowWindow|SDL_ShowWindowSystemMenu|SDL_StartTextInput|SDL_StartTextInputWithProperties|SDL_StopHapticEffect|SDL_StopHapticEffects|SDL_StopHapticRumble|SDL_StopTextInput|SDL_SyncWindow|SDL_TimeToDateTime|SDL_TryLockMutex|SDL_TryLockRWLockForReading|SDL_TryLockRWLockForWriting|SDL_TryWaitSemaphore|SDL_UnlockAudioStream|SDL_UpdateHapticEffect|SDL_UpdateNVTexture|SDL_UpdateTexture|SDL_UpdateWindowSurface|SDL_UpdateWindowSurfaceRects|SDL_UpdateYUVTexture|SDL_Vulkan_CreateSurface|SDL_Vulkan_LoadLibrary|SDL_WaitConditionTimeout|SDL_WaitSemaphoreTimeout|SDL_WarpMouseGlobal|SDL_WriteStorageFile|SDL_WriteSurfacePixel|SDL_WriteSurfacePixelFloat)$"; @@ ( func( ... ) - == 0 | - func( + !func( ... ) - < 0 | - func( + !func( ... ) - != 0 | - func( + !func( ... ) - == -1 )
322 lines
10 KiB
C++
322 lines
10 KiB
C++
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
|
|
|
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.
|
|
*/
|
|
/*
|
|
Screen keyboard and text input backend
|
|
for GDK platforms.
|
|
*/
|
|
#include "SDL_internal.h"
|
|
#include "SDL_gdktextinput.h"
|
|
|
|
#ifdef SDL_GDK_TEXTINPUT
|
|
|
|
// GDK headers are weird here
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#include <Windows.h>
|
|
#include <XGameUI.h>
|
|
#include <XUser.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include "../../events/SDL_keyboard_c.h"
|
|
#include "../windows/SDL_windowsvideo.h"
|
|
|
|
// TODO: Have a separate task queue for text input perhaps?
|
|
static XTaskQueueHandle g_TextTaskQueue = NULL;
|
|
// Global because there can be only one text entry shown at once.
|
|
static XAsyncBlock *g_TextBlock = NULL;
|
|
|
|
// Creation parameters
|
|
static bool g_DidRegisterHints = false;
|
|
static char *g_TitleText = NULL;
|
|
static char *g_DescriptionText = NULL;
|
|
static char *g_DefaultText = NULL;
|
|
static const Sint32 g_DefaultTextInputScope = (Sint32)XGameUiTextEntryInputScope::Default;
|
|
static Sint32 g_TextInputScope = g_DefaultTextInputScope;
|
|
static const Sint32 g_DefaultMaxTextLength = 1024; // as per doc: maximum allowed amount on consoles
|
|
static Sint32 g_MaxTextLength = g_DefaultMaxTextLength;
|
|
|
|
static void SDLCALL GDK_InternalHintCallback(
|
|
void *userdata,
|
|
const char *name,
|
|
const char *oldValue,
|
|
const char *newValue)
|
|
{
|
|
if (!userdata) {
|
|
return;
|
|
}
|
|
|
|
// oldValue is ignored because we store it ourselves.
|
|
// name is ignored because we deduce it from userdata
|
|
|
|
if (userdata == &g_TextInputScope || userdata == &g_MaxTextLength) {
|
|
// int32 hint
|
|
Sint32 intValue = (!newValue || newValue[0] == '\0') ? 0 : SDL_atoi(newValue);
|
|
if (userdata == &g_MaxTextLength && intValue <= 0) {
|
|
intValue = g_DefaultMaxTextLength;
|
|
} else if (userdata == &g_TextInputScope && intValue < 0) {
|
|
intValue = g_DefaultTextInputScope;
|
|
}
|
|
|
|
*(Sint32 *)userdata = intValue;
|
|
} else {
|
|
// string hint
|
|
if (!newValue || newValue[0] == '\0') {
|
|
// treat empty or NULL strings as just NULL for this impl
|
|
SDL_free(*(char **)userdata);
|
|
*(char **)userdata = NULL;
|
|
} else {
|
|
char *newString = SDL_strdup(newValue);
|
|
if (newString) {
|
|
// free previous value and write the new one
|
|
SDL_free(*(char **)userdata);
|
|
*(char **)userdata = newString;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool GDK_InternalEnsureTaskQueue(void)
|
|
{
|
|
if (!g_TextTaskQueue) {
|
|
if (!SDL_GetGDKTaskQueue(&g_TextTaskQueue)) {
|
|
// SetError will be done for us.
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void CALLBACK GDK_InternalTextEntryCallback(XAsyncBlock *asyncBlock)
|
|
{
|
|
HRESULT hR = S_OK;
|
|
Uint32 resultSize = 0;
|
|
Uint32 resultUsed = 0;
|
|
char *resultBuffer = NULL;
|
|
|
|
// The keyboard will be already hidden when we reach this code
|
|
|
|
if (FAILED(hR = XGameUiShowTextEntryResultSize(
|
|
asyncBlock,
|
|
&resultSize))) {
|
|
SDL_SetError("XGameUiShowTextEntryResultSize failure with HRESULT of %08X", hR);
|
|
} else if (resultSize > 0) {
|
|
// +1 to be super sure that the buffer will be null terminated
|
|
resultBuffer = (char *)SDL_calloc(sizeof(*resultBuffer), 1 + (size_t)resultSize);
|
|
if (resultBuffer) {
|
|
// still pass the original size that we got from ResultSize
|
|
if (FAILED(hR = XGameUiShowTextEntryResult(
|
|
asyncBlock,
|
|
resultSize,
|
|
resultBuffer,
|
|
&resultUsed))) {
|
|
SDL_SetError("XGameUiShowTextEntryResult failure with HRESULT of %08X", hR);
|
|
}
|
|
// check that we have some text and that we weren't cancelled
|
|
else if (resultUsed > 0 && resultBuffer[0] != '\0') {
|
|
// it's null terminated so it's fine
|
|
SDL_SendKeyboardText(resultBuffer);
|
|
}
|
|
// we're done with the buffer
|
|
SDL_free(resultBuffer);
|
|
resultBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
// free the async block after we're done
|
|
SDL_free(asyncBlock);
|
|
asyncBlock = NULL;
|
|
g_TextBlock = NULL; // once we do this we're fully done with the keyboard
|
|
}
|
|
|
|
void GDK_EnsureHints(void)
|
|
{
|
|
if (g_DidRegisterHints == false) {
|
|
SDL_AddHintCallback(
|
|
SDL_HINT_GDK_TEXTINPUT_TITLE,
|
|
GDK_InternalHintCallback,
|
|
&g_TitleText);
|
|
SDL_AddHintCallback(
|
|
SDL_HINT_GDK_TEXTINPUT_DESCRIPTION,
|
|
GDK_InternalHintCallback,
|
|
&g_DescriptionText);
|
|
SDL_AddHintCallback(
|
|
SDL_HINT_GDK_TEXTINPUT_DEFAULT_TEXT,
|
|
GDK_InternalHintCallback,
|
|
&g_DefaultText);
|
|
SDL_AddHintCallback(
|
|
SDL_HINT_GDK_TEXTINPUT_SCOPE,
|
|
GDK_InternalHintCallback,
|
|
&g_TextInputScope);
|
|
SDL_AddHintCallback(
|
|
SDL_HINT_GDK_TEXTINPUT_MAX_LENGTH,
|
|
GDK_InternalHintCallback,
|
|
&g_MaxTextLength);
|
|
g_DidRegisterHints = true;
|
|
}
|
|
}
|
|
|
|
bool GDK_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
|
|
{
|
|
/*
|
|
* Currently a stub, since all input is handled by the virtual keyboard,
|
|
* but perhaps when implementing XGameUiTextEntryOpen in the future
|
|
* you will need this.
|
|
*
|
|
* Also XGameUiTextEntryOpen docs say that it is
|
|
* "not implemented on desktop" so... no thanks.
|
|
*
|
|
* Right now this function isn't implemented on Desktop
|
|
* and seems to be present only in the docs? So I didn't bother.
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
bool GDK_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
|
|
{
|
|
// See notice in GDK_StartTextInput
|
|
return true;
|
|
}
|
|
|
|
bool GDK_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
|
|
{
|
|
/*
|
|
* XGameUiShowTextEntryAsync does not allow you to set
|
|
* the position of the virtual keyboard window.
|
|
*
|
|
* However, XGameUiTextEntryOpen seems to allow that,
|
|
* but again, see notice in GDK_StartTextInput.
|
|
*
|
|
* Right now it's a stub which may be useful later.
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
bool GDK_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window)
|
|
{
|
|
// See notice in GDK_StartTextInput
|
|
return true;
|
|
}
|
|
|
|
bool GDK_HasScreenKeyboardSupport(SDL_VideoDevice *_this)
|
|
{
|
|
// Currently always true for this input method
|
|
return true;
|
|
}
|
|
|
|
void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
|
|
{
|
|
/*
|
|
* There is XGameUiTextEntryOpen but it's only in online docs,
|
|
* My October Update 1 GDKX installation does not have this function defined
|
|
* and as such I decided not to use it at all, since some folks might use even older GDKs.
|
|
*
|
|
* That means the only text input option for us is a simple virtual keyboard widget.
|
|
*/
|
|
|
|
HRESULT hR = S_OK;
|
|
|
|
if (g_TextBlock) {
|
|
// already showing the keyboard
|
|
return;
|
|
}
|
|
|
|
if (!GDK_InternalEnsureTaskQueue()) {
|
|
// unable to obtain the SDL GDK queue
|
|
return;
|
|
}
|
|
|
|
g_TextBlock = (XAsyncBlock *)SDL_calloc(1, sizeof(*g_TextBlock));
|
|
if (!g_TextBlock) {
|
|
return;
|
|
}
|
|
|
|
XGameUiTextEntryInputScope scope;
|
|
switch (SDL_GetTextInputType(props)) {
|
|
default:
|
|
case SDL_TEXTINPUT_TYPE_TEXT:
|
|
scope = (XGameUiTextEntryInputScope)g_TextInputScope;
|
|
break;
|
|
case SDL_TEXTINPUT_TYPE_TEXT_NAME:
|
|
scope = XGameUiTextEntryInputScope::Default;
|
|
break;
|
|
case SDL_TEXTINPUT_TYPE_TEXT_EMAIL:
|
|
scope = XGameUiTextEntryInputScope::EmailSmtpAddress;
|
|
break;
|
|
case SDL_TEXTINPUT_TYPE_TEXT_USERNAME:
|
|
scope = XGameUiTextEntryInputScope::Default;
|
|
break;
|
|
case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN:
|
|
scope = XGameUiTextEntryInputScope::Password;
|
|
break;
|
|
case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE:
|
|
scope = XGameUiTextEntryInputScope::Default;
|
|
break;
|
|
case SDL_TEXTINPUT_TYPE_NUMBER:
|
|
scope = XGameUiTextEntryInputScope::Number;
|
|
break;
|
|
case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN:
|
|
// FIXME: Password or number scope?
|
|
scope = XGameUiTextEntryInputScope::Number;
|
|
break;
|
|
case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE:
|
|
scope = XGameUiTextEntryInputScope::Number;
|
|
break;
|
|
}
|
|
|
|
g_TextBlock->queue = g_TextTaskQueue;
|
|
g_TextBlock->context = _this;
|
|
g_TextBlock->callback = GDK_InternalTextEntryCallback;
|
|
if (FAILED(hR = XGameUiShowTextEntryAsync(
|
|
g_TextBlock,
|
|
g_TitleText,
|
|
g_DescriptionText,
|
|
g_DefaultText,
|
|
scope,
|
|
(uint32_t)g_MaxTextLength))) {
|
|
SDL_free(g_TextBlock);
|
|
g_TextBlock = NULL;
|
|
SDL_SetError("XGameUiShowTextEntryAsync failure with HRESULT of %08X", hR);
|
|
}
|
|
}
|
|
|
|
void GDK_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
|
|
{
|
|
if (g_TextBlock) {
|
|
XAsyncCancel(g_TextBlock);
|
|
// the completion callback will free the block
|
|
}
|
|
}
|
|
|
|
bool GDK_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
|
|
{
|
|
return (g_TextBlock != NULL);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|