From 7f6002caba3f0a6749820c2772161caf55b8d267 Mon Sep 17 00:00:00 2001 From: neonloop Date: Fri, 7 May 2021 20:00:12 +0000 Subject: Initial commit (uqm-0.8.0) --- src/libs/input/sdl/input.c | 625 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 625 insertions(+) create mode 100644 src/libs/input/sdl/input.c (limited to 'src/libs/input/sdl/input.c') diff --git a/src/libs/input/sdl/input.c b/src/libs/input/sdl/input.c new file mode 100644 index 0000000..bff17aa --- /dev/null +++ b/src/libs/input/sdl/input.c @@ -0,0 +1,625 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include "input.h" +#include "../inpintrn.h" +#include "libs/threadlib.h" +#include "libs/input/sdl/vcontrol.h" +#include "libs/input/sdl/keynames.h" +#include "libs/memlib.h" +#include "libs/file.h" +#include "libs/log.h" +#include "libs/reslib.h" +#include "options.h" + + +#define KBDBUFSIZE (1 << 8) +static int kbdhead=0, kbdtail=0; +static UniChar kbdbuf[KBDBUFSIZE]; +static UniChar lastchar; +#if SDL_MAJOR_VERSION == 1 +static unsigned int num_keys = 0; +static int *kbdstate = NULL; + // Holds all SDL keys +1 for holding invalid values +#else // Later versions of SDL use the text input API instead +static BOOLEAN set_character_mode = FALSE; + // Records whether the UI thread has caught up with game thread + // on this setting +#endif + +static volatile int *menu_vec; +static int num_menu; +// The last vector element is the character repeat "key" +// This is only used in SDL1 input but it's mostly harmless everywhere else +#define KEY_MENU_ANY (num_menu - 1) +static volatile int *flight_vec; +static int num_templ; +static int num_flight; + +static BOOLEAN InputInitialized = FALSE; + +static BOOLEAN in_character_mode = FALSE; + +static const char *menu_res_names[] = { + "pause", + "exit", + "abort", + "debug", + "fullscreen", + "up", + "down", + "left", + "right", + "select", + "cancel", + "special", + "pageup", + "pagedown", + "home", + "end", + "zoomin", + "zoomout", + "delete", + "backspace", + "editcancel", + "search", + "next", + NULL +}; + +static const char *flight_res_names[] = { + "up", + "down", + "left", + "right", + "weapon", + "special", + "escape", + NULL +}; + +static void +register_menu_controls (int index) +{ + int i; + char buf[40]; + buf[39] = '\0'; + + i = 1; + while (TRUE) + { + VCONTROL_GESTURE g; + snprintf (buf, 39, "menu.%s.%d", menu_res_names[index], i); + if (!res_IsString (buf)) + break; + VControl_ParseGesture (&g, res_GetString (buf)); + VControl_AddGestureBinding (&g, (int *)&menu_vec[index]); + i++; + } +} + + +static VCONTROL_GESTURE *controls; +#define CONTROL_PTR(i, j, k) \ + (controls + ((i) * num_flight + (j)) * MAX_FLIGHT_ALTERNATES + (k)) + +static void +register_flight_controls (void) +{ + int i, j, k; + char buf[40]; + + buf[39] = '\0'; + + for (i = 0; i < num_templ; i++) + { + /* Copy in name */ + snprintf (buf, 39, "keys.%d.name", i+1); + if (res_IsString (buf)) + { + strncpy (input_templates[i].name, res_GetString (buf), 29); + input_templates[i].name[29] = '\0'; + } + else + { + input_templates[i].name[0] = '\0'; + } + for (j = 0; j < num_flight; j++) + { + for (k = 0; k < MAX_FLIGHT_ALTERNATES; k++) + { + VCONTROL_GESTURE *g = CONTROL_PTR(i, j, k); + snprintf (buf, 39, "keys.%d.%s.%d", i+1, flight_res_names[j], k+1); + if (!res_IsString (buf)) + { + g->type = VCONTROL_NONE; + continue; + } + VControl_ParseGesture (g, res_GetString (buf)); + VControl_AddGestureBinding (g, (int *)(flight_vec + i * num_flight + j)); + } + } + } +} + +static void +initKeyConfig (void) +{ + int i; + + if (!menu_vec || !flight_vec) + { + log_add (log_Fatal, "initKeyConfig(): invalid input vectors"); + exit (EXIT_FAILURE); + } + + controls = HCalloc (sizeof (*controls) * num_templ * num_flight + * MAX_FLIGHT_ALTERNATES); + + /* First, load in the menu keys */ + LoadResourceIndex (contentDir, "menu.key", "menu."); + LoadResourceIndex (configDir, "override.cfg", "menu."); + for (i = 0; i < num_menu; i++) + { + if (!menu_res_names[i]) + break; + register_menu_controls (i); + } + + LoadResourceIndex (configDir, "flight.cfg", "keys."); + if (!res_HasKey ("keys.1.name")) + { + /* Either flight.cfg doesn't exist, or we're using an old version + of flight.cfg, and thus we wound up loading untyped values into + 'keys.keys.1.name' and such. Load the defaults from the content + directory. */ + LoadResourceIndex (contentDir, "uqm.key", "keys."); + } + + register_flight_controls (); + + return; +} + +static void +resetKeyboardState (void) +{ +#if SDL_MAJOR_VERSION == 1 + memset (kbdstate, 0, sizeof (int) * num_keys); + menu_vec[KEY_MENU_ANY] = 0; +#endif +} + +void +TFB_SetInputVectors (volatile int menu[], int num_menu_, volatile int flight[], + int num_templ_, int num_flight_) +{ + if (num_menu_ < 0 || num_templ_ < 0 || num_flight_ < 0) + { + log_add (log_Fatal, "TFB_SetInputVectors(): invalid vector size"); + exit (EXIT_FAILURE); + } + menu_vec = menu; + num_menu = num_menu_; + flight_vec = flight; + num_templ = num_templ_; + num_flight = num_flight_; +} + +#ifdef HAVE_JOYSTICK + +static void +initJoystick (void) +{ + int nJoysticks; + + if ((SDL_InitSubSystem(SDL_INIT_JOYSTICK)) == -1) + { + log_add (log_Fatal, "Couldn't initialize joystick subsystem: %s", + SDL_GetError()); + exit (EXIT_FAILURE); + } + + log_add (log_Info, "%i joysticks were found.", SDL_NumJoysticks ()); + + nJoysticks = SDL_NumJoysticks (); + if (nJoysticks > 0) + { + int i; + + log_add (log_Info, "The names of the joysticks are:"); + for (i = 0; i < nJoysticks; i++) + { + log_add (log_Info, " %s", +#if SDL_MAJOR_VERSION == 1 + SDL_JoystickName (i)); +#else + SDL_JoystickNameForIndex (i)); +#endif + } + SDL_JoystickEventState (SDL_ENABLE); + } +} + +#endif /* HAVE_JOYSTICK */ + +int +TFB_InitInput (int driver, int flags) +{ + (void)driver; + (void)flags; + +#if SDL_MAJOR_VERSION == 1 + SDL_EnableUNICODE(1); + (void)SDL_GetKeyState (&num_keys); + kbdstate = (int *)HMalloc (sizeof (int) * (num_keys + 1)); +#endif + +#ifdef HAVE_JOYSTICK + initJoystick (); +#endif + + in_character_mode = FALSE; + resetKeyboardState (); + + /* Prepare the Virtual Controller system. */ + VControl_Init (); + + initKeyConfig (); + + VControl_ResetInput (); + InputInitialized = TRUE; + + return 0; +} + +void +TFB_UninitInput (void) +{ + VControl_Uninit (); + HFree (controls); +#if SDL_MAJOR_VERSION == 1 + HFree (kbdstate); +#endif +} + +void +EnterCharacterMode (void) +{ + kbdhead = kbdtail = 0; + lastchar = 0; + in_character_mode = TRUE; + VControl_ResetInput (); +} + +void +ExitCharacterMode (void) +{ + VControl_ResetInput (); + in_character_mode = FALSE; + kbdhead = kbdtail = 0; + lastchar = 0; +} + +UniChar +GetNextCharacter (void) +{ + UniChar result; + if (kbdhead == kbdtail) + return 0; + result = kbdbuf[kbdhead]; + kbdhead = (kbdhead + 1) & (KBDBUFSIZE - 1); + return result; +} + +UniChar +GetLastCharacter (void) +{ + return lastchar; +} + +volatile int MouseButtonDown = 0; + +static void +ProcessMouseEvent (const SDL_Event *e) +{ + switch (e->type) + { + case SDL_MOUSEBUTTONDOWN: + MouseButtonDown = 1; + break; + case SDL_MOUSEBUTTONUP: + MouseButtonDown = 0; + break; + default: + break; + } +} + +#if SDL_MAJOR_VERSION == 1 + +static inline int +is_numpad_char_event (const SDL_Event *Event) +{ + return in_character_mode && + (Event->type == SDL_KEYDOWN || Event->type == SDL_KEYUP) && + (Event->key.keysym.mod & KMOD_NUM) && /* NumLock is ON */ + Event->key.keysym.unicode > 0 && /* Printable char */ + Event->key.keysym.sym >= SDLK_KP0 && /* Keypad key */ + Event->key.keysym.sym <= SDLK_KP_PLUS; +} + +void +ProcessInputEvent (const SDL_Event *Event) +{ + if (!InputInitialized) + return; + + ProcessMouseEvent (Event); + + // In character mode with NumLock on, numpad chars bypass VControl + // so that menu arrow events are not produced + if (!is_numpad_char_event (Event)) + VControl_HandleEvent (Event); + + if (Event->type == SDL_KEYDOWN || Event->type == SDL_KEYUP) + { // process character input event, if any + // keysym.sym is an SDLKey type which is an enum and can be signed + // or unsigned on different platforms; we'll use a guaranteed type + int k = Event->key.keysym.sym; + UniChar map_key = Event->key.keysym.unicode; + + if (k < 0 || k > num_keys) + k = num_keys; // for unknown keys + + if (Event->type == SDL_KEYDOWN) + { + int newtail; + + // dont care about the non-printable, non-char + if (!map_key) + return; + + kbdstate[k]++; + + newtail = (kbdtail + 1) & (KBDBUFSIZE - 1); + // ignore the char if the buffer is full + if (newtail != kbdhead) + { + kbdbuf[kbdtail] = map_key; + kbdtail = newtail; + lastchar = map_key; + menu_vec[KEY_MENU_ANY]++; + } + } + else if (Event->type == SDL_KEYUP) + { + if (kbdstate[k] == 0) + { // something is fishy -- better to reset the + // repeatable state to avoid big problems + menu_vec[KEY_MENU_ANY] = 0; + } + else + { + kbdstate[k]--; + if (menu_vec[KEY_MENU_ANY] > 0) + menu_vec[KEY_MENU_ANY]--; + } + } + } +} +#else +void +ProcessInputEvent (const SDL_Event *Event) +{ + if (!InputInitialized) + return; + + ProcessMouseEvent (Event); + + if (in_character_mode && !set_character_mode) + { + set_character_mode = TRUE; + SDL_StartTextInput (); + } + + if (!in_character_mode && set_character_mode) + { + set_character_mode = FALSE; + SDL_StopTextInput (); + } + + /* TODO: Block numpad input when NUM_LOCK is on */ + VControl_HandleEvent (Event); + + if (Event->type == SDL_TEXTINPUT) + { + int newtail; + int i = 0; + + while (Event->text.text[i]) + { + UniChar map_key = Event->text.text[i++]; + + /* Decode any UTF-8 keys */ + if (map_key >= 0xC0 && map_key < 0xE0) + { + /* 2-byte UTF-8 */ + map_key = (map_key & 0x1f) << 6; + map_key |= Event->text.text[i++] & 0x3f; + } + else if (map_key >= 0xE0 && map_key < 0xF0) + { + /* 3-byte UTF-8 */ + map_key = (map_key & 0x0f) << 6; + map_key |= Event->text.text[i++] & 0x3f; + map_key <<= 6; + map_key |= Event->text.text[i++] & 0x3f; + } + else if (map_key >= 0xF0) + { + /* Out of the BMP, won't fit in a UniChar */ + /* Use the replacement character instead */ + map_key = 0xFFFD; + while ((UniChar)Event->text.text[i] > 0x7F) + { + ++i; + } + } + + /* dont care about the non-printable, non-char */ + if (!map_key) + return; + + newtail = (kbdtail + 1) & (KBDBUFSIZE - 1); + + /* ignore the char if the buffer is full */ + if (newtail != kbdhead) + { + kbdbuf[kbdtail] = map_key; + kbdtail = newtail; + lastchar = map_key; + } + + /* Loop back in case there are more chars in the + * text input buffer */ + } + } +} + +#endif + +void +TFB_ResetControls (void) +{ + VControl_ResetInput (); + resetKeyboardState (); + // flush character buffer + kbdhead = kbdtail = 0; + lastchar = 0; +} + +void +InterrogateInputState (int templat, int control, int index, char *buffer, int maxlen) +{ + VCONTROL_GESTURE *g = CONTROL_PTR(templat, control, index); + + if (templat >= num_templ || control >= num_flight + || index >= MAX_FLIGHT_ALTERNATES) + { + log_add (log_Warning, "InterrogateInputState(): invalid control index"); + buffer[0] = 0; + return; + } + + switch (g->type) + { + case VCONTROL_KEY: + snprintf (buffer, maxlen, "%s", VControl_code2name (g->gesture.key)); + buffer[maxlen-1] = 0; + break; + case VCONTROL_JOYBUTTON: + snprintf (buffer, maxlen, "[J%d B%d]", g->gesture.button.port, g->gesture.button.index + 1); + buffer[maxlen-1] = 0; + break; + case VCONTROL_JOYAXIS: + snprintf (buffer, maxlen, "[J%d A%d %c]", g->gesture.axis.port, g->gesture.axis.index, g->gesture.axis.polarity > 0 ? '+' : '-'); + break; + case VCONTROL_JOYHAT: + snprintf (buffer, maxlen, "[J%d H%d %d]", g->gesture.hat.port, g->gesture.hat.index, g->gesture.hat.dir); + break; + default: + /* Something we don't handle yet */ + buffer[0] = 0; + break; + } + return; +} + +void +RemoveInputState (int templat, int control, int index) +{ + VCONTROL_GESTURE *g = CONTROL_PTR(templat, control, index); + char keybuf[40]; + keybuf[39] = '\0'; + + if (templat >= num_templ || control >= num_flight + || index >= MAX_FLIGHT_ALTERNATES) + { + log_add (log_Warning, "RemoveInputState(): invalid control index"); + return; + } + + VControl_RemoveGestureBinding (g, + (int *)(flight_vec + templat * num_flight + control)); + g->type = VCONTROL_NONE; + + snprintf (keybuf, 39, "keys.%d.%s.%d", templat+1, flight_res_names[control], index+1); + res_Remove (keybuf); + + return; +} + +void +RebindInputState (int templat, int control, int index) +{ + VCONTROL_GESTURE g; + char keybuf[40], valbuf[40]; + keybuf[39] = valbuf[39] = '\0'; + + if (templat >= num_templ || control >= num_flight + || index >= MAX_FLIGHT_ALTERNATES) + { + log_add (log_Warning, "RebindInputState(): invalid control index"); + return; + } + + /* Remove the old binding on this spot */ + RemoveInputState (templat, control, index); + + /* Wait for the next interesting bit of user input */ + VControl_ClearGesture (); + while (!VControl_GetLastGesture (&g)) + { + TaskSwitch (); + } + + /* And now, add the new binding. */ + VControl_AddGestureBinding (&g, + (int *)(flight_vec + templat * num_flight + control)); + *CONTROL_PTR(templat, control, index) = g; + snprintf (keybuf, 39, "keys.%d.%s.%d", templat+1, flight_res_names[control], index+1); + VControl_DumpGesture (valbuf, 39, &g); + res_PutString (keybuf, valbuf); +} + +void +SaveKeyConfiguration (uio_DirHandle *path, const char *fname) +{ + SaveResourceIndex (path, fname, "keys.", TRUE); +} + +void +BeginInputFrame (void) +{ + VControl_BeginFrame (); +} + -- cgit v1.2.3