summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/gdk
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/gdk')
-rw-r--r--contrib/SDL-3.2.8/src/video/gdk/SDL_gdktextinput.cpp321
-rw-r--r--contrib/SDL-3.2.8/src/video/gdk/SDL_gdktextinput.h50
2 files changed, 371 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/gdk/SDL_gdktextinput.cpp b/contrib/SDL-3.2.8/src/video/gdk/SDL_gdktextinput.cpp
new file mode 100644
index 0000000..e787871
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/gdk/SDL_gdktextinput.cpp
@@ -0,0 +1,321 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21/*
22 Screen keyboard and text input backend
23 for GDK platforms.
24*/
25#include "SDL_internal.h"
26#include "SDL_gdktextinput.h"
27
28#ifdef SDL_GDK_TEXTINPUT
29
30// GDK headers are weird here
31#ifndef WIN32_LEAN_AND_MEAN
32#define WIN32_LEAN_AND_MEAN
33#endif
34#include <Windows.h>
35#include <XGameUI.h>
36#include <XUser.h>
37
38#ifdef __cplusplus
39extern "C" {
40#endif
41
42#include "../../events/SDL_keyboard_c.h"
43#include "../windows/SDL_windowsvideo.h"
44
45// TODO: Have a separate task queue for text input perhaps?
46static XTaskQueueHandle g_TextTaskQueue = NULL;
47// Global because there can be only one text entry shown at once.
48static XAsyncBlock *g_TextBlock = NULL;
49
50// Creation parameters
51static bool g_DidRegisterHints = false;
52static char *g_TitleText = NULL;
53static char *g_DescriptionText = NULL;
54static char *g_DefaultText = NULL;
55static const Sint32 g_DefaultTextInputScope = (Sint32)XGameUiTextEntryInputScope::Default;
56static Sint32 g_TextInputScope = g_DefaultTextInputScope;
57static const Sint32 g_DefaultMaxTextLength = 1024; // as per doc: maximum allowed amount on consoles
58static Sint32 g_MaxTextLength = g_DefaultMaxTextLength;
59
60static void SDLCALL GDK_InternalHintCallback(
61 void *userdata,
62 const char *name,
63 const char *oldValue,
64 const char *newValue)
65{
66 if (!userdata) {
67 return;
68 }
69
70 // oldValue is ignored because we store it ourselves.
71 // name is ignored because we deduce it from userdata
72
73 if (userdata == &g_TextInputScope || userdata == &g_MaxTextLength) {
74 // int32 hint
75 Sint32 intValue = (!newValue || newValue[0] == '\0') ? 0 : SDL_atoi(newValue);
76 if (userdata == &g_MaxTextLength && intValue <= 0) {
77 intValue = g_DefaultMaxTextLength;
78 } else if (userdata == &g_TextInputScope && intValue < 0) {
79 intValue = g_DefaultTextInputScope;
80 }
81
82 *(Sint32 *)userdata = intValue;
83 } else {
84 // string hint
85 if (!newValue || newValue[0] == '\0') {
86 // treat empty or NULL strings as just NULL for this impl
87 SDL_free(*(char **)userdata);
88 *(char **)userdata = NULL;
89 } else {
90 char *newString = SDL_strdup(newValue);
91 if (newString) {
92 // free previous value and write the new one
93 SDL_free(*(char **)userdata);
94 *(char **)userdata = newString;
95 }
96 }
97 }
98}
99
100static bool GDK_InternalEnsureTaskQueue(void)
101{
102 if (!g_TextTaskQueue) {
103 if (!SDL_GetGDKTaskQueue(&g_TextTaskQueue)) {
104 // SetError will be done for us.
105 return false;
106 }
107 }
108 return true;
109}
110
111static void CALLBACK GDK_InternalTextEntryCallback(XAsyncBlock *asyncBlock)
112{
113 HRESULT hR = S_OK;
114 Uint32 resultSize = 0;
115 Uint32 resultUsed = 0;
116 char *resultBuffer = NULL;
117
118 // The keyboard will be already hidden when we reach this code
119
120 if (FAILED(hR = XGameUiShowTextEntryResultSize(
121 asyncBlock,
122 &resultSize))) {
123 SDL_SetError("XGameUiShowTextEntryResultSize failure with HRESULT of %08X", hR);
124 } else if (resultSize > 0) {
125 // +1 to be super sure that the buffer will be null terminated
126 resultBuffer = (char *)SDL_calloc(1 + (size_t)resultSize, sizeof(*resultBuffer));
127 if (resultBuffer) {
128 // still pass the original size that we got from ResultSize
129 if (FAILED(hR = XGameUiShowTextEntryResult(
130 asyncBlock,
131 resultSize,
132 resultBuffer,
133 &resultUsed))) {
134 SDL_SetError("XGameUiShowTextEntryResult failure with HRESULT of %08X", hR);
135 }
136 // check that we have some text and that we weren't cancelled
137 else if (resultUsed > 0 && resultBuffer[0] != '\0') {
138 // it's null terminated so it's fine
139 SDL_SendKeyboardText(resultBuffer);
140 }
141 // we're done with the buffer
142 SDL_free(resultBuffer);
143 resultBuffer = NULL;
144 }
145 }
146
147 // free the async block after we're done
148 SDL_free(asyncBlock);
149 asyncBlock = NULL;
150 g_TextBlock = NULL; // once we do this we're fully done with the keyboard
151}
152
153void GDK_EnsureHints(void)
154{
155 if (g_DidRegisterHints == false) {
156 SDL_AddHintCallback(
157 SDL_HINT_GDK_TEXTINPUT_TITLE,
158 GDK_InternalHintCallback,
159 &g_TitleText);
160 SDL_AddHintCallback(
161 SDL_HINT_GDK_TEXTINPUT_DESCRIPTION,
162 GDK_InternalHintCallback,
163 &g_DescriptionText);
164 SDL_AddHintCallback(
165 SDL_HINT_GDK_TEXTINPUT_DEFAULT_TEXT,
166 GDK_InternalHintCallback,
167 &g_DefaultText);
168 SDL_AddHintCallback(
169 SDL_HINT_GDK_TEXTINPUT_SCOPE,
170 GDK_InternalHintCallback,
171 &g_TextInputScope);
172 SDL_AddHintCallback(
173 SDL_HINT_GDK_TEXTINPUT_MAX_LENGTH,
174 GDK_InternalHintCallback,
175 &g_MaxTextLength);
176 g_DidRegisterHints = true;
177 }
178}
179
180bool GDK_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
181{
182 /*
183 * Currently a stub, since all input is handled by the virtual keyboard,
184 * but perhaps when implementing XGameUiTextEntryOpen in the future
185 * you will need this.
186 *
187 * Also XGameUiTextEntryOpen docs say that it is
188 * "not implemented on desktop" so... no thanks.
189 *
190 * Right now this function isn't implemented on Desktop
191 * and seems to be present only in the docs? So I didn't bother.
192 */
193 return true;
194}
195
196bool GDK_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
197{
198 // See notice in GDK_StartTextInput
199 return true;
200}
201
202bool GDK_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
203{
204 /*
205 * XGameUiShowTextEntryAsync does not allow you to set
206 * the position of the virtual keyboard window.
207 *
208 * However, XGameUiTextEntryOpen seems to allow that,
209 * but again, see notice in GDK_StartTextInput.
210 *
211 * Right now it's a stub which may be useful later.
212 */
213 return true;
214}
215
216bool GDK_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window)
217{
218 // See notice in GDK_StartTextInput
219 return true;
220}
221
222bool GDK_HasScreenKeyboardSupport(SDL_VideoDevice *_this)
223{
224 // Currently always true for this input method
225 return true;
226}
227
228void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
229{
230 /*
231 * There is XGameUiTextEntryOpen but it's only in online docs,
232 * My October Update 1 GDKX installation does not have this function defined
233 * and as such I decided not to use it at all, since some folks might use even older GDKs.
234 *
235 * That means the only text input option for us is a simple virtual keyboard widget.
236 */
237
238 HRESULT hR = S_OK;
239
240 if (g_TextBlock) {
241 // already showing the keyboard
242 return;
243 }
244
245 if (!GDK_InternalEnsureTaskQueue()) {
246 // unable to obtain the SDL GDK queue
247 return;
248 }
249
250 g_TextBlock = (XAsyncBlock *)SDL_calloc(1, sizeof(*g_TextBlock));
251 if (!g_TextBlock) {
252 return;
253 }
254
255 XGameUiTextEntryInputScope scope;
256 switch (SDL_GetTextInputType(props)) {
257 default:
258 case SDL_TEXTINPUT_TYPE_TEXT:
259 scope = (XGameUiTextEntryInputScope)g_TextInputScope;
260 break;
261 case SDL_TEXTINPUT_TYPE_TEXT_NAME:
262 scope = XGameUiTextEntryInputScope::Default;
263 break;
264 case SDL_TEXTINPUT_TYPE_TEXT_EMAIL:
265 scope = XGameUiTextEntryInputScope::EmailSmtpAddress;
266 break;
267 case SDL_TEXTINPUT_TYPE_TEXT_USERNAME:
268 scope = XGameUiTextEntryInputScope::Default;
269 break;
270 case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN:
271 scope = XGameUiTextEntryInputScope::Password;
272 break;
273 case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE:
274 scope = XGameUiTextEntryInputScope::Default;
275 break;
276 case SDL_TEXTINPUT_TYPE_NUMBER:
277 scope = XGameUiTextEntryInputScope::Number;
278 break;
279 case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN:
280 // FIXME: Password or number scope?
281 scope = XGameUiTextEntryInputScope::Number;
282 break;
283 case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE:
284 scope = XGameUiTextEntryInputScope::Number;
285 break;
286 }
287
288 g_TextBlock->queue = g_TextTaskQueue;
289 g_TextBlock->context = _this;
290 g_TextBlock->callback = GDK_InternalTextEntryCallback;
291 if (FAILED(hR = XGameUiShowTextEntryAsync(
292 g_TextBlock,
293 g_TitleText,
294 g_DescriptionText,
295 g_DefaultText,
296 scope,
297 (uint32_t)g_MaxTextLength))) {
298 SDL_free(g_TextBlock);
299 g_TextBlock = NULL;
300 SDL_SetError("XGameUiShowTextEntryAsync failure with HRESULT of %08X", hR);
301 }
302}
303
304void GDK_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
305{
306 if (g_TextBlock) {
307 XAsyncCancel(g_TextBlock);
308 // the completion callback will free the block
309 }
310}
311
312bool GDK_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
313{
314 return (g_TextBlock != NULL);
315}
316
317#ifdef __cplusplus
318}
319#endif
320
321#endif
diff --git a/contrib/SDL-3.2.8/src/video/gdk/SDL_gdktextinput.h b/contrib/SDL-3.2.8/src/video/gdk/SDL_gdktextinput.h
new file mode 100644
index 0000000..c3882ba
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/gdk/SDL_gdktextinput.h
@@ -0,0 +1,50 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifndef SDL_gdktextinput_h_
24#define SDL_gdktextinput_h_
25
26#ifdef SDL_GDK_TEXTINPUT
27#ifdef __cplusplus
28extern "C" {
29#endif
30
31#include "../SDL_sysvideo.h"
32
33void GDK_EnsureHints(void);
34
35bool GDK_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
36bool GDK_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
37bool GDK_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
38bool GDK_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window);
39
40bool GDK_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
41void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
42void GDK_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
43bool GDK_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
44
45#ifdef __cplusplus
46}
47#endif
48#endif
49
50#endif