Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Dialog/PSPOskDialog.cpp
5658 views
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "ppsspp_config.h"
19
20
#include <cctype>
21
#include <cmath>
22
#include <algorithm>
23
24
#include "Common/Data/Text/I18n.h"
25
#include "Common/Math/math_util.h"
26
#include "Common/Data/Encoding/Utf8.h"
27
#include "Common/Serialize/SerializeFuncs.h"
28
#include "Common/System/Request.h"
29
#include "Common/Serialize/Serializer.h"
30
31
#include "Core/Dialog/PSPOskDialog.h"
32
#include "Core/Util/PPGeDraw.h"
33
#include "Core/HLE/sceCtrl.h"
34
#include "Core/HLE/ErrorCodes.h"
35
#include "Core/HLE/sceUtility.h"
36
#include "Core/HW/Display.h"
37
#include "Core/Config.h"
38
#include "Core/Reporting.h"
39
40
// These are rough, it seems to take a long time to init, and probably depends on threads.
41
// TODO: This takes like 700ms on a PSP but that's annoyingly long.
42
const static int OSK_INIT_DELAY_US = 300000;
43
const static int OSK_SHUTDOWN_DELAY_US = 40000;
44
45
const uint8_t numKeyCols[OSK_KEYBOARD_COUNT] = {12, 12, 13, 13, 12, 12, 12, 12, 12};
46
const uint8_t numKeyRows[OSK_KEYBOARD_COUNT] = {4, 4, 6, 6, 5, 4, 4, 4, 4};
47
48
// Korean (Hangul) vowel Combination key
49
static const uint8_t kor_vowelCom[21] = { 0,8,9,1,8,10,20,8,11,4,13,14,5,13,15,20,13,16,20,18,19 };
50
51
// Korean (Hangul) last consonant Combination key
52
static const uint8_t kor_lconsCom[33] = { 18,0,2,21,3,4,26,3,5,0,7,8,15,7,9,16,7,10,18,7,11,24,7,12,25,7,13,26,7,14,18,16,17 };
53
54
// Korean (Hangul) last consonant Separation key
55
static const uint8_t kor_lconsSpr[33] = { 2,1,9,4,4,12,5,4,18,8,8,0,9,8,6,10,8,7,11,8,9,12,8,16,13,8,17,14,8,18,17,17,9 };
56
57
static const std::string_view OskKeyboardNames[] =
58
{
59
"en_US",
60
"ja_JP",
61
"ko_KR",
62
"ru_RU",
63
"English Full-width",
64
};
65
66
// This isn't a complete representation of these flags, it just helps ensure we show the right keyboards.
67
static const int allowedInputFlagsMap[OSK_KEYBOARD_COUNT] = {
68
PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT,
69
PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL,
70
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA,
71
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA,
72
PSP_UTILITY_OSK_INPUTTYPE_KOREAN,
73
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE,
74
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE,
75
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT,
76
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL,
77
};
78
static const int defaultInputFlagsMap[OSK_KEYBOARD_COUNT] = {
79
PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT,
80
PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL,
81
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA,
82
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA,
83
PSP_UTILITY_OSK_INPUTTYPE_KOREAN,
84
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE,
85
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE,
86
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT,
87
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL,
88
};
89
90
PSPOskDialog::PSPOskDialog(UtilityDialogType type) : PSPDialog(type) {
91
// This can break all kinds of stuff, changing the decimal point in sprintf for example.
92
// Not sure what the intended effect is so commented out for now.
93
// setlocale(LC_ALL, "");
94
}
95
96
PSPOskDialog::~PSPOskDialog() {
97
}
98
99
void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const PSPPointer<u16_le>& em_address)
100
{
101
if (!em_address.IsValid())
102
{
103
_string.clear();
104
return;
105
}
106
107
const size_t maxLength = 2047;
108
char stringBuffer[maxLength + 1];
109
char *string = stringBuffer;
110
111
const u16_le *input = &em_address[0];
112
int c;
113
while ((c = *input++) != 0 && string < stringBuffer + maxLength)
114
{
115
if (c < 0x80)
116
*string++ = c;
117
else if (c < 0x800) {
118
*string++ = 0xC0 | (c >> 6);
119
*string++ = 0x80 | (c & 0x3F);
120
} else {
121
*string++ = 0xE0 | (c >> 12);
122
*string++ = 0x80 | ((c >> 6) & 0x3F);
123
*string++ = 0x80 | (c & 0x3F);
124
}
125
}
126
*string++ = '\0';
127
_string = stringBuffer;
128
}
129
130
void GetWideStringFromPSPPointer(std::u16string& _string, const PSPPointer<u16_le>& em_address)
131
{
132
if (!em_address.IsValid())
133
{
134
_string.clear();
135
return;
136
}
137
138
const size_t maxLength = 2047;
139
char16_t stringBuffer[maxLength + 1];
140
char16_t *string = stringBuffer;
141
142
const u16_le *input = &em_address[0];
143
int c;
144
while ((c = *input++) != 0 && string < stringBuffer + maxLength)
145
*string++ = c;
146
*string++ = '\0';
147
_string = stringBuffer;
148
}
149
150
void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const char16_t *input)
151
{
152
char stringBuffer[2048];
153
char *string = stringBuffer;
154
155
int c;
156
while ((c = *input++) != 0)
157
{
158
if (c < 0x80)
159
*string++ = c;
160
else if (c < 0x800) {
161
*string++ = 0xC0 | (c >> 6);
162
*string++ = 0x80 | (c & 0x3F);
163
} else {
164
*string++ = 0xE0 | (c >> 12);
165
*string++ = 0x80 | ((c >> 6) & 0x3F);
166
*string++ = 0x80 | (c & 0x3F);
167
}
168
}
169
*string++ = '\0';
170
_string = stringBuffer;
171
}
172
173
static void FindValidKeyboard(s32 inputType, int direction, OskKeyboardLanguage &lang, OskKeyboardDisplay &disp) {
174
OskKeyboardLanguage origLang = lang;
175
OskKeyboardDisplay origDisp = disp;
176
177
if (inputType == 0) {
178
return;
179
}
180
// We use direction = 0 for default, but we actually move "forward".
181
const int *matchMap = allowedInputFlagsMap;
182
if (direction == 0) {
183
direction = 1;
184
matchMap = defaultInputFlagsMap;
185
}
186
187
// TODO: Limit by allowed keyboards properly... this is just an approximation.
188
int tries = OSK_LANGUAGE_COUNT * 2;
189
while (!(inputType & matchMap[disp]) && tries > 0) {
190
if ((--tries % 2) == 0) {
191
lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + lang + direction) % OSK_LANGUAGE_COUNT);
192
disp = OskKeyboardCases[lang][LOWERCASE];
193
} else {
194
disp = OskKeyboardCases[lang][UPPERCASE];
195
}
196
}
197
198
if (tries == 0) {
199
// In case of error, let's just fall back to allowing all.
200
lang = origLang;
201
disp = origDisp;
202
}
203
}
204
205
static bool IsKeyboardShiftValid(s32 inputType, OskKeyboardLanguage lang, OskKeyboardDisplay disp) {
206
// Swap disp and check if it's valid.
207
if (disp == OskKeyboardCases[lang][UPPERCASE])
208
disp = OskKeyboardCases[lang][LOWERCASE];
209
else
210
disp = OskKeyboardCases[lang][UPPERCASE];
211
212
return inputType == 0 || (inputType & allowedInputFlagsMap[disp]) != 0;
213
}
214
215
int PSPOskDialog::Init(u32 oskPtr) {
216
// Ignore if already running
217
if (GetStatus() != SCE_UTILITY_STATUS_NONE) {
218
ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid status");
219
return SCE_ERROR_UTILITY_INVALID_STATUS;
220
}
221
// Seems like this should crash?
222
if (!Memory::IsValidAddress(oskPtr)) {
223
ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid params (%08x)", oskPtr);
224
return -1;
225
}
226
227
oskParams = oskPtr;
228
if (oskParams->base.size != sizeof(SceUtilityOskParams))
229
{
230
ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid size %d", oskParams->base.size);
231
return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE;
232
}
233
// Also seems to crash.
234
if (!oskParams->fields.IsValid())
235
{
236
ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid field data (%08x)", oskParams->fields.ptr);
237
return -1;
238
}
239
240
if (oskParams->unk_60 != 0)
241
WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unknown param is non-zero (%08x)", oskParams->unk_60);
242
if (oskParams->fieldCount != 1)
243
WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unsupported field count %d", oskParams->fieldCount);
244
245
ChangeStatusInit(OSK_INIT_DELAY_US);
246
selectedChar = 0;
247
currentKeyboardLanguage = OSK_LANGUAGE_ENGLISH;
248
currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE;
249
FindValidKeyboard(oskParams->fields[0].inputtype, 0, currentKeyboardLanguage, currentKeyboard);
250
251
ConvertUCS2ToUTF8(oskDesc, oskParams->fields[0].desc);
252
ConvertUCS2ToUTF8(oskIntext, oskParams->fields[0].intext);
253
ConvertUCS2ToUTF8(oskOuttext, oskParams->fields[0].outtext);
254
255
i_level = 0;
256
257
inputChars.clear();
258
259
if (oskParams->fields[0].intext.IsValid()) {
260
auto src = oskParams->fields[0].intext;
261
int c;
262
while ((c = *src++) != 0)
263
inputChars += c;
264
}
265
266
// Eat any keys pressed before the dialog inited.
267
UpdateButtons();
268
InitCommon();
269
270
std::lock_guard<std::mutex> guard(nativeMutex_);
271
nativeStatus_ = PSPOskNativeStatus::IDLE;
272
273
StartFade(true);
274
return 0;
275
}
276
277
std::u16string PSPOskDialog::CombinationKorean(bool isInput)
278
{
279
std::u16string string;
280
281
isCombinated = true;
282
283
int selectedRow = selectedChar / numKeyCols[currentKeyboard];
284
int selectedCol = selectedChar % numKeyCols[currentKeyboard];
285
286
if (inputChars.size() == 0) {
287
wchar_t sw = OskKeyAt(currentKeyboard, selectedRow, selectedCol);
288
289
if (inputChars.size() < FieldMaxLength()) {
290
string += sw;
291
292
i_value[0] = GetIndex(KorCons(), sw);
293
294
if(i_value[0] != -1 && isInput == true)
295
i_level = 1;
296
} else {
297
isCombinated = false;
298
}
299
} else {
300
for(u32 i = 0; i < inputChars.size(); i++) {
301
if(i + 1 == inputChars.size()) {
302
wchar_t sw = OskKeyAt(currentKeyboard, selectedRow, selectedCol);
303
304
if(i_level == 0) {
305
string += inputChars[i];
306
if (inputChars.size() < FieldMaxLength()) {
307
string += sw;
308
309
i_value[0] = GetIndex(KorCons(), sw);
310
311
if(i_value[0] != -1 && isInput == true)
312
i_level = 1;
313
} else {
314
isCombinated = false;
315
}
316
} else if(i_level == 1) {
317
i_value[1] = GetIndex(KorVowel(), sw);
318
319
if(i_value[1] == -1) {
320
string += inputChars[i];
321
if (inputChars.size() < FieldMaxLength()) {
322
string += sw;
323
324
if(isInput == true) {
325
i_value[0] = GetIndex(KorCons(), sw);
326
327
if(i_value[0] != -1)
328
i_level = 1;
329
else
330
i_level = 0;
331
}
332
} else {
333
isCombinated = false;
334
}
335
} else {
336
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
337
string += code;
338
339
if(isInput == true) {
340
i_level = 2;
341
}
342
}
343
} else if(i_level == 2) {
344
int tmp = GetIndex(KorVowel(), sw);
345
if(tmp != -1) {
346
int tmp2 = -1;
347
for(size_t j = 0; j < sizeof(kor_vowelCom) / 4; j+=3) {
348
if(kor_vowelCom[j] == tmp && kor_vowelCom[j + 1] == i_value[1]) {
349
tmp2 = kor_vowelCom[j + 2];
350
break;
351
}
352
}
353
if(tmp2 != -1) {
354
if(isInput == true) {
355
i_value[1] = tmp2;
356
}
357
358
u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C;
359
360
string += code;
361
} else {
362
string += inputChars[i];
363
if (inputChars.size() < FieldMaxLength()) {
364
string += sw;
365
366
if(isInput == true) {
367
i_level = 0;
368
}
369
} else {
370
isCombinated = false;
371
}
372
}
373
} else {
374
int tmp2 = GetIndex(KorLCons(), sw);
375
376
if (tmp2 == -1) {
377
string += inputChars[i];
378
if (inputChars.size() < FieldMaxLength()) {
379
string += sw;
380
381
if (isInput == true) {
382
i_value[0] = GetIndex(KorCons(), sw);
383
384
if(i_value[0] != -1)
385
i_level = 1;
386
else
387
i_level = 0;
388
}
389
} else {
390
isCombinated = false;
391
}
392
} else {
393
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + tmp2 + 1;
394
395
string += code;
396
397
if (isInput == true) {
398
i_level = 3;
399
i_value[2] = tmp2;
400
}
401
}
402
}
403
} else if(i_level == 3) {
404
int tmp = GetIndex(KorLCons(), sw);
405
if(tmp != -1) {
406
int tmp2 = -1;
407
for(size_t j = 0; j < sizeof(kor_lconsCom) / 4; j+=3) {
408
if(kor_lconsCom[j] == tmp && kor_lconsCom[j + 1] == i_value[2]) {
409
tmp2 = kor_lconsCom[j + 2];
410
break;
411
}
412
}
413
if(tmp2 != -1) {
414
if(isInput == true) {
415
i_value[2] = tmp2;
416
}
417
418
u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C + i_value[2] + 1;
419
420
string += code;
421
} else {
422
string += inputChars[i];
423
if (inputChars.size() < FieldMaxLength()) {
424
string += sw;
425
426
if(isInput == true) {
427
i_value[0] = GetIndex(KorCons(), sw);
428
429
if(i_value[0] != -1)
430
i_level = 1;
431
else
432
i_level = 0;
433
}
434
} else {
435
isCombinated = false;
436
}
437
}
438
} else {
439
int tmp3 = GetIndex(KorVowel(), sw);
440
if (tmp3 == -1) {
441
string += inputChars[i];
442
if (inputChars.size() < FieldMaxLength()) {
443
string += sw;
444
445
if(isInput == true) {
446
i_value[0] = GetIndex(KorCons(), sw);
447
448
if(i_value[0] != -1)
449
i_level = 1;
450
else
451
i_level = 0;
452
}
453
} else {
454
isCombinated = false;
455
}
456
} else {
457
if (inputChars.size() < FieldMaxLength()) {
458
int tmp2 = -1;
459
for(size_t j = 0; j < sizeof(kor_lconsSpr) / 4; j+=3) {
460
if(kor_lconsSpr[j] == i_value[2]) {
461
tmp2 = (int)j;
462
break;
463
}
464
}
465
if(tmp2 != -1) {
466
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + kor_lconsSpr[tmp2 + 1];
467
string += code;
468
469
code = 0xAC00 + kor_lconsSpr[tmp2 + 2] * 0x24C + tmp3 * 0x1C;
470
string += code;
471
472
if(isInput == true) {
473
i_value[0] = kor_lconsSpr[tmp2 + 2];
474
i_value[1] = tmp3;
475
i_level = 2;
476
}
477
} else {
478
int tmp4 = GetIndex(KorCons(), KorLCons()[i_value[2]]);
479
480
if (tmp4 != -1) {
481
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
482
483
string += code;
484
485
code = 0xAC00 + tmp4 * 0x24C + tmp3 * 0x1C;
486
487
string += code;
488
489
if(isInput == true) {
490
i_value[0] = tmp4;
491
i_value[1] = tmp3;
492
i_level = 2;
493
}
494
} else {
495
string += inputChars[i];
496
string += sw;
497
498
if(isInput == true) {
499
i_level = 0;
500
}
501
}
502
}
503
} else {
504
string += inputChars[i];
505
isCombinated = false;
506
}
507
}
508
}
509
}
510
} else {
511
string += inputChars[i];
512
}
513
}
514
}
515
516
return string;
517
}
518
519
std::u16string PSPOskDialog::CombinationString(bool isInput)
520
{
521
std::u16string string;
522
523
isCombinated = false;
524
525
int selectedRow = selectedChar / numKeyCols[currentKeyboard];
526
int selectedCol = selectedChar % numKeyCols[currentKeyboard];
527
528
if(currentKeyboard == OSK_KEYBOARD_KOREAN)
529
{
530
string = CombinationKorean(isInput);
531
}
532
else
533
{
534
if(isInput == true)
535
{
536
i_level = 0;
537
}
538
539
if(OskKeyAt(currentKeyboard, selectedRow, selectedCol) == L'゛')
540
{
541
for(u32 i = 0; i < inputChars.size(); i++)
542
{
543
if(i + 1 == inputChars.size())
544
{
545
for(u32 j = 0; j < wcslen(JapDiacritics(0)); j+=2)
546
{
547
if(inputChars[i] == JapDiacritics(0)[j])
548
{
549
string += JapDiacritics(0)[j + 1];
550
isCombinated = true;
551
break;
552
}
553
}
554
555
if(isCombinated == false)
556
{
557
string += inputChars[i];
558
}
559
}
560
else
561
{
562
string += inputChars[i];
563
}
564
}
565
}
566
else if(OskKeyAt(currentKeyboard, selectedRow, selectedCol) == L'゜')
567
{
568
for(u32 i = 0; i < inputChars.size(); i++)
569
{
570
if(i + 1 == inputChars.size())
571
{
572
for(u32 j = 0; j < wcslen(JapDiacritics(1)); j+=2)
573
{
574
if(inputChars[i] == JapDiacritics(1)[j])
575
{
576
string += JapDiacritics(1)[j + 1];
577
isCombinated = true;
578
break;
579
}
580
}
581
582
if(isCombinated == false)
583
{
584
string += inputChars[i];
585
}
586
}
587
else
588
{
589
string += inputChars[i];
590
}
591
}
592
}
593
else
594
{
595
for(u32 i = 0; i < inputChars.size(); i++)
596
{
597
string += inputChars[i];
598
}
599
600
if (string.size() < FieldMaxLength())
601
{
602
string += OskKeyAt(currentKeyboard, selectedRow, selectedCol);
603
}
604
isCombinated = true;
605
}
606
}
607
608
return string;
609
}
610
611
void PSPOskDialog::RemoveKorean()
612
{
613
if(i_level == 1)
614
{
615
i_level = 0;
616
}
617
else if(i_level == 2)
618
{
619
int tmp = -1;
620
for(size_t i = 2; i < sizeof(kor_vowelCom) / 4; i+=3)
621
{
622
if(kor_vowelCom[i] == i_value[1])
623
{
624
tmp = kor_vowelCom[i - 1];
625
break;
626
}
627
}
628
629
if(tmp != -1)
630
{
631
i_value[1] = tmp;
632
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
633
inputChars += code;
634
}
635
else
636
{
637
i_level = 1;
638
inputChars += KorCons()[i_value[0]];
639
}
640
}
641
else if(i_level == 3)
642
{
643
int tmp = -1;
644
for(size_t i = 2; i < sizeof(kor_lconsCom) / 4; i+=3)
645
{
646
if(kor_lconsCom[i] == i_value[2])
647
{
648
tmp = kor_lconsCom[i - 1];
649
break;
650
}
651
}
652
653
if(tmp != -1)
654
{
655
i_value[2] = tmp;
656
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + i_value[2] + 1;
657
inputChars += code;
658
}
659
else
660
{
661
i_level = 2;
662
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
663
inputChars += code;
664
}
665
}
666
}
667
668
int PSPOskDialog::GetIndex(const wchar_t* src, wchar_t ch)
669
{
670
for(int i = 0, end = (int)wcslen(src); i < end; i++)
671
{
672
if(src[i] == ch)
673
{
674
return i;
675
}
676
}
677
678
return -1;
679
}
680
681
u32 PSPOskDialog::FieldMaxLength()
682
{
683
if ((oskParams->fields[0].outtextlimit > oskParams->fields[0].outtextlength - 1) || oskParams->fields[0].outtextlimit == 0)
684
return oskParams->fields[0].outtextlength - 1;
685
return oskParams->fields[0].outtextlimit;
686
}
687
688
void PSPOskDialog::RenderKeyboard()
689
{
690
// Sanity check that a valid keyboard is selected.
691
if ((int)currentKeyboard < 0 || (int)currentKeyboard >= OSK_KEYBOARD_COUNT) {
692
return;
693
}
694
695
int selectedRow = selectedChar / numKeyCols[currentKeyboard];
696
int selectedCol = selectedChar % numKeyCols[currentKeyboard];
697
698
char16_t temp[2];
699
temp[1] = '\0';
700
701
std::string buffer;
702
703
static const u32 FIELDDRAWMAX = 16;
704
u32 limit = FieldMaxLength();
705
u32 drawLimit = std::min(FIELDDRAWMAX, limit); // Field drew length limit.
706
707
const float keyboardLeftSide = (480.0f - (24.0f * numKeyCols[currentKeyboard])) / 2.0f;
708
const float characterWidth = 12.0f;
709
float previewLeftSide = (480.0f - (12.0f * drawLimit)) / 2.0f;
710
float title = (480.0f - (0.5f * drawLimit)) / 2.0f;
711
712
PPGeStyle descStyle = FadedStyle(PPGeAlign::BOX_CENTER, 0.5f);
713
PPGeDrawText(oskDesc, title, 20, descStyle);
714
715
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.5f);
716
717
PPGeStyle keyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f);
718
PPGeStyle selectedKeyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f);
719
selectedKeyStyle.color = CalcFadedColor(0xFF3060FF);
720
721
std::u16string result;
722
723
result = CombinationString(false);
724
725
u32 drawIndex = (u32)(result.size() > drawLimit ? result.size() - drawLimit : 0);
726
drawIndex = result.size() == limit + 1 ? drawIndex - 1 : drawIndex; // When the length reached limit, the last character don't fade in and out.
727
for (u32 i = 0; i < drawLimit; ++i, ++drawIndex)
728
{
729
if (drawIndex + 1 < result.size())
730
{
731
temp[0] = result[drawIndex];
732
ConvertUCS2ToUTF8(buffer, temp);
733
PPGeDrawText(buffer, previewLeftSide + (i * characterWidth), 40.0f, textStyle);
734
}
735
else
736
{
737
if (drawIndex + 1 == result.size())
738
{
739
temp[0] = result[drawIndex];
740
741
if(isCombinated == true)
742
{
743
float animStep = (float)(__DisplayGetNumVblanks() % 40) / 20.0f;
744
// Fade in and out the next character so they know it's not part of the string yet.
745
u32 alpha = (0.5f - (cosf(animStep * M_PI) / 2.0f)) * 128 + 127;
746
PPGeStyle animStyle = textStyle;
747
animStyle.color = CalcFadedColor((alpha << 24) | 0x00FFFFFF);
748
749
ConvertUCS2ToUTF8(buffer, temp);
750
751
PPGeDrawText(buffer, previewLeftSide + (i * characterWidth), 40.0f, animStyle);
752
753
// Also draw the underline for the same reason.
754
PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle);
755
}
756
else
757
{
758
ConvertUCS2ToUTF8(buffer, temp);
759
PPGeDrawText(buffer, previewLeftSide + (i * characterWidth), 40.0f, textStyle);
760
}
761
}
762
else
763
{
764
PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle);
765
}
766
}
767
}
768
769
for (int row = 0; row < numKeyRows[currentKeyboard]; ++row)
770
{
771
for (int col = 0; col < numKeyCols[currentKeyboard]; ++col)
772
{
773
temp[0] = OskKeyAt(currentKeyboard, row, col);
774
775
ConvertUCS2ToUTF8(buffer, temp);
776
777
if (selectedRow == row && col == selectedCol) {
778
PPGeDrawText(buffer, keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), selectedKeyStyle);
779
PPGeDrawText("_", keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle);
780
} else {
781
PPGeDrawText(buffer, keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle);
782
}
783
}
784
}
785
}
786
787
// TODO: Why does this have a 2 button press lag/delay when
788
// re-opening the dialog box? I don't get it.
789
int PSPOskDialog::NativeKeyboard() {
790
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {
791
return SCE_ERROR_UTILITY_INVALID_STATUS;
792
}
793
794
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
795
bool beginInputBox = false;
796
if (nativeStatus_ == PSPOskNativeStatus::IDLE) {
797
std::lock_guard<std::mutex> guard(nativeMutex_);
798
if (nativeStatus_ == PSPOskNativeStatus::IDLE) {
799
nativeStatus_ = PSPOskNativeStatus::WAITING;
800
beginInputBox = true;
801
}
802
}
803
804
if (beginInputBox) {
805
std::u16string titleText;
806
GetWideStringFromPSPPointer(titleText, oskParams->fields[0].desc);
807
808
std::u16string defaultText;
809
GetWideStringFromPSPPointer(defaultText, oskParams->fields[0].intext);
810
811
// There's already ConvertUCS2ToUTF8 in this file. Should we use that instead of the global ones?
812
System_InputBoxGetString(NON_EPHEMERAL_TOKEN, ::ConvertUCS2ToUTF8(titleText), ::ConvertUCS2ToUTF8(defaultText), false,
813
[&](const std::string &value, int) {
814
// Success callback
815
std::lock_guard<std::mutex> guard(nativeMutex_);
816
if (nativeStatus_ != PSPOskNativeStatus::WAITING) {
817
return;
818
}
819
nativeValue_ = value;
820
nativeStatus_ = PSPOskNativeStatus::SUCCESS;
821
},
822
[&]() {
823
// Failure callback
824
std::lock_guard<std::mutex> guard(nativeMutex_);
825
if (nativeStatus_ != PSPOskNativeStatus::WAITING) {
826
return;
827
}
828
nativeValue_ = "";
829
nativeStatus_ = PSPOskNativeStatus::FAILURE;
830
}
831
);
832
} else if (nativeStatus_ == PSPOskNativeStatus::SUCCESS) {
833
inputChars = ConvertUTF8ToUCS2(nativeValue_);
834
nativeValue_.clear();
835
836
u32 maxLength = FieldMaxLength();
837
if (inputChars.length() > maxLength) {
838
ERROR_LOG(Log::sceUtility, "NativeKeyboard: input text too long(%d characters/glyphs max), truncating to game-requested length.", maxLength);
839
inputChars.erase(maxLength, std::string::npos);
840
}
841
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
842
nativeStatus_ = PSPOskNativeStatus::DONE;
843
} else if (nativeStatus_ == PSPOskNativeStatus::FAILURE) {
844
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
845
nativeStatus_ = PSPOskNativeStatus::DONE;
846
}
847
#endif
848
849
u16_le *outText = oskParams->fields[0].outtext;
850
851
size_t end = oskParams->fields[0].outtextlength;
852
if (end > inputChars.size())
853
end = inputChars.size() + 1;
854
// Only write the bytes of the output and the null terminator, don't write the rest.
855
for (size_t i = 0; i < end; ++i) {
856
u16 value = 0;
857
if (i < FieldMaxLength() && i < inputChars.size())
858
value = inputChars[i];
859
outText[i] = value;
860
}
861
862
oskParams->base.result = 0;
863
oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED;
864
865
return 0;
866
}
867
868
int PSPOskDialog::Update(int animSpeed) {
869
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {
870
return SCE_ERROR_UTILITY_INVALID_STATUS;
871
}
872
873
int cancelButton = GetCancelButton();
874
int confirmButton = GetConfirmButton();
875
876
static int cancelBtnFramesHeld = 0;
877
static int confirmBtnFramesHeld = 0;
878
static int leftBtnFramesHeld = 0;
879
static int upBtnFramesHeld = 0;
880
static int downBtnFramesHeld = 0;
881
static int rightBtnFramesHeld = 0;
882
const int framesHeldThreshold = 10;
883
const int framesHeldRepeatRate = 5;
884
885
UpdateButtons();
886
UpdateCommon();
887
int selectedRow = selectedChar / numKeyCols[currentKeyboard];
888
int selectedExtra = selectedChar % numKeyCols[currentKeyboard];
889
890
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
891
// Windows: Fall back to the OSK/continue normally if we're in fullscreen.
892
// The dialog box doesn't work right if in fullscreen.
893
if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) {
894
if (g_Config.bBypassOSKWithKeyboard && !g_Config.bFullScreen)
895
return NativeKeyboard();
896
}
897
#endif
898
899
UpdateFade(animSpeed);
900
901
StartDraw();
902
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363));
903
RenderKeyboard();
904
905
auto di = GetI18NCategory(I18NCat::DIALOG);
906
907
PPGeStyle actionStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f);
908
PPGeStyle guideStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.6f);
909
910
PPGeDrawImage(ImageID("I_SQUARE"), 365, 222, 16, 16, guideStyle);
911
PPGeDrawText(di->T("Space"), 390, 222, actionStyle);
912
913
if (GetConfirmButton() != CTRL_CIRCLE) {
914
PPGeDrawImage(ImageID("I_CROSS"), 45, 222, 16, 16, guideStyle);
915
PPGeDrawImage(ImageID("I_CIRCLE"), 45, 247, 16, 16, guideStyle);
916
} else {
917
PPGeDrawImage(ImageID("I_CIRCLE"), 45, 222, 16, 16, guideStyle);
918
PPGeDrawImage(ImageID("I_CROSS"), 45, 247, 16, 16, guideStyle);
919
}
920
921
PPGeDrawText(di->T("Select"), 75, 222, actionStyle);
922
PPGeDrawText(di->T("Delete"), 75, 247, actionStyle);
923
924
PPGeDrawText("Start", 135, 220, guideStyle);
925
PPGeDrawText(di->T("Finish"), 185, 222, actionStyle);
926
927
auto lookupLangName = [&](int direction) {
928
// First, find the valid one...
929
OskKeyboardLanguage lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + currentKeyboardLanguage + direction) % OSK_LANGUAGE_COUNT);
930
OskKeyboardDisplay disp = OskKeyboardCases[lang][LOWERCASE];
931
FindValidKeyboard(oskParams->fields[0].inputtype, direction, lang, disp);
932
933
if (lang == currentKeyboardLanguage) {
934
return (const char *)nullptr;
935
}
936
937
// Now, let's grab the friendly name from the langvaluesmapping.
938
const std::string countryCode(OskKeyboardNames[lang]);
939
940
const auto &languageMapping = GetLangValuesMapping();
941
const auto iter = languageMapping.find(countryCode);
942
const char *language = iter == languageMapping.end() ? "" : iter->second.first.c_str();
943
944
// It seems like this is a "fake" country code for extra keyboard purposes.
945
if (countryCode == "English Full-width")
946
language = "English Full-width";
947
948
return language;
949
};
950
951
if (OskKeyboardNames[currentKeyboardLanguage] != "ko_KR" && IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) {
952
PPGeDrawText("Select", 135, 245, guideStyle);
953
PPGeDrawText(di->T("Shift"), 185, 247, actionStyle);
954
}
955
956
const char *prevLang = lookupLangName(-1);
957
if (prevLang) {
958
PPGeDrawText("L", 235, 220, guideStyle);
959
PPGeDrawText(prevLang, 255, 222, actionStyle);
960
}
961
962
const char *nextLang = lookupLangName(1);
963
if (nextLang) {
964
PPGeDrawText("R", 235, 245, guideStyle);
965
PPGeDrawText(nextLang, 255, 247, actionStyle);
966
}
967
968
if (IsButtonPressed(CTRL_UP) || IsButtonHeld(CTRL_UP, upBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
969
selectedChar -= numKeyCols[currentKeyboard];
970
} else if (IsButtonPressed(CTRL_DOWN) || IsButtonHeld(CTRL_DOWN, downBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
971
selectedChar += numKeyCols[currentKeyboard];
972
} else if (IsButtonPressed(CTRL_LEFT) || IsButtonHeld(CTRL_LEFT, leftBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
973
selectedChar--;
974
if (((selectedChar + numKeyCols[currentKeyboard]) % numKeyCols[currentKeyboard]) == numKeyCols[currentKeyboard] - 1)
975
selectedChar += numKeyCols[currentKeyboard];
976
} else if (IsButtonPressed(CTRL_RIGHT) || IsButtonHeld(CTRL_RIGHT, rightBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
977
selectedChar++;
978
if ((selectedChar % numKeyCols[currentKeyboard]) == 0)
979
selectedChar -= numKeyCols[currentKeyboard];
980
}
981
982
selectedChar = (selectedChar + (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard])) % (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard]);
983
984
if (IsButtonPressed(confirmButton) || IsButtonHeld(confirmButton, confirmBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
985
inputChars = CombinationString(true);
986
} else if (IsButtonPressed(CTRL_SELECT)) {
987
// Select now swaps case.
988
if (IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) {
989
if (currentKeyboard == OskKeyboardCases[currentKeyboardLanguage][UPPERCASE])
990
currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE];
991
else
992
currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][UPPERCASE];
993
}
994
995
if (selectedRow >= numKeyRows[currentKeyboard]) {
996
selectedRow = numKeyRows[currentKeyboard] - 1;
997
}
998
999
if (selectedExtra >= numKeyCols[currentKeyboard]) {
1000
selectedExtra = numKeyCols[currentKeyboard] - 1;
1001
}
1002
1003
selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;
1004
} else if (IsButtonPressed(CTRL_RTRIGGER)) {
1005
// RTRIGGER now cycles languages forward.
1006
currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage + 1) % OSK_LANGUAGE_COUNT);
1007
currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE];
1008
FindValidKeyboard(oskParams->fields[0].inputtype, 1, currentKeyboardLanguage, currentKeyboard);
1009
1010
if (selectedRow >= numKeyRows[currentKeyboard]) {
1011
selectedRow = numKeyRows[currentKeyboard] - 1;
1012
}
1013
1014
if (selectedExtra >= numKeyCols[currentKeyboard]) {
1015
selectedExtra = numKeyCols[currentKeyboard] - 1;
1016
}
1017
1018
selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;
1019
} else if (IsButtonPressed(CTRL_LTRIGGER)) {
1020
// LTRIGGER now cycles languages backward.
1021
if (currentKeyboardLanguage - 1 >= 0)
1022
currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT);
1023
else
1024
currentKeyboardLanguage = (OskKeyboardLanguage)(OSK_LANGUAGE_COUNT - 1);
1025
currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE];
1026
FindValidKeyboard(oskParams->fields[0].inputtype, -1, currentKeyboardLanguage, currentKeyboard);
1027
1028
if (selectedRow >= numKeyRows[currentKeyboard]) {
1029
selectedRow = numKeyRows[currentKeyboard] - 1;
1030
}
1031
1032
if (selectedExtra >= numKeyCols[currentKeyboard]) {
1033
selectedExtra = numKeyCols[currentKeyboard] - 1;
1034
}
1035
1036
selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;
1037
} else if (IsButtonPressed(cancelButton) || IsButtonHeld(cancelButton, cancelBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
1038
if (inputChars.size() > 0) {
1039
inputChars.resize(inputChars.size() - 1);
1040
if (i_level != 0) {
1041
RemoveKorean();
1042
}
1043
}
1044
} else if (IsButtonPressed(CTRL_START)) {
1045
StartFade(false);
1046
} else if (IsButtonPressed(CTRL_SQUARE) && inputChars.size() < FieldMaxLength()) {
1047
// Use a regular space if the current keyboard isn't Japanese nor full-width English
1048
if (currentKeyboardLanguage != OSK_LANGUAGE_JAPANESE && currentKeyboardLanguage != OSK_LANGUAGE_ENGLISH_FW)
1049
inputChars += u" ";
1050
else
1051
inputChars += u" ";
1052
}
1053
1054
EndDraw();
1055
1056
u16_le *outText = oskParams->fields[0].outtext;
1057
size_t end = oskParams->fields[0].outtextlength;
1058
// Only write the bytes of the output and the null terminator, don't write the rest.
1059
if (end > inputChars.size())
1060
end = inputChars.size() + 1;
1061
for (size_t i = 0; i < end; ++i)
1062
{
1063
u16 value = 0;
1064
if (i < FieldMaxLength() && i < inputChars.size())
1065
value = inputChars[i];
1066
outText[i] = value;
1067
}
1068
1069
oskParams->base.result = 0;
1070
oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED;
1071
return 0;
1072
}
1073
1074
int PSPOskDialog::Shutdown(bool force)
1075
{
1076
if (GetStatus() != SCE_UTILITY_STATUS_FINISHED && !force)
1077
return SCE_ERROR_UTILITY_INVALID_STATUS;
1078
1079
PSPDialog::Shutdown(force);
1080
if (!force) {
1081
ChangeStatusShutdown(OSK_SHUTDOWN_DELAY_US);
1082
}
1083
nativeStatus_ = PSPOskNativeStatus::IDLE;
1084
1085
return 0;
1086
}
1087
1088
void PSPOskDialog::DoState(PointerWrap &p)
1089
{
1090
PSPDialog::DoState(p);
1091
1092
auto s = p.Section("PSPOskDialog", 1, 2);
1093
if (!s)
1094
return;
1095
1096
// TODO: Should we save currentKeyboard/currentKeyboardLanguage?
1097
1098
Do(p, oskParams);
1099
Do(p, oskDesc);
1100
Do(p, oskIntext);
1101
Do(p, oskOuttext);
1102
Do(p, selectedChar);
1103
if (s >= 2) {
1104
Do(p, inputChars);
1105
} else {
1106
// Discard the wstring.
1107
std::wstring wstr;
1108
Do(p, wstr);
1109
}
1110
// Don't need to save state native status or value.
1111
}
1112
1113
pspUtilityDialogCommon *PSPOskDialog::GetCommonParam()
1114
{
1115
return &oskParams->base;
1116
}
1117
1118