Basic unified text input port

This commit is contained in:
NepDisk 2025-08-14 22:57:24 -04:00
parent be4d7eda72
commit a4b6934cc5
10 changed files with 464 additions and 370 deletions

View file

@ -441,4 +441,3 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL Windows AND NOT "${SRB2_CONFIG_INTERNAL_LIBRA
COMMENT "Copying runtime DLLs"
)
endif()

View file

@ -34,6 +34,7 @@ m_cond.c
m_easing.c
m_fixed.c
m_menu.c
m_textinput.c
m_memcpy.c
m_misc.cpp
m_perfstats.c

View file

@ -32,6 +32,7 @@
#include "i_threads.h"
#include "d_main.h"
#include "m_menu.h"
#include "m_textinput.h"
#include "filesrch.h"
#include "m_misc.h"
@ -101,9 +102,7 @@ static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
static INT32 inputline; // current input line number
static INT32 inputhist; // line number of history input line to restore
static size_t input_cur; // position of cursor in line
static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
static size_t input_len; // length of current line, used to bound cursor and such
static textinput_t input;
// notice: input does NOT include the "$" at the start of the line. - 11/3/16
// protos.
@ -502,7 +501,8 @@ static void CON_InputInit(void)
// prepare the first prompt line
memset(inputlines, 0, sizeof (inputlines));
inputline = 0;
input_cur = input_sel = input_len = 0;
M_TextInputInit(&input, inputlines[inputline], CON_MAXPROMPTCHARS);
Unlock_state();
}
@ -799,119 +799,6 @@ void CON_Ticker(void)
Unlock_state();
}
//
// ----
//
// Shortcuts for adding and deleting characters, strings, and sections
// Necessary due to moving cursor
//
static void CON_InputClear(void)
{
Lock_state();
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
input_cur = input_sel = input_len = 0;
Unlock_state();
}
static void CON_InputSetString(const char *c)
{
Lock_state();
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
strcpy(inputlines[inputline], c);
input_cur = input_sel = input_len = strlen(c);
Unlock_state();
}
static void CON_InputAddString(const char *c)
{
size_t csize = strlen(c);
Lock_state();
if (input_len + csize > CON_MAXPROMPTCHARS-1)
{
Unlock_state();
return;
}
if (input_cur != input_len)
memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
memcpy(&inputlines[inputline][input_cur], c, csize);
input_len += csize;
input_sel = (input_cur += csize);
Unlock_state();
}
static void CON_InputDelSelection(void)
{
size_t start, end, len;
Lock_state();
if (!input_cur)
{
Unlock_state();
return;
}
if (input_cur > input_sel)
{
start = input_sel;
end = input_cur;
}
else
{
start = input_cur;
end = input_sel;
}
len = (end - start);
if (end != input_len)
memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
memset(&inputlines[inputline][input_len - len], 0, len);
input_len -= len;
input_sel = input_cur = start;
Unlock_state();
}
static void CON_InputAddChar(char c)
{
if (input_len >= CON_MAXPROMPTCHARS-1)
return;
Lock_state();
if (input_cur != input_len)
memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
inputlines[inputline][input_cur++] = c;
inputlines[inputline][++input_len] = 0;
input_sel = input_cur;
Unlock_state();
}
static void CON_InputDelChar(void)
{
if (!input_cur)
return;
Lock_state();
if (input_cur != input_len)
memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
inputlines[inputline][--input_len] = 0;
input_sel = --input_cur;
Unlock_state();
}
//
// ----
//
@ -995,77 +882,16 @@ boolean CON_Responder(event_t *ev)
}
}
Lock_state();
M_TextInputHandle(&input, key);
Unlock_state();
// Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
if (key == KEY_LSHIFT || key == KEY_RSHIFT
|| key == KEY_LCTRL || key == KEY_RCTRL
|| key == KEY_LALT || key == KEY_RALT)
return true;
if (key == KEY_LEFTARROW)
{
if (input_cur != 0)
{
if (ctrldown)
input_cur = M_JumpWordReverse(inputlines[inputline], input_cur);
else
--input_cur;
}
if (!shiftdown)
input_sel = input_cur;
return true;
}
else if (key == KEY_RIGHTARROW)
{
if (input_cur < input_len)
{
if (ctrldown)
input_cur += M_JumpWord(&inputlines[inputline][input_cur]);
else
++input_cur;
}
if (!shiftdown)
input_sel = input_cur;
return true;
}
// backspace and delete command prompt
if (input_sel != input_cur)
{
if (key == KEY_BACKSPACE || key == KEY_DEL)
{
CON_InputDelSelection();
return true;
}
}
else if (key == KEY_BACKSPACE)
{
if (ctrldown)
{
input_sel = M_JumpWordReverse(inputlines[inputline], input_cur);
CON_InputDelSelection();
}
else
CON_InputDelChar();
return true;
}
else if (key == KEY_DEL)
{
if (input_cur == input_len)
return true;
if (ctrldown)
{
input_sel = input_cur + M_JumpWord(&inputlines[inputline][input_cur]);
CON_InputDelSelection();
}
else
{
++input_cur;
CON_InputDelChar();
}
return true;
}
// ctrl modifier -- changes behavior, adds shortcuts
if (ctrldown)
{
@ -1076,9 +902,9 @@ boolean CON_Responder(event_t *ev)
if (!completion[0])
{
if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
if (!input.length || input.length >= 40 || strchr(input.buffer, ' '))
return true;
strcpy(completion, inputlines[inputline]);
strcpy(completion, input.buffer);
}
len = strlen(completion);
@ -1117,42 +943,9 @@ boolean CON_Responder(event_t *ev)
return true;
}
if (key == 'x' || key == 'X')
{
if (input_sel > input_cur)
I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
else
I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
CON_InputDelSelection();
// Those are already handled in M_TextInputHandle, but they do extra logic... Maybe textinput_t should have some sort of callbacks?
if (key == 'x' || key == 'X' || key == 'v' || key == 'V')
completion[0] = 0;
return true;
}
else if (key == 'c' || key == 'C')
{
if (input_sel > input_cur)
I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
else
I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
return true;
}
else if (key == 'v' || key == 'V')
{
const char *paste = I_ClipboardPaste();
if (input_sel != input_cur)
CON_InputDelSelection();
if (paste != NULL)
CON_InputAddString(paste);
completion[0] = 0;
return true;
}
// Select all
if (key == 'a' || key == 'A')
{
input_sel = 0;
input_cur = input_len;
return true;
}
// ...why shouldn't it eat the key? if it doesn't, it just means you
// can control Sonic from the console, which is silly
@ -1167,9 +960,9 @@ boolean CON_Responder(event_t *ev)
// remember typing for several completions (a-la-4dos)
if (!completion[0])
{
if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
if (!input.length || input.length >= 40 || strchr(input.buffer, ' '))
return true;
strcpy(completion, inputlines[inputline]);
strcpy(completion, input.buffer);
skips = 0;
com_skips = 0;
var_skips = 0;
@ -1223,7 +1016,9 @@ boolean CON_Responder(event_t *ev)
if (cmd)
{
CON_InputSetString(va("%s ", cmd));
Lock_state();
M_TextInputSetString(&input, va("%s ", cmd));
Unlock_state();
}
else
{
@ -1246,20 +1041,6 @@ boolean CON_Responder(event_t *ev)
con_scrollup--;
return true;
}
else if (key == KEY_HOME)
{
input_cur = 0;
if (!shiftdown)
input_sel = input_cur;
return true;
}
else if (key == KEY_END)
{
input_cur = input_len;
if (!shiftdown)
input_sel = input_cur;
return true;
}
// At this point we're messing with input
// Clear completion
@ -1268,21 +1049,28 @@ boolean CON_Responder(event_t *ev)
// command enter
if (key == KEY_ENTER)
{
if (!input_len)
if (!input.length)
return true;
// push the command
COM_BufAddText(inputlines[inputline]);
COM_BufAddText(input.buffer);
COM_BufAddText("\n");
CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]);
Lock_state();
// Only add command to history if it differs from previous one
if (strcmp(inputlines[inputline], inputlines[(inputline-1) & 31]))
if (strcmp(input.buffer, inputlines[(inputline-1) & 31]))
{
inputline = (inputline+1) & 31;
M_TextInputInit(&input, inputlines[inputline], CON_MAXPROMPTCHARS);
}
inputhist = inputline;
CON_InputClear();
M_TextInputClear(&input);
Unlock_state();
return true;
}
@ -1300,7 +1088,9 @@ boolean CON_Responder(event_t *ev)
if (inputhist == inputline)
inputhist = (inputline + 1) & 31;
CON_InputSetString(inputlines[inputhist]);
Lock_state();
M_TextInputSetString(&input, inputlines[inputhist]);
Unlock_state();
return true;
}
@ -1314,46 +1104,16 @@ boolean CON_Responder(event_t *ev)
while (inputhist != inputline && !inputlines[inputhist][0]);
// back to currentline
Lock_state();
if (inputhist == inputline)
CON_InputClear();
M_TextInputClear(&input);
else
CON_InputSetString(inputlines[inputhist]);
M_TextInputSetString(&input, inputlines[inputhist]);
Unlock_state();
return true;
}
// allow people to use keypad in console (good for typing IP addresses) - Calum
if (key >= KEY_KEYPAD7 && key <= KEY_KPADDEL)
{
char keypad_translation[] = {'7','8','9','-',
'4','5','6','+',
'1','2','3',
'0','.'};
key = keypad_translation[key - KEY_KEYPAD7];
}
else if (key == KEY_KPADSLASH)
key = '/';
// same capslock code as hu_stuff.c's HU_responder. Check there for details.
if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z'))
{
if (shiftdown ^ capslock)
key = shiftxform[key];
}
else
{
if (shiftdown)
key = shiftxform[key];
}
// enter a char into the command prompt
if (key < 32 || key > 127)
return true;
if (input_sel != input_cur)
CON_InputDelSelection();
CON_InputAddChar(key);
return true;
}
@ -1618,16 +1378,16 @@ static void CON_DrawInput(void)
clen = con_width-13;
if (input_len <= clen)
if (input.length <= clen)
{
c = 0;
clen = input_len;
clen = input.length;
}
else // input line scrolls left if it gets too long
{
clen -= 2; // There will always be some extra truncation -- but where is what we'll find out
if (input_cur <= clen/2)
if (input.cursor <= clen/2)
{
// Close enough to right edge to show all
c = 0;
@ -1638,15 +1398,15 @@ static void CON_DrawInput(void)
{
// Cursor in the middle (or right side) of input
// Move over for the ellipsis
c = input_cur - (clen/2) + 2;
c = input.cursor - (clen/2) + 2;
x += charwidth*2;
lellip = 1;
if (c + clen >= input_len)
if (c + clen >= input.length)
{
// Cursor in the right side of input
// We were too far over, so move back
c = input_len - clen;
c = input.length - clen;
}
else
{
@ -1660,8 +1420,8 @@ static void CON_DrawInput(void)
if (lellip)
{
x -= charwidth*3;
if (input_sel < c)
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
if (input.select < c)
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART);
for (i = 0; i < 3; ++i, x += charwidth)
V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
}
@ -1670,7 +1430,7 @@ static void CON_DrawInput(void)
for (cend = c + clen; c < cend; ++c, x += charwidth)
{
if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c))
if ((input.select > c && input.cursor <= c) || (input.select <= c && input.cursor > c))
{
V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 77 | V_NOSCALESTART);
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, true);
@ -1678,15 +1438,15 @@ static void CON_DrawInput(void)
else
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, true);
if (c == input_cur && con_tick >= 4)
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
if (c == input.cursor && con_tick >= 4)
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_menucaps.value);
}
if (cend == input_cur && con_tick >= 4)
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
if (cend == input.cursor && con_tick >= 4)
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_menucaps.value);
if (rellip)
{
if (input_sel > cend)
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
if (input.select > cend)
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART);
for (i = 0; i < 3; ++i, x += charwidth)
V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
}

View file

@ -104,7 +104,7 @@ extern "C" {
// Special Hashing.
//#define NOMD5
//#define NOFILEHASH
//#define NOVERIFYIWADS
#define NOVERIFYIWADS
// Uncheck this to compile debugging code
//#define RANGECHECK

View file

@ -1824,8 +1824,13 @@ void LUA_Step(void)
{
if (!gL)
return;
lua_settop(gL, 0);
lua_gc(gL, LUA_GCSTEP, 1);
if (lua_gettop(gL) != 0)
{
CONS_Alert(CONS_WARNING, "Eek, there is garbage on lua stack!\n");
lua_settop(gL, 0);
lua_gc(gL, LUA_GCSTEP, 1);
}
}
void LUA_Archive(savebuffer_t *save, boolean network)

View file

@ -30,7 +30,9 @@
#include "hu_stuff.h"
#include "g_game.h"
#include "g_input.h"
#include "v_video.h"
#include "m_argv.h"
#include "m_textinput.h"
// Data.
#include "sounds.h"
@ -142,6 +144,9 @@ const char *quitmsg[NUM_QUITMESSAGES];
boolean fromlevelselect = false;
char menu_text_input_buf[MAXSTRINGLENGTH];
static textinput_t menuinput;
typedef enum
{
LLM_CREATESERVER,
@ -1128,60 +1133,15 @@ static UINT32 M_StringCvarLength(consvar_t *cv)
static boolean M_ChangeStringCvar(INT32 choice)
{
consvar_t *cv = currentMenu->menuitems[itemOn].cvar;
char buf[MAXSTRINGLENGTH];
size_t len = strlen(cv->string);
if (shiftdown && choice >= 32 && choice <= 127)
choice = shiftxform[choice];
switch (choice)
if (M_TextInputHandle(&menuinput, choice))
{
case KEY_BACKSPACE:
if (len > 0)
{
S_StartSound(NULL,sfx_menu1); // Tails
M_Memcpy(buf, cv->string, len);
buf[len-1] = 0;
CV_Set(cv, buf);
}
return true;
case KEY_DEL:
if (cv->string[0])
{
S_StartSound(NULL,sfx_menu1); // Tails
CV_Set(cv, "");
}
return true;
case KEY_LEFTARROW:
case KEY_RIGHTARROW:
// TODO: navigation
return true;
default:
if (cv == &cv_dummyip)
{
// Rudimentary number and period enforcing - also allows letters so hostnames can be used instead
if (!((choice >= '-' && choice <= ':') || (choice >= 'A' && choice <= 'Z') || (choice >= 'a' && choice <= 'z'))
&& !(choice >= KEY_KEYPAD7 && choice <= KEY_KPADDEL && choice != KEY_MINUSPAD && choice != KEY_PLUSPAD)) //numpad too!
break;
S_StartSound(NULL,sfx_menu1); // Tails
CV_Set(cv, menuinput.buffer);
if (choice >= KEY_KEYPAD7)
// sir, have you been golfing tonight?
choice = "789-456+1230."[choice - KEY_KEYPAD7];
}
if (choice < 32 || choice > 127)
break;
if (len < M_StringCvarLength(cv) - 1)
{
S_StartSound(NULL, sfx_menu1); // Tails
M_Memcpy(buf, cv->string, len);
buf[len++] = (char)choice;
buf[len] = 0;
CV_Set(cv, buf);
}
return true;
return true;
}
return false;
}
@ -1282,6 +1242,20 @@ static boolean M_WipeBuffer(INT32 ch, menufunc_f *routine)
// use this when routine being NULL isn't a free pass
static INT32 MR_Dummy(INT32 ch) { return true; }
void M_UpdateTextInputString(void)
{
menuitem_t *item = currentMenu->numitems ? &currentMenu->menuitems[itemOn] : NULL;
if (item && item->cvar && !item->cvar->PossibleValue)
{
// Just in case
memset(menu_text_input_buf, 0, sizeof menu_text_input_buf);
M_TextInputInit(&menuinput, menu_text_input_buf, sizeof menu_text_input_buf);
M_TextInputSetString(&menuinput, item->cvar->string);
CONS_Printf("Set input string to %s\n", item->cvar->string);
}
}
//
// M_Responder
//
@ -2260,7 +2234,24 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
{
// Solid color textbox.
V_DrawFill(x+5, y+5, width*8+6, boxlines*8+6, 159);
//V_DrawFill(x+8, y+8, width*8, boxlines*8, 31);
}
void M_DrawTextInput(INT32 x, INT32 y, textinput_t *input, INT32 flags)
{
V_DrawString(x, y, V_ALLOWLOWERCASE|flags, input->buffer);
// draw text cursor for name
if (input->length && skullAnimCounter < 4) // blink cursor
V_DrawCharacter(x+V_SubStringWidth(input->buffer, input->cursor, V_ALLOWLOWERCASE), y+3, '_'|flags, false);
// draw selection
if (input->select != input->cursor)
{
size_t start = min(input->select, input->cursor);
size_t end = max(input->select, input->cursor);
size_t len = end - start;
INT32 startx = V_SubStringWidth(input->buffer, start, V_ALLOWLOWERCASE);
V_DrawFill(x+startx, y, V_SubStringWidth(input->buffer+start, len, V_ALLOWLOWERCASE), 8, 103|V_TRANSLUCENT|flags);
}
}
// horizontally centered text
@ -2368,10 +2359,8 @@ static void M_DrawRightString(menuitem_t *item, INT16 x, INT16 y, INT32 vflags,
w = M_StringCvarLength(cv);
M_DrawTextBox(x + xofs, y + yofs, w, 1);
V_DrawString(x + xofs + 8, y + yofs + 8, vflags|V_ALLOWLOWERCASE, cv->string);
if (selected && skullAnimCounter < 4) // blink cursor
V_DrawCharacter(x + xofs + 8 + V_StringWidth(cv->string, vflags|V_ALLOWLOWERCASE), y + yofs + 8,
'_' | (vflags & ~(V_FLIP|V_PARAMMASK)), false);
if (menuinput.buffer)
M_DrawTextInput(x + xofs, y + yofs, &menuinput, vflags);
}
else if (item->status & IT_SLIDER)
{
@ -2526,7 +2515,7 @@ static INT32 M_DrawMenuItem(menuitem_t *item, INT16 x, INT16 y, INT32 vflags, bo
fixed_t scale = item->status & IT_SMALL ? FRACUNIT/2 : FRACUNIT;
int font;
INT32 colorflag;
INT32 (*widthfunc)(const char *string, INT32 option);
INT32 (*widthfunc)(const char *string, INT32 length, INT32 option);
if (item->status & IT_SECRET)
{
@ -2609,24 +2598,24 @@ static INT32 M_DrawMenuItem(menuitem_t *item, INT16 x, INT16 y, INT32 vflags, bo
vflags |= V_6WIDTHSPACE; // FALLTHRU
case ITF_THIN:
font = TINY_FONT;
widthfunc = V_ThinStringWidth;
widthfunc = V_ThinSubStringWidth;
break;
case ITF_HEADER:
x -= 16; // FALLTHRU
default:
font = HU_FONT;
widthfunc = V_StringWidth;
widthfunc = V_SubStringWidth;
break;
}
if (item->status & IT_CENTER)
width = widthfunc(string, vflags)/2;
width = widthfunc(string, -1, vflags)/2;
else if (item->status & IT_RIGHT)
width = widthfunc(string, vflags);
width = widthfunc(string, -1, vflags);
if (item->status & IT_STICKER)
M_DrawSticker(x, y + 2, widthfunc(string, vflags), vflags & ~V_FLIP, true);
M_DrawSticker(x, y + 2, widthfunc(string, -1, vflags), vflags & ~V_FLIP, true);
V_DrawStringScaled((x - width)<<FRACBITS, y<<FRACBITS, scale, FRACUNIT, FRACUNIT, (selected ? highlightflags : colorflag)|vflags, font, string);

307
src/m_textinput.c Normal file
View file

@ -0,0 +1,307 @@
#include "m_textinput.h"
#include "i_system.h"
#include "keys.h"
#include "console.h"
static void M_TextInputDel(textinput_t *input, size_t start, size_t end)
{
size_t len;
len = (end - start);
if (end != input->length)
memmove(&input->buffer[start], &input->buffer[end], input->length-end);
memset(&input->buffer[input->length - len], 0, len);
input->length -= len;
if (input->select >= end)
input->select -= len;
else if (input->select > start)
input->select = start;
if (input->cursor >= end)
input->cursor -= len;
else if (input->cursor > start)
input->cursor = start;
}
static void M_TextInputDelSelection(textinput_t *input)
{
size_t start, end;
if (input->cursor > input->select)
{
start = input->select;
end = input->cursor;
}
else
{
start = input->cursor;
end = input->select;
}
M_TextInputDel(input, start, end);
input->select = input->cursor = start;
}
static void M_TextInputAddString(textinput_t *input, const char *c)
{
size_t csize = strlen(c);
if (input->length + csize > input->buffer_size-1)
return;
if (input->cursor != input->length)
memmove(&input->buffer[input->cursor+csize], &input->buffer[input->cursor], input->length-input->cursor);
memcpy(&input->buffer[input->cursor], c, csize);
input->length += csize;
input->select = (input->cursor += csize);
}
static void M_TextInputAddChar(textinput_t *input, char c)
{
if (input->length >= input->buffer_size-1)
return;
if (input->cursor != input->length)
memmove(&input->buffer[input->cursor+1], &input->buffer[input->cursor], input->length-input->cursor);
input->buffer[input->cursor++] = c;
input->buffer[++input->length] = 0;
input->select = input->cursor;
}
static void M_TextInputDelChar(textinput_t *input)
{
if (!input->cursor)
return;
if (input->cursor != input->length)
memmove(&input->buffer[input->cursor-1], &input->buffer[input->cursor], input->length-input->cursor);
input->buffer[--input->length] = 0;
input->select = --input->cursor;
}
static void M_TextInputToWordEnd(textinput_t *input, boolean move_sel)
{
// Skip spaces
while (input->cursor < input->length && isspace(input->buffer[input->cursor]))
++input->cursor;
// Skip word
while (input->cursor < input->length && !isspace(input->buffer[input->cursor]))
++input->cursor;
if (move_sel) input->select = input->cursor;
}
static void M_TextInputToWordBegin(textinput_t *input, boolean move_sel)
{
// Hack, always move back 1 character if possible so if we press ctrl-left at a word beginning
// we move to previous word
if (input->cursor) --input->cursor;
// Skip spaces
while (input->cursor && isspace(input->buffer[input->cursor]))
--input->cursor;
// Skip word
while (input->cursor && !isspace(input->buffer[input->cursor]))
--input->cursor;
// Unless we reached beginning of line, we're pointing at a space before word, so move cursor
// forward to fix that
if (input->cursor) ++input->cursor;
if (move_sel) input->select = input->cursor;
}
void M_TextInputInit(textinput_t *input, char *buffer, size_t buffer_size)
{
input->buffer = buffer;
input->buffer_size = buffer_size;
M_TextInputClear(input);
}
void M_TextInputClear(textinput_t *input)
{
input->cursor = 0;
input->select = 0;
input->length = 0;
}
void M_TextInputSetString(textinput_t *input, const char *c)
{
memset(input->buffer, 0, input->buffer_size);
strcpy(input->buffer, c);
input->cursor = input->select = input->length = strlen(c);
}
boolean M_TextInputHandle(textinput_t *input, INT32 key)
{
if (key == KEY_LSHIFT || key == KEY_RSHIFT
|| key == KEY_LCTRL || key == KEY_RCTRL
|| key == KEY_LALT || key == KEY_RALT)
return false;
//if ((cv_keyboardlayout.value != 3 && ctrldown) || (cv_keyboardlayout.value == 3 && ctrldown && !altdown))
if (ctrldown)
{
if (key == 'x' || key == 'X')
{
if (input->select > input->cursor)
I_ClipboardCopy(&input->buffer[input->cursor], input->select-input->cursor);
else
I_ClipboardCopy(&input->buffer[input->select], input->cursor-input->select);
M_TextInputDelSelection(input);
return true;
}
else if (key == 'c' || key == 'C')
{
if (input->select > input->cursor)
I_ClipboardCopy(&input->buffer[input->cursor], input->select-input->cursor);
else
I_ClipboardCopy(&input->buffer[input->select], input->cursor-input->select);
return true;
}
else if (key == 'v' || key == 'V')
{
const char *paste = I_ClipboardPaste();
if (input->select != input->cursor)
M_TextInputDelSelection(input);
if (paste != NULL)
M_TextInputAddString(input, paste);
return true;
}
else if (key == 'w' || key == 'W')
{
size_t word_start, word_end, i;
word_end = i = input->cursor;
// Unless we're pointing at the beginning of line, decrement i so we only start
// removing symbols that come before the cursor
if (i) --i;
// We might be pointing to spaces, skip them first
while (i && isspace(input->buffer[i]))
--i;
// Now skip the "word"
while (i && !isspace(input->buffer[i]))
--i;
// Unless we reached beginning of line, i is pointing at first space that was found
// before word start, and we don't want to remove it
if (i) ++i;
word_start = i;
if (word_start != word_end)
M_TextInputDel(input, word_start, word_end);
return true;
}
else if (key == KEY_RIGHTARROW)
M_TextInputToWordEnd(input, !shiftdown);
else if (key == KEY_LEFTARROW)
M_TextInputToWordBegin(input, !shiftdown);
// Select all
if (key == 'a' || key == 'A')
{
input->select = 0;
input->cursor = input->length;
return true;
}
// ...why shouldn't it eat the key? if it doesn't, it just means you
// can control Sonic from the console, which is silly
return true;
}
if (key == KEY_LEFTARROW)
{
if (input->cursor != 0)
--input->cursor;
if (!shiftdown)
input->select = input->cursor;
return true;
}
else if (key == KEY_RIGHTARROW)
{
if (input->cursor < input->length)
++input->cursor;
if (!shiftdown)
input->select = input->cursor;
return true;
}
else if (key == KEY_HOME)
{
input->cursor = 0;
if (!shiftdown)
input->select = input->cursor;
return true;
}
else if (key == KEY_END)
{
input->cursor = input->length;
if (!shiftdown)
input->select = input->cursor;
return true;
}
// backspace and delete command prompt
if (input->select != input->cursor)
{
if (key == KEY_BACKSPACE || key == KEY_DEL)
{
M_TextInputDelSelection(input);
return true;
}
}
else if (key == KEY_BACKSPACE)
{
M_TextInputDelChar(input);
return true;
}
else if (key == KEY_DEL)
{
if (input->cursor == input->length)
return true;
++input->cursor;
M_TextInputDelChar(input);
return true;
}
// allow people to use keypad in console (good for typing IP addresses) - Calum
if (key >= KEY_KEYPAD7 && key <= KEY_KPADDEL)
{
char keypad_translation[] = {'7','8','9','-',
'4','5','6','+',
'1','2','3',
'0','.'};
key = keypad_translation[key - KEY_KEYPAD7];
}
else if (key == KEY_KPADSLASH)
key = '/';
// same capslock code as hu_stuff.c's HU_responder. Check there for details.
key = CON_ShiftChar(key);
// enter a char into the command prompt
if (key < 32 || key > 127)
return false;
// add key to cmd line here
if (key >= 'A' && key <= 'Z' && !(shiftdown ^ capslock)) //this is only really necessary for dedicated servers
key = key + 'a' - 'A';
if (input->select != input->cursor)
M_TextInputDelSelection(input);
M_TextInputAddChar(input, key);
return true;
}

23
src/m_textinput.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef __M_TEXTINPUT__
#define __M_TEXTINPUT__
#include "doomtype.h"
typedef struct textinput_s {
size_t cursor;
size_t select;
size_t length;
char *buffer;
size_t buffer_size;
} textinput_t;
void M_TextInputInit(textinput_t *input, char *buffer, size_t buffer_size);
void M_TextInputClear(textinput_t *input);
void M_TextInputSetString(textinput_t *input, const char *c);
boolean M_TextInputHandle(textinput_t *input, INT32 key);
#endif

View file

@ -2952,11 +2952,14 @@ INT32 V_LevelNameHeight(const char *string)
//
// Find string width from hu_font chars
//
INT32 V_StringWidth(const char *string, INT32 option)
INT32 V_SubStringWidth(const char *string, INT32 length, INT32 option)
{
INT32 c, w = 0;
INT32 spacewidth = 4, charwidth = 0;
size_t i;
ssize_t i;
if (length < 0)
length = strlen(string);
switch (option & V_SPACINGMASK)
{
@ -2972,7 +2975,7 @@ INT32 V_StringWidth(const char *string, INT32 option)
break;
}
for (i = 0; i < strlen(string); i++)
for (i = 0; string[i] && i < length; i++)
{
c = string[i];
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
@ -2994,11 +2997,14 @@ INT32 V_StringWidth(const char *string, INT32 option)
//
// Find string width from hu_font chars, 0.5x scale
//
INT32 V_SmallStringWidth(const char *string, INT32 option)
INT32 V_SmallSubStringWidth(const char *string, INT32 length, INT32 option)
{
INT32 c, w = 0;
INT32 spacewidth = 2, charwidth = 0;
size_t i;
ssize_t i;
if (length < 0)
length = strlen(string);
switch (option & V_SPACINGMASK)
{
@ -3014,7 +3020,7 @@ INT32 V_SmallStringWidth(const char *string, INT32 option)
break;
}
for (i = 0; i < strlen(string); i++)
for (i = 0; string[i] && i < length; i++)
{
c = string[i];
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
@ -3033,12 +3039,15 @@ INT32 V_SmallStringWidth(const char *string, INT32 option)
//
// Find string width from tny_font chars
//
INT32 V_ThinStringWidth(const char *string, INT32 option)
INT32 V_ThinSubStringWidth(const char *string, INT32 length, INT32 option)
{
INT32 c, w = 0;
INT32 spacewidth = 2, charwidth = 0;
boolean lowercase = (option & V_ALLOWLOWERCASE);
size_t i;
ssize_t i;
if (length < 0)
length = strlen(string);
switch (option & V_SPACINGMASK)
{
@ -3055,7 +3064,7 @@ INT32 V_ThinStringWidth(const char *string, INT32 option)
break;
}
for (i = 0; i < strlen(string); i++)
for (i = 0; string[i] && i < length; i++)
{
c = string[i];
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09

View file

@ -329,13 +329,14 @@ INT16 V_LevelActNumWidth(UINT8 num); // act number width
INT32 V_CreditStringWidth(const char *string);
// Find string width from hu_font chars
INT32 V_StringWidth(const char *string, INT32 option);
INT32 V_SubStringWidth(const char *string, INT32 length, INT32 option);
#define V_StringWidth(string, option) V_SubStringWidth(string, -1, option)
// Find string width from hu_font chars, 0.5x scale
INT32 V_SmallStringWidth(const char *string, INT32 option);
INT32 V_SmallSubStringWidth(const char *string, INT32 length, INT32 option);
#define V_SmallStringWidth(string, option) V_SmallSubStringWidth(string, -1, option)
// Find string width from tny_font chars
INT32 V_ThinStringWidth(const char *string, INT32 option);
// Find string width from tny_font chars, 0.5x scale
INT32 V_SmallThinStringWidth(const char *string, INT32 option);
INT32 V_ThinSubStringWidth(const char *string, INT32 length, INT32 option);
#define V_ThinStringWidth(string, option) V_ThinSubStringWidth(string, -1, option)
void V_DoPostProcessor(INT32 view, INT32 param);