From da07772b1c897212c36f706b792410b2159eec7d Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Sun, 15 Dec 2024 21:33:33 -0500 Subject: [PATCH 1/4] Allow typing with keyboard in menuitemKeyboard --- port/include/input.h | 26 +++++ port/src/input.c | 54 +++++++++ src/game/menu.c | 29 +++++ src/game/menuitem.c | 261 ++++++++++++++++++++++++++++++++++++------- src/include/data.h | 2 + 5 files changed, 329 insertions(+), 43 deletions(-) diff --git a/port/include/input.h b/port/include/input.h index b0a38298f..cbfd4a88c 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -21,6 +21,7 @@ enum virtkey { VK_KEYBOARD_BEGIN = 0, VK_RETURN = 40, VK_ESCAPE = 41, + VK_BACKSPACE = 42, VK_DELETE = 76, /* same order as SDL mouse buttons */ @@ -87,6 +88,14 @@ enum mouselockmode { MLOCK_AUTO = 2 }; +enum keymod { + /* same order as SDL keymods */ + KM_LSHIFT = 0x0001, + KM_RSHIFT = 0x0002, + KM_CAPS = 0x2000, + KM_SHIFT = KM_LSHIFT | KM_RSHIFT +}; + // returns bitmask of connected controllers or -1 if failed s32 inputInit(void); @@ -140,6 +149,9 @@ s32 inputAssignController(s32 cidx, s32 id); s32 inputKeyPressed(u32 vk); s32 inputKeyJustPressed(u32 vk); +// same as inputKeyJustPressed but returns true again if key repeat activates +s32 inputKeyJustPressedWithRepeat(u32 vk); + // idx is controller index, contbtn is one of the CONT_ constants s32 inputButtonPressed(s32 idx, u32 contbtn); @@ -217,4 +229,18 @@ s32 inputAutoLockMouse(s32 wantlock); // show/hide mouse cursor; if mouse lock is on the cursor is always hidden void inputMouseShowCursor(s32 show); +// enables/disables/queries text input event processing, respectively +void inputStartTextInput(void); +void inputStopTextInput(void); +s32 inputIsTextInputActive(void); + +// fills textInputBuffer with textInput chars +void inputSetTextInput(char *textInput); + +// returns pointer to textInputBuffer +char *inputGetTextInput(void); + +// returns keymod values +u32 inputGetModState(void); + #endif diff --git a/port/src/input.c b/port/src/input.c index 59a842d71..d4f016d7f 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -87,6 +87,10 @@ static f32 mouseSensX = 1.5f; static f32 mouseSensY = 1.5f; static s32 lastKey = 0; +static s32 keyRepeat = 0; + +// buffer used to store text input during a text input event +static char textInputBuffer[11] = {0}; static const char *ckNames[CK_TOTAL_COUNT] = { "R_CBUTTONS", @@ -496,6 +500,7 @@ static int inputEventFilter(void *data, SDL_Event *event) if (!lastKey) { lastKey = VK_KEYBOARD_BEGIN + event->key.keysym.scancode; } + keyRepeat = event->key.repeat; break; case SDL_CONTROLLERBUTTONDOWN: @@ -521,6 +526,9 @@ static int inputEventFilter(void *data, SDL_Event *event) } } break; + case SDL_TEXTINPUT: + inputSetTextInput(event->text.text); + break; default: break; @@ -658,6 +666,9 @@ s32 inputInit(void) SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC); } + // SDL enables text input event handling by default + inputStopTextInput(); + // try to load controller db from an external file in the save folder if (fsFileSize("$S/" CONTROLLERDB_FNAME)) { const char *dbpath = fsFullPath("$S/" CONTROLLERDB_FNAME); @@ -1114,6 +1125,11 @@ s32 inputKeyJustPressed(u32 vk) return result; } +s32 inputKeyJustPressedWithRepeat(u32 vk) +{ + return inputKeyJustPressed(vk) || (keyRepeat && inputKeyPressed(vk)); +} + static inline u32 inputContToContKey(const u32 cont) { if (cont == 0) { @@ -1316,6 +1332,44 @@ s32 inputGetLastKey(void) return lastKey; } +void inputStartTextInput(void) +{ + SDL_StartTextInput(); +} + +void inputStopTextInput(void) +{ + SDL_StopTextInput(); + textInputBuffer[0] = '\0'; +} + +s32 inputIsTextInputActive(void) +{ + return SDL_IsTextInputActive(); +} + +void inputSetTextInput(char *textInput) +{ + for (size_t i = 0; i < sizeof textInputBuffer - 1; ++i) { + if (textInput && i < strlen(textInput)) { + textInputBuffer[i] = textInput[i]; + } else { + textInputBuffer[i] = '\0'; + } + } +} + +char *inputGetTextInput(void) +{ + return textInputBuffer; +} + + +u32 inputGetModState(void) +{ + return SDL_GetModState(); +} + PD_CONSTRUCTOR static void inputConfigInit(void) { configRegisterInt("Input.MouseEnabled", &mouseEnabled, 0, 1); diff --git a/src/game/menu.c b/src/game/menu.c index e2bf7d9a6..bef1ab4e8 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -141,6 +141,8 @@ s32 g_MpPlayerNum = 0; #ifndef PLATFORM_N64 s32 g_MenuMouseControl = true; s32 g_MenuUsingMouse = false; +s32 g_MenuTypeWithKeyboard = false; +s32 g_MenuTypeBackspace = false; #endif void menuPlaySound(s32 menusound) @@ -602,7 +604,11 @@ void menuCalculateItemSize(struct menuitem *item, s16 *width, s16 *height, struc break; case MENUITEMTYPE_KEYBOARD: *width = 130; +#ifndef PLATFORM_N64 + *height = inputGetAssignedControllerId(g_MpPlayerNum) == -1 ? 84 : 73; +#else *height = 73; +#endif break; case MENUITEMTYPE_LIST: if (item->param2 > 0) { @@ -4695,6 +4701,8 @@ void menuProcessInput(void) inputs.back2 = 0; #ifndef PLATFORM_N64 + s32 usingcontroller = inputGetAssignedControllerId(g_MpPlayerNum) != -1; + inputs.mousemoved = false; inputs.mousescroll = 0; inputs.mousex = 0; @@ -4703,6 +4711,11 @@ void menuProcessInput(void) if (menu->playernum == 0) { // ESC always acts as back inputs.back = inputKeyJustPressed(VK_ESCAPE); + if (inputs.back && g_MenuTypeWithKeyboard) { + if (inputIsTextInputActive()) { + inputStopTextInput(); + } + } if (inputMouseIsEnabled() && !inputMouseIsLocked() && g_MenuMouseControl) { inputs.mousemoved = inputMouseGetPosition(&inputs.mousex, &inputs.mousey); inputs.mousescroll = inputKeyPressed(VK_MOUSE_WHEEL_DN) - inputKeyPressed(VK_MOUSE_WHEEL_UP); @@ -4713,6 +4726,14 @@ void menuProcessInput(void) if (dialog && inputs.mousemoved) { g_MenuUsingMouse = true; } + g_MenuTypeBackspace = inputKeyJustPressedWithRepeat(VK_BACKSPACE); + + if (usingcontroller && g_MenuTypeWithKeyboard) { + g_MenuTypeWithKeyboard = false; + if (inputIsTextInputActive()) { + inputStopTextInput(); + } + } } #endif @@ -4783,7 +4804,11 @@ void menuProcessInput(void) #endif if (buttonsnow & B_BUTTON) { +#ifndef PLATFORM_N64 + inputs.back = inputIsTextInputActive() && !usingcontroller ? 0 : 1; +#else inputs.back = 1; +#endif } if (buttonsnow & Z_TRIG) { @@ -4795,7 +4820,11 @@ void menuProcessInput(void) } if (buttons & R_TRIG) { +#ifndef PLATFORM_N64 + inputs.shoulder = inputIsTextInputActive() && !usingcontroller ? 0 : 1; +#else inputs.shoulder = 1; +#endif } if ((stickx < 0 ? -stickx : stickx) < (thisstickx < 0 ? -thisstickx : thisstickx)) { diff --git a/src/game/menuitem.c b/src/game/menuitem.c index c95acb89e..c71548a9b 100644 --- a/src/game/menuitem.c +++ b/src/game/menuitem.c @@ -22,6 +22,9 @@ #include "lib/str.h" #include "data.h" #include "types.h" +#ifndef PLATFORM_N64 +#include "input.h" +#endif u8 g_MpSelectedPlayersForStats[MAX_PLAYERS]; @@ -1059,6 +1062,15 @@ Gfx *menuitemDropdownOverlay(Gfx *gdl, s16 x, s16 y, s16 x2, s16 y2, struct menu return gdl; } +#ifndef PLATFORM_N64 +static s32 menuitemKeyboardGetNumRows(s32 rows, s32 usingcontroller) +{ + return rows + !usingcontroller; +} +#else +#define menuitemKeyboardGetNumRows(rows, usingcontroller) (rows) +#endif + bool menuitemKeyboardIsStringEmptyOrSpaces(char *text) { s32 i; @@ -1110,6 +1122,9 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) u32 alpha; s32 textheight; s32 textwidth; +#ifndef PLATFORM_N64 + s32 usingcontroller = inputGetAssignedControllerId(g_MpPlayerNum) != -1; +#endif label[1] = '\n'; label[2] = '\0'; @@ -1164,7 +1179,7 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) gdl = text0f153838(gdl); // Render horizontal grid lines - for (row = 0; row < 6; row++) { + for (row = 0; row < menuitemKeyboardGetNumRows(6, usingcontroller); row++) { gdl = menugfxDrawFilledRect(gdl, context->x + 4, context->y + row * 11 + 13, context->x + 124, context->y + row * 11 + 14, 0x00ffff7f, 0x00ffff7f); } @@ -1177,6 +1192,12 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) rowspan = 4; } +#ifndef PLATFORM_N64 + if (!usingcontroller && (col == 0 || col == 10)) { + rowspan = 6; + } +#endif + gdl = menugfxDrawFilledRect(gdl, context->x + col * 12 + 4, context->y + 13, context->x + col * 12 + 5, context->y + rowspan * 11 + 14, 0x00ffff7f, 0x00ffff7f); } @@ -1187,7 +1208,7 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) y = context->y + 2; for (col = 0; col < 10; col++) { - for (row = 0; row < 5; row++) { + for (row = 0; row < menuitemKeyboardGetNumRows(5, usingcontroller); row++) { if (context->dialog->transitionfrac < 0) { textcolour = g_MenuColours[context->dialog->type].item_unfocused; } else { @@ -1206,6 +1227,12 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) textcolour = (textcolour & 0xff) | 0xffff0000; } +#ifndef PLATFORM_N64 + if (g_MenuTypeWithKeyboard && !usingcontroller && col == 0 && row == 5) { + textcolour = (textcolour & 0xff) | 0xffff0000; + } +#endif + // If this button is the focused one, set highlighted colour if (col == data->col && row == data->row) { alpha = menuGetSinOscFrac(40) * 255; @@ -1262,6 +1289,33 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) textMeasure(&textheight, &textwidth, langGet(labels[index]), g_CharsHandelGothicXs, g_FontHandelGothicXs, 0); x = (buttonwidth - textwidth) / 2 + x; +#ifndef PLATFORM_N64 + // Dim the CAPSLOCK button if typing with keyboard + if (index == 1 && g_MenuTypeWithKeyboard && !usingcontroller) { + if (data->capseffective) { + // CAPS button - make it yellow + textcolour = (textcolour & 0xff) | 0x7f7f0000; + } else { + if (context->dialog->transitionfrac < 0) { + textcolour = g_MenuColours[context->dialog->type].item_disabled; + } else { + textcolour = colourBlend( + g_MenuColours[context->dialog->type2].item_disabled, + g_MenuColours[context->dialog->type].item_disabled, + context->dialog->colourweight); + } + + if (context->dialog->dimmed) { + textcolour = (colourBlend(textcolour, 0, 127) & 0xffffff00) | (textcolour & 0xff); + } + + textSetWaveColours( + g_MenuWave2Colours[context->dialog->type].item_disabled, + g_MenuWave1Colours[context->dialog->type].item_disabled); + } + } +#endif + // Dim the OK button if string is empty if (index == 3 && menuitemKeyboardIsStringEmptyOrSpaces(data->string)) { if (context->dialog->transitionfrac < 0) { @@ -1284,13 +1338,29 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) gdl = textRenderProjected(gdl, &x, &y, langGet(labels[index]), g_CharsHandelGothicXs, g_FontHandelGothicXs, textcolour, context->width, context->height, 0, 0); - if (index == 3 && menuitemKeyboardIsStringEmptyOrSpaces(data->string)) { + if ((index == 3 && menuitemKeyboardIsStringEmptyOrSpaces(data->string)) +#ifndef PLATFORM_N64 + || (index == 1 && !usingcontroller && g_MenuTypeWithKeyboard) +#endif + ) { textSetWaveColours( g_MenuWave2Colours[context->dialog->type].item_unfocused, g_MenuWave1Colours[context->dialog->type].item_unfocused); } } - } else { + } +#ifndef PLATFORM_N64 + else if (row == 5) { + static char *kbtext = "TYPE WITH KEYBOARD"; + if (col == 0) { + ++y; + textMeasure(&textheight, &textwidth, kbtext, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0); + x = context->x + (context->width - textwidth) / 2; + gdl = textRenderProjected(gdl, &x, &y, kbtext, g_CharsHandelGothicXs, g_FontHandelGothicXs, textcolour, context->width, context->height, 0, 0); + } + } +#endif + else { // Alpha-numeric cell label[0] = g_KeyboardKeys[row][col]; @@ -1333,6 +1403,14 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) } } +#ifndef PLATFORM_N64 + if (data->row == 5 && !usingcontroller) { + if (data->col == 0) { + x2 += 108; + } + } +#endif + gdl = menugfxDrawLine(gdl, x1, y1, x2, y1 + 1, -1, -1); // top gdl = menugfxDrawLine(gdl, x2, y1, x2 + 1, y2 + 1, -1, -1); // right gdl = menugfxDrawLine(gdl, x1, y2, x2, y2 + 1, -1, -1); // bottom @@ -1361,6 +1439,9 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 struct menuitemdata_keyboard *kb = &data->keyboard; union handlerdata handlerdata; s32 delete = false; +#ifndef PLATFORM_N64 + s32 usingcontroller = inputGetAssignedControllerId(g_MpPlayerNum) != -1; +#endif if (tickflags & MENUTICKFLAG_ITEMISFOCUSED) { s16 prevcol = kb->col; @@ -1373,7 +1454,7 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 const s32 dleft = dialog->x + 4; const s32 dright = dleft + 12 * 10; const s32 dtop = menuitemGetTop(item, dialog) + 12; - const s32 dbottom = dtop + 11 * 5; + const s32 dbottom = dtop + 11 * (5 + !usingcontroller); const s32 mx = inputs->mousex; const s32 my = inputs->mousey; if (mx > dleft && mx < dright && my > dtop && my < dbottom) { @@ -1390,57 +1471,80 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 kb->col = 8; } } + if (kb->row == 5 && !usingcontroller) { + kb->col = 0; + } } } + + if (usingcontroller && kb->row == 5) { + kb->row = 4; + kb->col = 0; + } + + if (g_MenuTypeWithKeyboard && !usingcontroller) { + inputStartTextInput(); + } else #endif + { + // Handle left/right movement + // In most cases the loop only runs once, but on row 4 the buttons span + // across multiple columns so the loop will run again until the column + // number is valid. + if (inputs->leftright != 0) { + do { + kb->col += inputs->leftright; - // Handle left/right movement - // In most cases the loop only runs once, but on row 4 the buttons span - // across multiple columns so the loop will run again until the column - // number is valid. - if (inputs->leftright != 0) { - do { - kb->col += inputs->leftright; + if (kb->col < 0) { + kb->col = 9; + } - if (kb->col < 0) { - kb->col = 9; - } - - if (kb->col > 9) { - kb->col = 0; - } - } while (kb->row == 4 && kb->col != 0 && kb->col != 2 && kb->col != 5 && kb->col != 8); - } - - // Handle up/down movement - if (inputs->updown) { - kb->row += inputs->updown; - - if (kb->row < 0) { - kb->row = 4; + if (kb->col > 9) { + kb->col = 0; + } + } while ((kb->row == 4 && kb->col != 0 && kb->col != 2 && kb->col != 5 && kb->col != 8) +#ifndef PLATFORM_N64 + || (!usingcontroller && kb->row == 5 && kb->col != 0) +#endif + ); } - if (kb->row > 4) { - kb->row = 0; - } + // Handle up/down movement + if (inputs->updown) { + kb->row += inputs->updown; - // If moving onto row 4, bump column to a valid one - if (kb->row == 4) { - if (kb->col == 9) { - kb->col = 8; + if (kb->row < 0) { + kb->row = menuitemKeyboardGetNumRows(4, usingcontroller); } - if (kb->col == 7 || kb->col == 6) { - kb->col = 5; + if (kb->row > menuitemKeyboardGetNumRows(4, usingcontroller)) { + kb->row = 0; } - if (kb->col == 3 || kb->col == 4) { - kb->col = 2; + // If moving onto row 4, bump column to a valid one + if (kb->row == 4) { + if (kb->col == 9) { + kb->col = 8; + } + + if (kb->col == 7 || kb->col == 6) { + kb->col = 5; + } + + if (kb->col == 3 || kb->col == 4) { + kb->col = 2; + } + + if (kb->col == 1) { + kb->col = 0; + } } - if (kb->col == 1) { +#ifndef PLATFORM_N64 + if (kb->row == 5 && !usingcontroller) { kb->col = 0; } +#endif } } @@ -1462,6 +1566,10 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 menuPopDialog(); item->handler(MENUOP_SET, item, &handlerdata); + +#ifndef PLATFORM_N64 + inputStopTextInput(); +#endif } inputs->start = false; @@ -1475,7 +1583,12 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 } // CAPS - if (kb->col == 2) { + + if (kb->col == 2 +#ifndef PLATFORM_N64 + && (!g_MenuTypeWithKeyboard || usingcontroller) +#endif + ) { kb->capslock = 1 - kb->capslock; } @@ -1502,9 +1615,30 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 } inputs->select = false; + +#ifndef PLATFORM_N64 + inputStopTextInput(); +#endif } } - } else { + } +#ifndef PLATFORM_N64 + else if (!usingcontroller && kb->row == 5 && kb->col == 0) { + g_MenuTypeWithKeyboard ^= 1; + + if (g_MenuTypeWithKeyboard) { + menuPlaySound(MENUSOUND_TOGGLEON); + inputStartTextInput(); + } else { + if (kb->capslock) { + kb->capslock = 0; + } + menuPlaySound(MENUSOUND_TOGGLEOFF); + inputStopTextInput(); + } + } +#endif + else { // Pressed A on number or letter s32 appended = false; s32 i; @@ -1543,6 +1677,43 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 } } } +#ifndef PLATFORM_N64 + else if (g_MenuTypeWithKeyboard && !usingcontroller) { + delete = g_MenuTypeBackspace; + kb->capslock = (inputGetModState() & KM_CAPS) ? 1 : 0; + + if (strlen(inputGetTextInput())) { + s32 textwidth, textheight, charwidth; + size_t insertidx = strlen(kb->string); + textMeasure(&textheight, &textwidth, kb->string, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0); + char *textinput = inputGetTextInput(); + size_t textinputlen = textinput ? strlen(textinput) : 0; + + for (size_t j = 0; j < textinputlen && insertidx < sizeof kb->string - 1; ++j) { + u8 key[2] = {0}; + key[0] = textinput[j]; + + if ((key[0] >= ' ' && key[0] <= '!') || + (key[0] == '.') || + (key[0] >= '0' && key[0] <= '9') || + (key[0] == '?') || + (key[0] >= 'A' && key[0] <= 'Z') || + (key[0] >= 'a' && key[0] <= 'z')) { + textMeasure(&textheight, &charwidth, key, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0); + textwidth += charwidth; + + if (textwidth <= 58) { + kb->string[insertidx++] = key[0]; + } + + menuPlaySound(MENUSOUND_FOCUS); + } + } + + inputSetTextInput(NULL); + } + } +#endif // Handle deleting if (delete && kb->string[0] != '\0') { @@ -1567,7 +1738,11 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 u32 prev = kb->capseffective; kb->capseffective = kb->capslock; - if (inputs->shoulder) { + if (inputs->shoulder +#ifndef PLATFORM_N64 + || (g_MenuTypeWithKeyboard && !usingcontroller && (inputGetModState() & KM_SHIFT)) +#endif + ) { // Invert kb->capseffective = 1 - kb->capseffective; } diff --git a/src/include/data.h b/src/include/data.h index 24c106292..207860c5d 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -554,6 +554,8 @@ extern s32 g_TickExtraSleep; extern s32 g_MusicDisableMpDeath; extern s32 g_BgunGeMuzzleFlashes; extern s32 g_FileAutoSelect; +extern s32 g_MenuTypeWithKeyboard; +extern s32 g_MenuTypeBackspace; #define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayerstats->mpindex & 3] #define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy) From 8945809c1fa82ef4e5cf47c10a99adff132dc169 Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Tue, 17 Dec 2024 17:40:10 -0500 Subject: [PATCH 2/4] Factor in dstscroll to align mouse with chars in 3+ multiplayer --- src/game/menuitem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/menuitem.c b/src/game/menuitem.c index c71548a9b..a18e0bce5 100644 --- a/src/game/menuitem.c +++ b/src/game/menuitem.c @@ -4701,7 +4701,7 @@ Gfx *menuitemOverlay(Gfx *gdl, s16 x, s16 y, s16 x2, s16 y2, struct menuitem *it s32 menuitemGetTop(struct menuitem *item, struct menudialog *dialog) { struct menu *menu = &g_Menus[g_MpPlayerNum]; - s32 dtop = dialog->y + LINEHEIGHT + 1; + s32 dtop = dialog->y + LINEHEIGHT + 1 + dialog->dstscroll; for (s32 i = 0; i < dialog->numcols; ++i) { const s32 colindex = i + dialog->colstart; From 5084b4be399ea1ff3d8872ba613e2db4cf007b62 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Jan 2025 15:26:03 +0100 Subject: [PATCH 3/4] port: simplify the keyboard input a bit and match it to port-net's backspace key now repeats properly TYPE WITH KEYBOARD changes into a tooltip when typing players with controllers are allowed to type, because that seems more convenient players 2, 3 and 4 are allowed to type too if they hit the button, but only one player can have keyboard focus at a time --- port/include/input.h | 54 ++++--- port/src/input.c | 140 ++++++++++++----- src/game/menu.c | 30 +--- src/game/menuitem.c | 346 +++++++++++++++++-------------------------- src/include/data.h | 3 +- 5 files changed, 285 insertions(+), 288 deletions(-) diff --git a/port/include/input.h b/port/include/input.h index cbfd4a88c..916cd72ba 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -19,10 +19,29 @@ enum virtkey { /* same order as SDL scancodes */ VK_KEYBOARD_BEGIN = 0, + VK_A = 4, + VK_Z = 29, + VK_1 = 30, + VK_9 = 38, + VK_0 = 39, VK_RETURN = 40, VK_ESCAPE = 41, VK_BACKSPACE = 42, + VK_SPACE = 44, + VK_MINUS = 45, + VK_LEFTBRACKET = 47, + VK_RIGHTBRACKET = 48, + VK_SEMICOLON = 51, + VK_GRAVE = 53, + VK_COMMA = 54, + VK_PERIOD = 55, + VK_F1 = 58, + VK_F9 = 66, VK_DELETE = 76, + VK_LCTRL = 224, + VK_LSHIFT = 225, + VK_RCTRL = 228, + VK_RSHIFT = 229, /* same order as SDL mouse buttons */ VK_MOUSE_BEGIN = 512, @@ -46,6 +65,17 @@ enum virtkey { VK_TOTAL_COUNT = VK_JOY_BEGIN + INPUT_MAX_CONTROLLERS * INPUT_MAX_CONTROLLER_BUTTONS, }; +enum keymod { + /* same order as SDL keymods */ + KM_LSHIFT = 0x0001, + KM_RSHIFT = 0x0002, + KM_LCTRL = 0x0040, + KM_RCTRL = 0x0080, + KM_CAPS = 0x2000, + KM_CTRL = KM_LCTRL | KM_RCTRL, + KM_SHIFT = KM_LSHIFT | KM_RSHIFT +}; + enum contkey { CK_C_R, CK_C_L, @@ -88,14 +118,6 @@ enum mouselockmode { MLOCK_AUTO = 2 }; -enum keymod { - /* same order as SDL keymods */ - KM_LSHIFT = 0x0001, - KM_RSHIFT = 0x0002, - KM_CAPS = 0x2000, - KM_SHIFT = KM_LSHIFT | KM_RSHIFT -}; - // returns bitmask of connected controllers or -1 if failed s32 inputInit(void); @@ -149,9 +171,6 @@ s32 inputAssignController(s32 cidx, s32 id); s32 inputKeyPressed(u32 vk); s32 inputKeyJustPressed(u32 vk); -// same as inputKeyJustPressed but returns true again if key repeat activates -s32 inputKeyJustPressedWithRepeat(u32 vk); - // idx is controller index, contbtn is one of the CONT_ constants s32 inputButtonPressed(s32 idx, u32 contbtn); @@ -229,18 +248,19 @@ s32 inputAutoLockMouse(s32 wantlock); // show/hide mouse cursor; if mouse lock is on the cursor is always hidden void inputMouseShowCursor(s32 show); -// enables/disables/queries text input event processing, respectively void inputStartTextInput(void); void inputStopTextInput(void); s32 inputIsTextInputActive(void); -// fills textInputBuffer with textInput chars -void inputSetTextInput(char *textInput); +void inputClearLastTextChar(void); +char inputGetLastTextChar(void); -// returns pointer to textInputBuffer -char *inputGetTextInput(void); +s32 inputTextHandler(char *out, const u32 outSize, s32 *curCol, s32 oskCharsOnly); + +void inputClearClipboard(void); +const char *inputGetClipboard(void); // returns keymod values -u32 inputGetModState(void); +u32 inputGetKeyModState(void); #endif diff --git a/port/src/input.c b/port/src/input.c index d4f016d7f..8500f4716 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -87,10 +87,10 @@ static f32 mouseSensX = 1.5f; static f32 mouseSensY = 1.5f; static s32 lastKey = 0; -static s32 keyRepeat = 0; +static char lastChar = 0; +static s32 textInput = 0; -// buffer used to store text input during a text input event -static char textInputBuffer[11] = {0}; +static char *clipboardText = NULL; static const char *ckNames[CK_TOTAL_COUNT] = { "R_CBUTTONS", @@ -500,7 +500,6 @@ static int inputEventFilter(void *data, SDL_Event *event) if (!lastKey) { lastKey = VK_KEYBOARD_BEGIN + event->key.keysym.scancode; } - keyRepeat = event->key.repeat; break; case SDL_CONTROLLERBUTTONDOWN: @@ -526,8 +525,11 @@ static int inputEventFilter(void *data, SDL_Event *event) } } break; + case SDL_TEXTINPUT: - inputSetTextInput(event->text.text); + if (!lastChar && event->text.text[0] && (u8)event->text.text[0] < 0x80) { + lastChar = event->text.text[0]; + } break; default: @@ -666,9 +668,6 @@ s32 inputInit(void) SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC); } - // SDL enables text input event handling by default - inputStopTextInput(); - // try to load controller db from an external file in the save folder if (fsFileSize("$S/" CONTROLLERDB_FNAME)) { const char *dbpath = fsFullPath("$S/" CONTROLLERDB_FNAME); @@ -742,6 +741,14 @@ s32 inputReadController(s32 idx, OSContPad *npad) npad->button = 0; + if (textInput) { + npad->stick_x = 0; + npad->stick_y = 0; + npad->rstick_x = 0; + npad->rstick_y = 0; + return 0; + } + for (u32 i = 0; i < CONT_NUM_BUTTONS; ++i) { if (inputBindPressed(idx, i)) { npad->button |= 1U << i; @@ -1125,11 +1132,6 @@ s32 inputKeyJustPressed(u32 vk) return result; } -s32 inputKeyJustPressedWithRepeat(u32 vk) -{ - return inputKeyJustPressed(vk) || (keyRepeat && inputKeyPressed(vk)); -} - static inline u32 inputContToContKey(const u32 cont) { if (cont == 0) { @@ -1334,38 +1336,110 @@ s32 inputGetLastKey(void) void inputStartTextInput(void) { + lastChar = 0; + lastKey = 0; + textInput = 1; SDL_StartTextInput(); } +void inputClearLastTextChar(void) +{ + lastChar = 0; +} + +char inputGetLastTextChar(void) +{ + return lastChar; +} + +static inline s32 filterChar(const char ch) +{ + return isalnum(ch) || ch == ' ' || ch == '?' || ch == '!' || ch == '.'; +} + +s32 inputTextHandler(char *out, const u32 outSize, s32 *curCol, s32 oskCharsOnly) +{ + const s32 ctrlHeld = inputGetKeyModState() & KM_CTRL; + + if (!ctrlHeld) { + const char chr = inputGetLastTextChar(); + inputClearLastTextChar(); + const s32 valid = chr && (oskCharsOnly ? filterChar(chr) : isprint(chr)); + if (valid) { + if (*curCol < outSize - 1) { + out[(*curCol)++] = chr; + out[*curCol] = '\0'; + } + } + } + + const s32 key = inputGetLastKey(); + inputClearLastKey(); + if (ctrlHeld && (key == VK_A + ('v' - 'a'))) { + // CTRL+V; paste from clipboard + const char *clip = inputGetClipboard(); + if (clip) { + const s32 remain = outSize - *curCol - 1; + inputClearClipboard(); + *curCol += snprintf(out + *curCol, remain, "%s", clip); + if (*curCol > outSize) { + *curCol = outSize; + } + } + } else if (key == VK_BACKSPACE) { + if (*curCol) { + out[--*curCol] = '\0'; + } else { + out[0] = '\0'; + } + } else if (key == VK_RETURN) { + if (out[0] && *curCol) { + return 1; + } + } else if (key == VK_ESCAPE) { + return -1; + } + + return 0; +} + +void inputClearClipboard(void) +{ + if (clipboardText) { + SDL_free(clipboardText); + clipboardText = NULL; + } +} + +const char *inputGetClipboard(void) +{ + if (!clipboardText) { + char *text = SDL_GetClipboardText(); + if (text) { + clipboardText = text; + // remove non-printable and multibyte chars + for (; *text; ++text) { + if ((u8)*text < 0x20 || (u8)*text >= 0x7F) { + *text = '?'; + } + } + } + } + return clipboardText; +} + void inputStopTextInput(void) { SDL_StopTextInput(); - textInputBuffer[0] = '\0'; + textInput = 0; } s32 inputIsTextInputActive(void) { - return SDL_IsTextInputActive(); + return textInput; } -void inputSetTextInput(char *textInput) -{ - for (size_t i = 0; i < sizeof textInputBuffer - 1; ++i) { - if (textInput && i < strlen(textInput)) { - textInputBuffer[i] = textInput[i]; - } else { - textInputBuffer[i] = '\0'; - } - } -} - -char *inputGetTextInput(void) -{ - return textInputBuffer; -} - - -u32 inputGetModState(void) +u32 inputGetKeyModState(void) { return SDL_GetModState(); } diff --git a/src/game/menu.c b/src/game/menu.c index bef1ab4e8..970b62ddb 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -141,8 +141,7 @@ s32 g_MpPlayerNum = 0; #ifndef PLATFORM_N64 s32 g_MenuMouseControl = true; s32 g_MenuUsingMouse = false; -s32 g_MenuTypeWithKeyboard = false; -s32 g_MenuTypeBackspace = false; +s32 g_MenuKeyboardPlayer = -1; #endif void menuPlaySound(s32 menusound) @@ -605,7 +604,7 @@ void menuCalculateItemSize(struct menuitem *item, s16 *width, s16 *height, struc case MENUITEMTYPE_KEYBOARD: *width = 130; #ifndef PLATFORM_N64 - *height = inputGetAssignedControllerId(g_MpPlayerNum) == -1 ? 84 : 73; + *height = 84; #else *height = 73; #endif @@ -4701,8 +4700,6 @@ void menuProcessInput(void) inputs.back2 = 0; #ifndef PLATFORM_N64 - s32 usingcontroller = inputGetAssignedControllerId(g_MpPlayerNum) != -1; - inputs.mousemoved = false; inputs.mousescroll = 0; inputs.mousex = 0; @@ -4711,11 +4708,6 @@ void menuProcessInput(void) if (menu->playernum == 0) { // ESC always acts as back inputs.back = inputKeyJustPressed(VK_ESCAPE); - if (inputs.back && g_MenuTypeWithKeyboard) { - if (inputIsTextInputActive()) { - inputStopTextInput(); - } - } if (inputMouseIsEnabled() && !inputMouseIsLocked() && g_MenuMouseControl) { inputs.mousemoved = inputMouseGetPosition(&inputs.mousex, &inputs.mousey); inputs.mousescroll = inputKeyPressed(VK_MOUSE_WHEEL_DN) - inputKeyPressed(VK_MOUSE_WHEEL_UP); @@ -4726,11 +4718,11 @@ void menuProcessInput(void) if (dialog && inputs.mousemoved) { g_MenuUsingMouse = true; } - g_MenuTypeBackspace = inputKeyJustPressedWithRepeat(VK_BACKSPACE); - - if (usingcontroller && g_MenuTypeWithKeyboard) { - g_MenuTypeWithKeyboard = false; - if (inputIsTextInputActive()) { + if (inputs.back && g_MenuKeyboardPlayer != -1) { + // eat the back input so that other menus don't quit when we hit ESC + inputs.back = false; + if (g_MenuKeyboardPlayer == menu->playernum) { + g_MenuKeyboardPlayer = -1; inputStopTextInput(); } } @@ -4804,11 +4796,7 @@ void menuProcessInput(void) #endif if (buttonsnow & B_BUTTON) { -#ifndef PLATFORM_N64 - inputs.back = inputIsTextInputActive() && !usingcontroller ? 0 : 1; -#else inputs.back = 1; -#endif } if (buttonsnow & Z_TRIG) { @@ -4820,11 +4808,7 @@ void menuProcessInput(void) } if (buttons & R_TRIG) { -#ifndef PLATFORM_N64 - inputs.shoulder = inputIsTextInputActive() && !usingcontroller ? 0 : 1; -#else inputs.shoulder = 1; -#endif } if ((stickx < 0 ? -stickx : stickx) < (thisstickx < 0 ? -thisstickx : thisstickx)) { diff --git a/src/game/menuitem.c b/src/game/menuitem.c index a18e0bce5..7fa556e9e 100644 --- a/src/game/menuitem.c +++ b/src/game/menuitem.c @@ -24,6 +24,9 @@ #include "types.h" #ifndef PLATFORM_N64 #include "input.h" +#define MENU_KEYBOARD_ROWS 6 +#else +#define MENU_KEYBOARD_ROWS 5 #endif u8 g_MpSelectedPlayersForStats[MAX_PLAYERS]; @@ -1062,15 +1065,6 @@ Gfx *menuitemDropdownOverlay(Gfx *gdl, s16 x, s16 y, s16 x2, s16 y2, struct menu return gdl; } -#ifndef PLATFORM_N64 -static s32 menuitemKeyboardGetNumRows(s32 rows, s32 usingcontroller) -{ - return rows + !usingcontroller; -} -#else -#define menuitemKeyboardGetNumRows(rows, usingcontroller) (rows) -#endif - bool menuitemKeyboardIsStringEmptyOrSpaces(char *text) { s32 i; @@ -1122,9 +1116,6 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) u32 alpha; s32 textheight; s32 textwidth; -#ifndef PLATFORM_N64 - s32 usingcontroller = inputGetAssignedControllerId(g_MpPlayerNum) != -1; -#endif label[1] = '\n'; label[2] = '\0'; @@ -1179,7 +1170,7 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) gdl = text0f153838(gdl); // Render horizontal grid lines - for (row = 0; row < menuitemKeyboardGetNumRows(6, usingcontroller); row++) { + for (row = 0; row < MENU_KEYBOARD_ROWS + 1; row++) { gdl = menugfxDrawFilledRect(gdl, context->x + 4, context->y + row * 11 + 13, context->x + 124, context->y + row * 11 + 14, 0x00ffff7f, 0x00ffff7f); } @@ -1192,23 +1183,24 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) rowspan = 4; } -#ifndef PLATFORM_N64 - if (!usingcontroller && (col == 0 || col == 10)) { - rowspan = 6; - } -#endif - gdl = menugfxDrawFilledRect(gdl, context->x + col * 12 + 4, context->y + 13, context->x + col * 12 + 5, context->y + rowspan * 11 + 14, 0x00ffff7f, 0x00ffff7f); } +#if MENU_KEYBOARD_ROWS > 5 + gdl = menugfxDrawFilledRect(gdl, context->x + 4, context->y + 5 * 11 + 13, + context->x + 5, context->y + 6 * 11 + 14, 0x00ffff7f, 0x00ffff7f); + gdl = menugfxDrawFilledRect(gdl, context->x + 10 * 12 + 4, context->y + 5 * 11 + 13, + context->x + 10 * 12 + 5, context->y + 6 * 11 + 14, 0x00ffff7f, 0x00ffff7f); +#endif + gdl = text0f153628(gdl); x = context->x + 10; y = context->y + 2; for (col = 0; col < 10; col++) { - for (row = 0; row < menuitemKeyboardGetNumRows(5, usingcontroller); row++) { + for (row = 0; row < MENU_KEYBOARD_ROWS; row++) { if (context->dialog->transitionfrac < 0) { textcolour = g_MenuColours[context->dialog->type].item_unfocused; } else { @@ -1227,12 +1219,6 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) textcolour = (textcolour & 0xff) | 0xffff0000; } -#ifndef PLATFORM_N64 - if (g_MenuTypeWithKeyboard && !usingcontroller && col == 0 && row == 5) { - textcolour = (textcolour & 0xff) | 0xffff0000; - } -#endif - // If this button is the focused one, set highlighted colour if (col == data->col && row == data->row) { alpha = menuGetSinOscFrac(40) * 255; @@ -1289,33 +1275,6 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) textMeasure(&textheight, &textwidth, langGet(labels[index]), g_CharsHandelGothicXs, g_FontHandelGothicXs, 0); x = (buttonwidth - textwidth) / 2 + x; -#ifndef PLATFORM_N64 - // Dim the CAPSLOCK button if typing with keyboard - if (index == 1 && g_MenuTypeWithKeyboard && !usingcontroller) { - if (data->capseffective) { - // CAPS button - make it yellow - textcolour = (textcolour & 0xff) | 0x7f7f0000; - } else { - if (context->dialog->transitionfrac < 0) { - textcolour = g_MenuColours[context->dialog->type].item_disabled; - } else { - textcolour = colourBlend( - g_MenuColours[context->dialog->type2].item_disabled, - g_MenuColours[context->dialog->type].item_disabled, - context->dialog->colourweight); - } - - if (context->dialog->dimmed) { - textcolour = (colourBlend(textcolour, 0, 127) & 0xffffff00) | (textcolour & 0xff); - } - - textSetWaveColours( - g_MenuWave2Colours[context->dialog->type].item_disabled, - g_MenuWave1Colours[context->dialog->type].item_disabled); - } - } -#endif - // Dim the OK button if string is empty if (index == 3 && menuitemKeyboardIsStringEmptyOrSpaces(data->string)) { if (context->dialog->transitionfrac < 0) { @@ -1336,31 +1295,45 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) g_MenuWave1Colours[context->dialog->type].item_disabled); } +#ifndef PLATFORM_N64 + // Dim the Caps button if keyboard typing is enabled + if (index == 1 && g_MenuKeyboardPlayer == g_MpPlayerNum) { + textcolour = colourBlend( + g_MenuColours[context->dialog->type2].item_disabled, + g_MenuColours[context->dialog->type].item_disabled, + context->dialog->colourweight); + textSetWaveColours( + g_MenuWave2Colours[context->dialog->type].item_disabled, + g_MenuWave1Colours[context->dialog->type].item_disabled); + } +#endif + gdl = textRenderProjected(gdl, &x, &y, langGet(labels[index]), g_CharsHandelGothicXs, g_FontHandelGothicXs, textcolour, context->width, context->height, 0, 0); - if ((index == 3 && menuitemKeyboardIsStringEmptyOrSpaces(data->string)) -#ifndef PLATFORM_N64 - || (index == 1 && !usingcontroller && g_MenuTypeWithKeyboard) -#endif - ) { + if (index == 3 && menuitemKeyboardIsStringEmptyOrSpaces(data->string)) { textSetWaveColours( g_MenuWave2Colours[context->dialog->type].item_unfocused, g_MenuWave1Colours[context->dialog->type].item_unfocused); } } - } + } else if (row == 5) { #ifndef PLATFORM_N64 - else if (row == 5) { - static char *kbtext = "TYPE WITH KEYBOARD"; if (col == 0) { + char *kbtext; + // make the button yellow if it's active + if (g_MenuKeyboardPlayer == g_MpPlayerNum) { + textcolour = (textcolour & 0xff) | 0xffff0000; + kbtext = (char *)"ESC: CANCEL ENTER: OK"; + } else { + kbtext = (char *)"TYPE WITH KEYBOARD"; + } ++y; textMeasure(&textheight, &textwidth, kbtext, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0); x = context->x + (context->width - textwidth) / 2; gdl = textRenderProjected(gdl, &x, &y, kbtext, g_CharsHandelGothicXs, g_FontHandelGothicXs, textcolour, context->width, context->height, 0, 0); } - } #endif - else { + } else { // Alpha-numeric cell label[0] = g_KeyboardKeys[row][col]; @@ -1401,16 +1374,10 @@ Gfx *menuitemKeyboardRender(Gfx *gdl, struct menurendercontext *context) if (data->col == 0) { x2 += 12; } + } else if (data->row == 5) { + x2 = context->x + 9 * 12 + 16; } -#ifndef PLATFORM_N64 - if (data->row == 5 && !usingcontroller) { - if (data->col == 0) { - x2 += 108; - } - } -#endif - gdl = menugfxDrawLine(gdl, x1, y1, x2, y1 + 1, -1, -1); // top gdl = menugfxDrawLine(gdl, x2, y1, x2 + 1, y2 + 1, -1, -1); // right gdl = menugfxDrawLine(gdl, x1, y2, x2, y2 + 1, -1, -1); // bottom @@ -1439,9 +1406,6 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 struct menuitemdata_keyboard *kb = &data->keyboard; union handlerdata handlerdata; s32 delete = false; -#ifndef PLATFORM_N64 - s32 usingcontroller = inputGetAssignedControllerId(g_MpPlayerNum) != -1; -#endif if (tickflags & MENUTICKFLAG_ITEMISFOCUSED) { s16 prevcol = kb->col; @@ -1454,7 +1418,7 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 const s32 dleft = dialog->x + 4; const s32 dright = dleft + 12 * 10; const s32 dtop = menuitemGetTop(item, dialog) + 12; - const s32 dbottom = dtop + 11 * (5 + !usingcontroller); + const s32 dbottom = dtop + 11 * 6; const s32 mx = inputs->mousex; const s32 my = inputs->mousey; if (mx > dleft && mx < dright && my > dtop && my < dbottom) { @@ -1470,84 +1434,67 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 } else { kb->col = 8; } + } else if (kb->row == 5) { + kb->col = 0; } - if (kb->row == 5 && !usingcontroller) { + } + } +#endif + + // Handle left/right movement + // In most cases the loop only runs once, but on row 4 the buttons span + // across multiple columns so the loop will run again until the column + // number is valid. + if (inputs->leftright != 0) { + do { + kb->col += inputs->leftright; + + if (kb->col < 0) { + kb->col = 9; + } + + if (kb->col > 9) { + kb->col = 0; + } + } while (kb->row == 4 && kb->col != 0 && kb->col != 2 && kb->col != 5 && kb->col != 8); + } + + // Handle up/down movement + if (inputs->updown) { + kb->row += inputs->updown; + + if (kb->row < 0) { + kb->row = 5; + } + + if (kb->row > MENU_KEYBOARD_ROWS - 1) { + kb->row = 0; + } + + // If moving onto row 4, bump column to a valid one + if (kb->row == 4) { + if (kb->col == 9) { + kb->col = 8; + } + + if (kb->col == 7 || kb->col == 6) { + kb->col = 5; + } + + if (kb->col == 3 || kb->col == 4) { + kb->col = 2; + } + + if (kb->col == 1) { kb->col = 0; } } } - if (usingcontroller && kb->row == 5) { - kb->row = 4; + if (kb->row == 5) { kb->col = 0; } - if (g_MenuTypeWithKeyboard && !usingcontroller) { - inputStartTextInput(); - } else -#endif - { - // Handle left/right movement - // In most cases the loop only runs once, but on row 4 the buttons span - // across multiple columns so the loop will run again until the column - // number is valid. - if (inputs->leftright != 0) { - do { - kb->col += inputs->leftright; - - if (kb->col < 0) { - kb->col = 9; - } - - if (kb->col > 9) { - kb->col = 0; - } - } while ((kb->row == 4 && kb->col != 0 && kb->col != 2 && kb->col != 5 && kb->col != 8) -#ifndef PLATFORM_N64 - || (!usingcontroller && kb->row == 5 && kb->col != 0) -#endif - ); - } - - // Handle up/down movement - if (inputs->updown) { - kb->row += inputs->updown; - - if (kb->row < 0) { - kb->row = menuitemKeyboardGetNumRows(4, usingcontroller); - } - - if (kb->row > menuitemKeyboardGetNumRows(4, usingcontroller)) { - kb->row = 0; - } - - // If moving onto row 4, bump column to a valid one - if (kb->row == 4) { - if (kb->col == 9) { - kb->col = 8; - } - - if (kb->col == 7 || kb->col == 6) { - kb->col = 5; - } - - if (kb->col == 3 || kb->col == 4) { - kb->col = 2; - } - - if (kb->col == 1) { - kb->col = 0; - } - } - -#ifndef PLATFORM_N64 - if (kb->row == 5 && !usingcontroller) { - kb->col = 0; - } -#endif - } - } - if (prevcol != kb->col || prevrow != kb->row) { menuPlaySound(MENUSOUND_KEYBOARDFOCUS); } @@ -1566,15 +1513,42 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 menuPopDialog(); item->handler(MENUOP_SET, item, &handlerdata); - -#ifndef PLATFORM_N64 - inputStopTextInput(); -#endif } inputs->start = false; } +#ifndef PLATFORM_N64 + if (g_MenuKeyboardPlayer == g_MpPlayerNum) { + // match caps state to keyboard shift/caps if typing with keyboard + const u32 kmod = inputGetKeyModState(); + kb->capslock = (kmod & KM_CAPS) != 0; + if (kmod & KM_SHIFT) { + kb->capslock = !kb->capslock; + } + // handle text input + s32 prevpos = strlen(kb->string); + s32 pos = prevpos; + s32 result = inputTextHandler(kb->string, sizeof(kb->string), &pos, true); + if (result == -1) { + // cancel + kb->row = 5; + kb->col = 0; + inputs->select = true; + } else if (result == 1) { + // accept + kb->row = 4; + kb->col = 8; + inputs->select = true; + g_MenuKeyboardPlayer = -1; + inputStopTextInput(); + } else if (prevpos < pos) { + // added char + menuPlaySound(MENUSOUND_FOCUS); + } + } +#endif + if (inputs->select) { if (kb->row == 4) { // DEL @@ -1583,12 +1557,7 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 } // CAPS - - if (kb->col == 2 -#ifndef PLATFORM_N64 - && (!g_MenuTypeWithKeyboard || usingcontroller) -#endif - ) { + if (kb->col == 2) { kb->capslock = 1 - kb->capslock; } @@ -1615,30 +1584,22 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 } inputs->select = false; - -#ifndef PLATFORM_N64 - inputStopTextInput(); -#endif } } - } + } else if (kb->row == 5) { #ifndef PLATFORM_N64 - else if (!usingcontroller && kb->row == 5 && kb->col == 0) { - g_MenuTypeWithKeyboard ^= 1; - - if (g_MenuTypeWithKeyboard) { - menuPlaySound(MENUSOUND_TOGGLEON); + if (g_MenuKeyboardPlayer == -1) { + g_MenuKeyboardPlayer = g_MpPlayerNum; + inputClearLastTextChar(); inputStartTextInput(); + menuPlaySound(MENUSOUND_SELECT); } else { - if (kb->capslock) { - kb->capslock = 0; - } - menuPlaySound(MENUSOUND_TOGGLEOFF); + g_MenuKeyboardPlayer = -1; inputStopTextInput(); + menuPlaySound(MENUSOUND_KEYBOARDCANCEL); } - } #endif - else { + } else { // Pressed A on number or letter s32 appended = false; s32 i; @@ -1677,43 +1638,6 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 } } } -#ifndef PLATFORM_N64 - else if (g_MenuTypeWithKeyboard && !usingcontroller) { - delete = g_MenuTypeBackspace; - kb->capslock = (inputGetModState() & KM_CAPS) ? 1 : 0; - - if (strlen(inputGetTextInput())) { - s32 textwidth, textheight, charwidth; - size_t insertidx = strlen(kb->string); - textMeasure(&textheight, &textwidth, kb->string, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0); - char *textinput = inputGetTextInput(); - size_t textinputlen = textinput ? strlen(textinput) : 0; - - for (size_t j = 0; j < textinputlen && insertidx < sizeof kb->string - 1; ++j) { - u8 key[2] = {0}; - key[0] = textinput[j]; - - if ((key[0] >= ' ' && key[0] <= '!') || - (key[0] == '.') || - (key[0] >= '0' && key[0] <= '9') || - (key[0] == '?') || - (key[0] >= 'A' && key[0] <= 'Z') || - (key[0] >= 'a' && key[0] <= 'z')) { - textMeasure(&textheight, &charwidth, key, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0); - textwidth += charwidth; - - if (textwidth <= 58) { - kb->string[insertidx++] = key[0]; - } - - menuPlaySound(MENUSOUND_FOCUS); - } - } - - inputSetTextInput(NULL); - } - } -#endif // Handle deleting if (delete && kb->string[0] != '\0') { @@ -1738,11 +1662,7 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 u32 prev = kb->capseffective; kb->capseffective = kb->capslock; - if (inputs->shoulder -#ifndef PLATFORM_N64 - || (g_MenuTypeWithKeyboard && !usingcontroller && (inputGetModState() & KM_SHIFT)) -#endif - ) { + if (inputs->shoulder) { // Invert kb->capseffective = 1 - kb->capseffective; } @@ -4701,7 +4621,7 @@ Gfx *menuitemOverlay(Gfx *gdl, s16 x, s16 y, s16 x2, s16 y2, struct menuitem *it s32 menuitemGetTop(struct menuitem *item, struct menudialog *dialog) { struct menu *menu = &g_Menus[g_MpPlayerNum]; - s32 dtop = dialog->y + LINEHEIGHT + 1 + dialog->dstscroll; + s32 dtop = dialog->y + LINEHEIGHT + 1; for (s32 i = 0; i < dialog->numcols; ++i) { const s32 colindex = i + dialog->colstart; diff --git a/src/include/data.h b/src/include/data.h index 207860c5d..07086603f 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -538,6 +538,7 @@ extern const struct weathercfg g_DefaultWeatherConfig; extern const struct weathercfg *g_CurWeatherConfig; extern s32 g_MenuUsingMouse; +extern s32 g_MenuKeyboardPlayer; extern f32 g_ViShakeIntensityMult; extern u32 g_TexFilter2D; @@ -554,8 +555,6 @@ extern s32 g_TickExtraSleep; extern s32 g_MusicDisableMpDeath; extern s32 g_BgunGeMuzzleFlashes; extern s32 g_FileAutoSelect; -extern s32 g_MenuTypeWithKeyboard; -extern s32 g_MenuTypeBackspace; #define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayerstats->mpindex & 3] #define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy) From 4f7817a0ca83a893ee515854ff4f93415810810a Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Jan 2025 15:45:42 +0100 Subject: [PATCH 4/4] port: forgot the constant --- src/game/menuitem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/menuitem.c b/src/game/menuitem.c index 7fa556e9e..7bf3a37ea 100644 --- a/src/game/menuitem.c +++ b/src/game/menuitem.c @@ -1464,7 +1464,7 @@ bool menuitemKeyboardTick(struct menuitem *item, struct menuinputs *inputs, u32 kb->row += inputs->updown; if (kb->row < 0) { - kb->row = 5; + kb->row = MENU_KEYBOARD_ROWS - 1; } if (kb->row > MENU_KEYBOARD_ROWS - 1) {