mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-10-19 03:32:41 +00:00
Added text-input system for typing in-game
This commit is contained in:
parent
b7a1ec009c
commit
96a2cacf2d
7 changed files with 218 additions and 7 deletions
|
@ -10,6 +10,16 @@
|
|||
#include "../configfile.h"
|
||||
#include "controller_keyboard.h"
|
||||
|
||||
#include "pc/gfx/gfx_window_manager_api.h"
|
||||
#include "pc/pc_main.h""
|
||||
#include "engine/math_util.h"
|
||||
|
||||
#define SCANCODE_BACKSPACE 0x0E
|
||||
#define SCANCODE_ESCAPE 0x01
|
||||
#define SCANCODE_ENTER 0x1C
|
||||
#define SCANCODE_V 0x2F
|
||||
#define SCANCODE_INSERT 0x152
|
||||
|
||||
static int keyboard_buttons_down;
|
||||
|
||||
#define MAX_KEYBINDS 64
|
||||
|
@ -18,6 +28,14 @@ static int num_keybinds = 0;
|
|||
|
||||
static u32 keyboard_lastkey = VK_INVALID;
|
||||
|
||||
char textInput[MAX_TEXT_INPUT];
|
||||
static bool inTextInput = false;
|
||||
|
||||
u8 held_ctrl, held_shift, held_alt;
|
||||
static enum TextInputMode textInputMode;
|
||||
void (*textInputOnEscape)(void) = NULL;
|
||||
void (*textInputOnEnter)(void) = NULL;
|
||||
|
||||
static int keyboard_map_scancode(int scancode) {
|
||||
int ret = 0;
|
||||
for (int i = 0; i < num_keybinds; i++) {
|
||||
|
@ -28,7 +46,56 @@ static int keyboard_map_scancode(int scancode) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void keyboard_alter_text_input_modifier(int scancode, bool down) {
|
||||
if (down) {
|
||||
switch (scancode) {
|
||||
case 0x1D: held_ctrl |= (1 << 0); break;
|
||||
case 0x11D: held_ctrl |= (1 << 1); break;
|
||||
case 0x2A: held_shift |= (1 << 0); break;
|
||||
case 0x36: held_shift |= (1 << 1); break;
|
||||
case 0x38: held_alt |= (1 << 0); break;
|
||||
case 0x138: held_alt |= (1 << 1); break;
|
||||
}
|
||||
} else {
|
||||
switch (scancode) {
|
||||
case 0x1D: held_ctrl &= ~(1 << 0); break;
|
||||
case 0x11D: held_ctrl &= ~(1 << 1); break;
|
||||
case 0x2A: held_shift &= ~(1 << 0); break;
|
||||
case 0x36: held_shift &= ~(1 << 1); break;
|
||||
case 0x38: held_alt &= ~(1 << 0); break;
|
||||
case 0x138: held_alt &= ~(1 << 1); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool keyboard_on_key_down(int scancode) {
|
||||
if (inTextInput) {
|
||||
// alter the held value of modifier keys
|
||||
keyboard_alter_text_input_modifier(scancode, true);
|
||||
|
||||
// perform text-input-specific actions
|
||||
switch (scancode) {
|
||||
case SCANCODE_BACKSPACE:
|
||||
textInput[max(strlen(textInput) - 1, 0)] = '\0';
|
||||
break;
|
||||
case SCANCODE_ESCAPE:
|
||||
if (textInputOnEscape != NULL) { textInputOnEscape(); }
|
||||
break;
|
||||
case SCANCODE_ENTER:
|
||||
if (textInputOnEnter != NULL) { textInputOnEnter(); }
|
||||
break;
|
||||
case SCANCODE_V:
|
||||
if (held_ctrl) { keyboard_on_text_input(wm_api->get_clipboard_text()); }
|
||||
break;
|
||||
case SCANCODE_INSERT:
|
||||
if (held_shift) { keyboard_on_text_input(wm_api->get_clipboard_text()); }
|
||||
break;
|
||||
}
|
||||
|
||||
// ignore any normal key down event if we're in text-input mode
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int mapped = keyboard_map_scancode(scancode);
|
||||
keyboard_buttons_down |= mapped;
|
||||
keyboard_lastkey = scancode;
|
||||
|
@ -36,6 +103,14 @@ bool keyboard_on_key_down(int scancode) {
|
|||
}
|
||||
|
||||
bool keyboard_on_key_up(int scancode) {
|
||||
if (inTextInput) {
|
||||
// alter the held value of modifier keys
|
||||
keyboard_alter_text_input_modifier(scancode, false);
|
||||
|
||||
// ignore any key up event if we're in text-input mode
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int mapped = keyboard_map_scancode(scancode);
|
||||
keyboard_buttons_down &= ~mapped;
|
||||
if (keyboard_lastkey == (u32) scancode)
|
||||
|
@ -47,6 +122,79 @@ void keyboard_on_all_keys_up(void) {
|
|||
keyboard_buttons_down = 0;
|
||||
}
|
||||
|
||||
char* keyboard_start_text_input(enum TextInputMode inInputMode, void (*onEscape)(void), void (*onEnter)(void)) {
|
||||
// set text-input events
|
||||
textInputOnEscape = onEscape;
|
||||
textInputOnEnter = onEnter;
|
||||
|
||||
// clear buffer
|
||||
for (int i = 0; i < MAX_TEXT_INPUT; i++) { textInput[i] = '\0'; }
|
||||
|
||||
// clear held-value for modifiers
|
||||
held_ctrl = 0;
|
||||
held_shift = 0;
|
||||
held_alt = 0;
|
||||
|
||||
// start allowing text input
|
||||
wm_api->start_text_input();
|
||||
textInputMode = inInputMode;
|
||||
inTextInput = true;
|
||||
}
|
||||
|
||||
void keyboard_stop_text_input(void) {
|
||||
// stop allowing text input
|
||||
wm_api->stop_text_input();
|
||||
inTextInput = false;
|
||||
}
|
||||
|
||||
static bool keyboard_allow_character_input(char c) {
|
||||
switch (textInputMode) {
|
||||
case TIM_IP:
|
||||
// IP only allows numbers, periods, and spaces
|
||||
return (c >= '0' && c <= '9')
|
||||
|| (c == '.')
|
||||
|| (c == ' ');
|
||||
|
||||
case TIM_MULTI_LINE:
|
||||
// multi-line allows new-line character
|
||||
if (c == '\n') { return true; }
|
||||
// intentional fall-through
|
||||
|
||||
case TIM_SINGLE_LINE:
|
||||
// allow all characters that we can display in-game
|
||||
return (c >= '0' && c <= '9')
|
||||
|| (c >= 'a' && c <= 'z')
|
||||
|| (c >= 'A' && c <= 'Z')
|
||||
|| (c == '\'') || (c == '.')
|
||||
|| (c == ',') || (c == '-')
|
||||
|| (c == '(') || (c == ')')
|
||||
|| (c == '&') || (c == '!')
|
||||
|| (c == '%') || (c == '?')
|
||||
|| (c == '"') || (c == '~')
|
||||
|| (c == '*') || (c == ' ');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void keyboard_on_text_input(char* text) {
|
||||
// sanity check input
|
||||
if (text == NULL) { return; }
|
||||
|
||||
int i = strlen(textInput);
|
||||
while (*text != NULL) {
|
||||
// make sure we don't overrun the buffer
|
||||
if (i >= MAX_TEXT_INPUT) { break; }
|
||||
|
||||
// copy over character if we're allowed to input it
|
||||
if (keyboard_allow_character_input(*text)) {
|
||||
textInput[i++] = *text;
|
||||
}
|
||||
|
||||
text++;
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard_add_binds(int mask, unsigned int *scancode) {
|
||||
for (int i = 0; i < MAX_BINDS && num_keybinds < MAX_KEYBINDS; ++i) {
|
||||
if (scancode[i] < VK_BASE_KEYBOARD + VK_SIZE) {
|
||||
|
|
|
@ -9,9 +9,23 @@
|
|||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_TEXT_INPUT 256
|
||||
extern char textInput[];
|
||||
|
||||
enum TextInputMode {
|
||||
TIM_IP,
|
||||
TIM_MULTI_LINE,
|
||||
TIM_SINGLE_LINE,
|
||||
};
|
||||
|
||||
bool keyboard_on_key_down(int scancode);
|
||||
bool keyboard_on_key_up(int scancode);
|
||||
void keyboard_on_all_keys_up(void);
|
||||
void keyboard_on_text_input(char* text);
|
||||
char* keyboard_start_text_input(enum TextInputMode, void (*onEscape)(void), void (*onEnter)(void));
|
||||
void keyboard_stop_text_input(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
|
||||
using namespace Microsoft::WRL; // For ComPtr
|
||||
|
||||
static bool inTextInput = false;
|
||||
|
||||
static struct {
|
||||
HWND h_wnd;
|
||||
bool showing_error;
|
||||
|
@ -74,6 +76,7 @@ static struct {
|
|||
bool (*on_key_down)(int scancode);
|
||||
bool (*on_key_up)(int scancode);
|
||||
void (*on_all_keys_up)(void);
|
||||
void (*on_text_input)(char*);
|
||||
} dxgi;
|
||||
|
||||
static void load_dxgi_library(void) {
|
||||
|
@ -225,6 +228,19 @@ static void gfx_dxgi_on_resize(void) {
|
|||
|
||||
static void onkeydown(WPARAM w_param, LPARAM l_param) {
|
||||
int key = ((l_param >> 16) & 0x1ff);
|
||||
if (inTextInput) {
|
||||
const int keyboardScanCode = (l_param >> 16) & 0x00ff;
|
||||
const int virtualKey = w_param;
|
||||
|
||||
BYTE keyboardState[256];
|
||||
GetKeyboardState(keyboardState);
|
||||
|
||||
WORD ascii = 0;
|
||||
const int len = ToAscii(virtualKey, keyboardScanCode, keyboardState, &ascii, 0);
|
||||
if (len > 0) {
|
||||
dxgi.on_text_input((char*)&ascii);
|
||||
}
|
||||
}
|
||||
if (dxgi.on_key_down != nullptr) {
|
||||
dxgi.on_key_down(key);
|
||||
}
|
||||
|
@ -345,10 +361,11 @@ static void gfx_dxgi_init(const char *window_title) {
|
|||
update_screen_settings();
|
||||
}
|
||||
|
||||
static void gfx_dxgi_set_keyboard_callbacks(bool (*on_key_down)(int scancode), bool (*on_key_up)(int scancode), void (*on_all_keys_up)(void)) {
|
||||
static void gfx_dxgi_set_keyboard_callbacks(bool (*on_key_down)(int scancode), bool (*on_key_up)(int scancode), void (*on_all_keys_up)(void), void (*on_text_input)(char*)) {
|
||||
dxgi.on_key_down = on_key_down;
|
||||
dxgi.on_key_up = on_key_up;
|
||||
dxgi.on_all_keys_up = on_all_keys_up;
|
||||
dxgi.on_text_input = on_text_input;
|
||||
}
|
||||
|
||||
static void gfx_dxgi_main_loop(void (*run_one_game_iter)(void)) {
|
||||
|
@ -608,6 +625,18 @@ HWND gfx_dxgi_get_h_wnd(void) {
|
|||
void gfx_dxgi_shutdown(void) {
|
||||
}
|
||||
|
||||
void gfx_dxgi_start_text_input(void) { inTextInput = TRUE; }
|
||||
void gfx_dxgi_stop_text_input(void) { inTextInput = FALSE; }
|
||||
|
||||
static char* gfx_dxgi_get_clipboard_text(void) {
|
||||
if (OpenClipboard(NULL)) {
|
||||
HANDLE clip = GetClipboardData(CF_TEXT);
|
||||
CloseClipboard();
|
||||
return (char*)clip;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ThrowIfFailed(HRESULT res) {
|
||||
if (FAILED(res)) {
|
||||
fprintf(stderr, "Error: 0x%08X\n", res);
|
||||
|
@ -636,6 +665,9 @@ struct GfxWindowManagerAPI gfx_dxgi = {
|
|||
gfx_dxgi_swap_buffers_end,
|
||||
gfx_dxgi_get_time,
|
||||
gfx_dxgi_shutdown,
|
||||
gfx_dxgi_start_text_input,
|
||||
gfx_dxgi_stop_text_input,
|
||||
gfx_dxgi_get_clipboard_text,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -49,6 +49,7 @@ static int inverted_scancode_table[512];
|
|||
static kb_callback_t kb_key_down = NULL;
|
||||
static kb_callback_t kb_key_up = NULL;
|
||||
static void (*kb_all_keys_up)(void) = NULL;
|
||||
static void (*kb_text_input)(char*) = NULL;
|
||||
|
||||
// whether to use timer for frame control
|
||||
static bool use_timer = true;
|
||||
|
@ -279,11 +280,15 @@ static void gfx_sdl_onkeyup(int scancode) {
|
|||
}
|
||||
|
||||
static void gfx_sdl_handle_events(void) {
|
||||
SDL_StartTextInput();
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
#ifndef TARGET_WEB
|
||||
// Scancodes are broken in Emscripten SDL2: https://bugzilla.libsdl.org/show_bug.cgi?id=3259
|
||||
case SDL_TEXTINPUT:
|
||||
kb_text_input(event.text.text);
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
gfx_sdl_onkeydown(event.key.keysym.scancode);
|
||||
break;
|
||||
|
@ -320,10 +325,11 @@ static void gfx_sdl_handle_events(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void)) {
|
||||
static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void), void (*on_text_input)(char*)) {
|
||||
kb_key_down = on_key_down;
|
||||
kb_key_up = on_key_up;
|
||||
kb_all_keys_up = on_all_keys_up;
|
||||
kb_text_input = on_text_input;
|
||||
}
|
||||
|
||||
static bool gfx_sdl_start_frame(void) {
|
||||
|
@ -361,6 +367,10 @@ static void gfx_sdl_shutdown(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static void gfx_sdl_start_text_input(void) { SDL_StartTextInput(); }
|
||||
static void gfx_sdl_stop_text_input(void) { SDL_StopTextInput(); }
|
||||
static char* gfx_sdl_get_clipboard_text(void) { SDL_GetClipboardText(); }
|
||||
|
||||
struct GfxWindowManagerAPI gfx_sdl = {
|
||||
gfx_sdl_init,
|
||||
gfx_sdl_set_keyboard_callbacks,
|
||||
|
@ -371,7 +381,10 @@ struct GfxWindowManagerAPI gfx_sdl = {
|
|||
gfx_sdl_swap_buffers_begin,
|
||||
gfx_sdl_swap_buffers_end,
|
||||
gfx_sdl_get_time,
|
||||
gfx_sdl_shutdown
|
||||
gfx_sdl_shutdown,
|
||||
gfx_sdl_start_text_input,
|
||||
gfx_sdl_stop_text_input,
|
||||
gfx_sdl_get_clipboard_text,
|
||||
};
|
||||
|
||||
#endif // BACKEND_WM
|
||||
|
|
|
@ -11,7 +11,7 @@ typedef bool (*kb_callback_t)(int code);
|
|||
|
||||
struct GfxWindowManagerAPI {
|
||||
void (*init)(const char *window_title);
|
||||
void (*set_keyboard_callbacks)(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void));
|
||||
void (*set_keyboard_callbacks)(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void), void (*on_text_input)(char*));
|
||||
void (*main_loop)(void (*run_one_game_iter)(void));
|
||||
void (*get_dimensions)(uint32_t *width, uint32_t *height);
|
||||
void (*handle_events)(void);
|
||||
|
@ -20,6 +20,9 @@ struct GfxWindowManagerAPI {
|
|||
void (*swap_buffers_end)(void);
|
||||
double (*get_time)(void); // For debug
|
||||
void (*shutdown)(void);
|
||||
void (*start_text_input)(void);
|
||||
void (*stop_text_input)(void);
|
||||
char* (*get_clipboard_text)(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,7 +55,7 @@ struct RumbleData gRumbleDataQueue[3];
|
|||
struct StructSH8031D9B0 gCurrRumbleSettings;
|
||||
|
||||
static struct AudioAPI *audio_api;
|
||||
static struct GfxWindowManagerAPI *wm_api;
|
||||
struct GfxWindowManagerAPI *wm_api;
|
||||
static struct GfxRenderingAPI *rendering_api;
|
||||
|
||||
extern void gfx_run(Gfx *commands);
|
||||
|
@ -241,14 +241,14 @@ void main_func(void) {
|
|||
#endif
|
||||
|
||||
char window_title[96] =
|
||||
"Super Mario 64 EX (" RAPI_NAME ")"
|
||||
"Super Mario 64 coop EX (" RAPI_NAME ")"
|
||||
#ifdef NIGHTLY
|
||||
" nightly " GIT_HASH
|
||||
#endif
|
||||
;
|
||||
|
||||
gfx_init(wm_api, rendering_api, window_title);
|
||||
wm_api->set_keyboard_callbacks(keyboard_on_key_down, keyboard_on_key_up, keyboard_on_all_keys_up);
|
||||
wm_api->set_keyboard_callbacks(keyboard_on_key_down, keyboard_on_key_up, keyboard_on_all_keys_up, keyboard_on_text_input);
|
||||
|
||||
if (audio_api == NULL && audio_sdl.init())
|
||||
audio_api = &audio_sdl;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern struct GfxWindowManagerAPI* wm_api;
|
||||
void game_deinit(void);
|
||||
void game_exit(void);
|
||||
|
||||
|
|
Loading…
Reference in a new issue