diff options
Diffstat (limited to 'src/libs/input/sdl')
-rw-r--r-- | src/libs/input/sdl/Makeinfo | 2 | ||||
-rw-r--r-- | src/libs/input/sdl/input.c | 625 | ||||
-rw-r--r-- | src/libs/input/sdl/input.h | 27 | ||||
-rw-r--r-- | src/libs/input/sdl/keynames.c | 229 | ||||
-rw-r--r-- | src/libs/input/sdl/keynames.h | 22 | ||||
-rw-r--r-- | src/libs/input/sdl/vcontrol.c | 1300 | ||||
-rw-r--r-- | src/libs/input/sdl/vcontrol.h | 108 |
7 files changed, 2313 insertions, 0 deletions
diff --git a/src/libs/input/sdl/Makeinfo b/src/libs/input/sdl/Makeinfo new file mode 100644 index 0000000..427da53 --- /dev/null +++ b/src/libs/input/sdl/Makeinfo @@ -0,0 +1,2 @@ +uqm_CFILES="input.c keynames.c vcontrol.c" +uqm_HFILES="input.h keynames.h vcontrol.h" 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 <assert.h> +#include <errno.h> +#include <string.h> +#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 (); +} + diff --git a/src/libs/input/sdl/input.h b/src/libs/input/sdl/input.h new file mode 100644 index 0000000..3c599aa --- /dev/null +++ b/src/libs/input/sdl/input.h @@ -0,0 +1,27 @@ +//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. + */ + +#ifndef INPUT_H +#define INPUT_H + +#include "port.h" +#include SDL_INCLUDE(SDL.h) + +extern void ProcessInputEvent (const SDL_Event *Event); + +#endif diff --git a/src/libs/input/sdl/keynames.c b/src/libs/input/sdl/keynames.c new file mode 100644 index 0000000..86c104a --- /dev/null +++ b/src/libs/input/sdl/keynames.c @@ -0,0 +1,229 @@ +/* + * 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 "port.h" +#include SDL_INCLUDE(SDL.h) +#include <string.h> +#include "keynames.h" + +/* This code is adapted from the code in SDL_keysym.h. Though this + * would almost certainly be fast if we were to use a direct char * + * array, this technique permits us to be independent of the actual + * character encoding to keysyms. */ + +/* These names are case-insensitive when compared, but we format + * them to look pretty when output */ + +/* This version of Virtual Controller does not support SDLK_WORLD_* + * keysyms or the Num/Caps/ScrollLock keys. SDL treats locking keys + * specially, and we cannot treat them as normal keys. Pain, + * tragedy. */ + +typedef struct vcontrol_keyname { + const char *name; + int code; +} keyname; + +static keyname keynames[] = { + {"Backspace", SDLK_BACKSPACE}, + {"Tab", SDLK_TAB}, + {"Clear", SDLK_CLEAR}, + {"Return", SDLK_RETURN}, + {"Pause", SDLK_PAUSE}, + {"Escape", SDLK_ESCAPE}, + {"Space", SDLK_SPACE}, + {"!", SDLK_EXCLAIM}, + {"\"", SDLK_QUOTEDBL}, + {"Hash", SDLK_HASH}, + {"$", SDLK_DOLLAR}, + {"&", SDLK_AMPERSAND}, + {"'", SDLK_QUOTE}, + {"(", SDLK_LEFTPAREN}, + {")", SDLK_RIGHTPAREN}, + {"*", SDLK_ASTERISK}, + {"+", SDLK_PLUS}, + {",", SDLK_COMMA}, + {"-", SDLK_MINUS}, + {".", SDLK_PERIOD}, + {"/", SDLK_SLASH}, + {"0", SDLK_0}, + {"1", SDLK_1}, + {"2", SDLK_2}, + {"3", SDLK_3}, + {"4", SDLK_4}, + {"5", SDLK_5}, + {"6", SDLK_6}, + {"7", SDLK_7}, + {"8", SDLK_8}, + {"9", SDLK_9}, + {":", SDLK_COLON}, + {";", SDLK_SEMICOLON}, + {"<", SDLK_LESS}, + {"=", SDLK_EQUALS}, + {">", SDLK_GREATER}, + {"?", SDLK_QUESTION}, + {"@", SDLK_AT}, + {"[", SDLK_LEFTBRACKET}, + {"\\", SDLK_BACKSLASH}, + {"]", SDLK_RIGHTBRACKET}, + {"^", SDLK_CARET}, + {"_", SDLK_UNDERSCORE}, + {"`", SDLK_BACKQUOTE}, + {"a", SDLK_a}, + {"b", SDLK_b}, + {"c", SDLK_c}, + {"d", SDLK_d}, + {"e", SDLK_e}, + {"f", SDLK_f}, + {"g", SDLK_g}, + {"h", SDLK_h}, + {"i", SDLK_i}, + {"j", SDLK_j}, + {"k", SDLK_k}, + {"l", SDLK_l}, + {"m", SDLK_m}, + {"n", SDLK_n}, + {"o", SDLK_o}, + {"p", SDLK_p}, + {"q", SDLK_q}, + {"r", SDLK_r}, + {"s", SDLK_s}, + {"t", SDLK_t}, + {"u", SDLK_u}, + {"v", SDLK_v}, + {"w", SDLK_w}, + {"x", SDLK_x}, + {"y", SDLK_y}, + {"z", SDLK_z}, + {"Delete", SDLK_DELETE}, +#if SDL_MAJOR_VERSION == 1 + {"Keypad-0", SDLK_KP0}, + {"Keypad-1", SDLK_KP1}, + {"Keypad-2", SDLK_KP2}, + {"Keypad-3", SDLK_KP3}, + {"Keypad-4", SDLK_KP4}, + {"Keypad-5", SDLK_KP5}, + {"Keypad-6", SDLK_KP6}, + {"Keypad-7", SDLK_KP7}, + {"Keypad-8", SDLK_KP8}, + {"Keypad-9", SDLK_KP9}, +#else + {"Keypad-0", SDLK_KP_0}, + {"Keypad-1", SDLK_KP_1}, + {"Keypad-2", SDLK_KP_2}, + {"Keypad-3", SDLK_KP_3}, + {"Keypad-4", SDLK_KP_4}, + {"Keypad-5", SDLK_KP_5}, + {"Keypad-6", SDLK_KP_6}, + {"Keypad-7", SDLK_KP_7}, + {"Keypad-8", SDLK_KP_8}, + {"Keypad-9", SDLK_KP_9}, +#endif + {"Keypad-.", SDLK_KP_PERIOD}, + {"Keypad-/", SDLK_KP_DIVIDE}, + {"Keypad-*", SDLK_KP_MULTIPLY}, + {"Keypad--", SDLK_KP_MINUS}, + {"Keypad-+", SDLK_KP_PLUS}, + {"Keypad-Enter", SDLK_KP_ENTER}, + {"Keypad-=", SDLK_KP_EQUALS}, + {"Up", SDLK_UP}, + {"Down", SDLK_DOWN}, + {"Right", SDLK_RIGHT}, + {"Left", SDLK_LEFT}, + {"Insert", SDLK_INSERT}, + {"Home", SDLK_HOME}, + {"End", SDLK_END}, + {"PageUp", SDLK_PAGEUP}, + {"PageDown", SDLK_PAGEDOWN}, + {"F1", SDLK_F1}, + {"F2", SDLK_F2}, + {"F3", SDLK_F3}, + {"F4", SDLK_F4}, + {"F5", SDLK_F5}, + {"F6", SDLK_F6}, + {"F7", SDLK_F7}, + {"F8", SDLK_F8}, + {"F9", SDLK_F9}, + {"F10", SDLK_F10}, + {"F11", SDLK_F11}, + {"F12", SDLK_F12}, + {"F13", SDLK_F13}, + {"F14", SDLK_F14}, + {"F15", SDLK_F15}, + {"RightShift", SDLK_RSHIFT}, + {"LeftShift", SDLK_LSHIFT}, + {"RightControl", SDLK_RCTRL}, + {"LeftControl", SDLK_LCTRL}, + {"RightAlt", SDLK_RALT}, + {"LeftAlt", SDLK_LALT}, +#if SDL_MAJOR_VERSION == 1 + {"RightMeta", SDLK_RMETA}, + {"LeftMeta", SDLK_LMETA}, + {"RightSuper", SDLK_RSUPER}, + {"LeftSuper", SDLK_LSUPER}, + {"AltGr", SDLK_MODE}, + {"Compose", SDLK_COMPOSE}, + {"Help", SDLK_HELP}, + {"Print", SDLK_PRINT}, + {"SysReq", SDLK_SYSREQ}, + {"Break", SDLK_BREAK}, + {"Menu", SDLK_MENU}, + {"Power", SDLK_POWER}, + {"Euro", SDLK_EURO}, + {"Undo", SDLK_UNDO}, +#ifdef _WIN32_WCE + {"App1", SDLK_APP1}, + {"App2", SDLK_APP2}, + {"App3", SDLK_APP3}, + {"App4", SDLK_APP4}, + {"App5", SDLK_APP5}, + {"App6", SDLK_APP6}, +#endif /* _WIN32_WCE */ +#endif /* SDL_MAJOR_VERSION == 1 */ + + {"Unknown", 0}}; +/* Last element must have code zero */ + +const char * +VControl_code2name (int code) +{ + int i = 0; + while (1) + { + int test = keynames[i].code; + if (test == code || !test) + { + return keynames[i].name; + } + ++i; + } +} + +int +VControl_name2code (const char *name) +{ + int i = 0; + while (1) + { + const char *test = keynames[i].name; + int code = keynames[i].code; + if (!strcasecmp(test, name) || !code) + { + return code; + } + ++i; + } +} diff --git a/src/libs/input/sdl/keynames.h b/src/libs/input/sdl/keynames.h new file mode 100644 index 0000000..affd854 --- /dev/null +++ b/src/libs/input/sdl/keynames.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef LIBS_INPUT_SDL_KEYNAMES_H_ +#define LIBS_INPUT_SDL_KEYNAMES_H_ + +const char *VControl_code2name (int code); +int VControl_name2code (const char *code); +#endif diff --git a/src/libs/input/sdl/vcontrol.c b/src/libs/input/sdl/vcontrol.c new file mode 100644 index 0000000..9c226ae --- /dev/null +++ b/src/libs/input/sdl/vcontrol.c @@ -0,0 +1,1300 @@ +/* + * 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 "port.h" +#include SDL_INCLUDE(SDL.h) +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "vcontrol.h" +#include "libs/memlib.h" +#include "keynames.h" +#include "libs/log.h" +#include "libs/reslib.h" + +/* How many binding slots are allocated at once. */ +#define POOL_CHUNK_SIZE 64 + +/* Total number of key input buckets. SDL1 keys are a simple enum, + * but SDL2 scatters key symbols through the entire 32-bit space, + * so we do not rely on being able to declare an array with one + * entry per key. */ +#define KEYBOARD_INPUT_BUCKETS 512 + +typedef struct vcontrol_keybinding { + int *target; + sdl_key_t keycode; + struct vcontrol_keypool *parent; + struct vcontrol_keybinding *next; +} keybinding; + +typedef struct vcontrol_keypool { + keybinding pool[POOL_CHUNK_SIZE]; + int remaining; + struct vcontrol_keypool *next; +} keypool; + + +#ifdef HAVE_JOYSTICK + +typedef struct vcontrol_joystick_axis { + keybinding *neg, *pos; + int polarity; +} axis_type; + +typedef struct vcontrol_joystick_hat { + keybinding *left, *right, *up, *down; + Uint8 last; +} hat_type; + +typedef struct vcontrol_joystick { + SDL_Joystick *stick; + int numaxes, numbuttons, numhats; + int threshold; + axis_type *axes; + keybinding **buttons; + hat_type *hats; +} joystick; + +static joystick *joysticks; + +#endif /* HAVE_JOYSTICK */ + +static unsigned int joycount; +static keybinding *bindings[KEYBOARD_INPUT_BUCKETS]; + +static keypool *pool; + +/* Last interesting event */ +static int event_ready; +static SDL_Event last_interesting; + +static keypool * +allocate_key_chunk (void) +{ + keypool *x = HMalloc (sizeof (keypool)); + if (x) + { + int i; + x->remaining = POOL_CHUNK_SIZE; + x->next = NULL; + for (i = 0; i < POOL_CHUNK_SIZE; i++) + { + x->pool[i].target = NULL; + x->pool[i].keycode = SDLK_UNKNOWN; + x->pool[i].next = NULL; + x->pool[i].parent = x; + } + } + return x; +} + +static void +free_key_pool (keypool *x) +{ + if (x) + { + free_key_pool (x->next); + HFree (x); + } +} + +#ifdef HAVE_JOYSTICK + +static void +create_joystick (int index) +{ + SDL_Joystick *stick; + int axes, buttons, hats; + if ((unsigned int) index >= joycount) + { + log_add (log_Warning, "VControl warning: Tried to open a non-existent joystick!"); + return; + } + if (joysticks[index].stick) + { + // Joystick is already created. Return. + return; + } + stick = SDL_JoystickOpen (index); + if (stick) + { + joystick *x = &joysticks[index]; + int j; +#if SDL_MAJOR_VERSION == 1 + log_add (log_Info, "VControl opened joystick: %s", SDL_JoystickName (index)); +#else + log_add (log_Info, "VControl opened joystick: %s", SDL_JoystickName (stick)); +#endif + axes = SDL_JoystickNumAxes (stick); + buttons = SDL_JoystickNumButtons (stick); + hats = SDL_JoystickNumHats (stick); + log_add (log_Info, "%d axes, %d buttons, %d hats.", axes, buttons, hats); + x->numaxes = axes; + x->numbuttons = buttons; + x->numhats = hats; + x->axes = HMalloc (sizeof (axis_type) * axes); + x->buttons = HMalloc (sizeof (keybinding *) * buttons); + x->hats = HMalloc (sizeof (hat_type) * hats); + for (j = 0; j < axes; j++) + { + x->axes[j].neg = x->axes[j].pos = NULL; + } + for (j = 0; j < hats; j++) + { + x->hats[j].left = x->hats[j].right = NULL; + x->hats[j].up = x->hats[j].down = NULL; + x->hats[j].last = SDL_HAT_CENTERED; + } + for (j = 0; j < buttons; j++) + { + x->buttons[j] = NULL; + } + x->stick = stick; + } + else + { + log_add (log_Warning, "VControl: Could not initialize joystick #%d", index); + } +} + +static void +destroy_joystick (int index) +{ + SDL_Joystick *stick = joysticks[index].stick; + if (stick) + { + SDL_JoystickClose (stick); + joysticks[index].stick = NULL; + HFree (joysticks[index].axes); + HFree (joysticks[index].buttons); + HFree (joysticks[index].hats); + joysticks[index].numaxes = joysticks[index].numbuttons = 0; + joysticks[index].axes = NULL; + joysticks[index].buttons = NULL; + joysticks[index].hats = NULL; + } +} + +#endif /* HAVE_JOYSTICK */ + +static void +key_init (void) +{ + unsigned int i; + int num_keys; // Temp to match type of param for SDL_GetKeyState(). + pool = allocate_key_chunk (); + for (i = 0; i < KEYBOARD_INPUT_BUCKETS; i++) + bindings[i] = NULL; + +#ifdef HAVE_JOYSTICK + /* Prepare for possible joystick controls. We don't actually + GRAB joysticks unless we're asked to make a joystick + binding, though. */ + joycount = SDL_NumJoysticks (); + if (joycount) + { + joysticks = HMalloc (sizeof (joystick) * joycount); + for (i = 0; i < joycount; i++) + { + joysticks[i].stick = NULL; + joysticks[i].numaxes = joysticks[i].numbuttons = 0; + joysticks[i].axes = NULL; + joysticks[i].buttons = NULL; + joysticks[i].threshold = 10000; + } + } + else + { + joysticks = NULL; + } +#else + joycount = 0; +#endif /* HAVE_JOYSTICK */ +} + +static void +key_uninit (void) +{ + unsigned int i; + free_key_pool (pool); + for (i = 0; i < KEYBOARD_INPUT_BUCKETS; i++) + bindings[i] = NULL; + pool = NULL; + +#ifdef HAVE_JOYSTICK + for (i = 0; i < joycount; i++) + destroy_joystick (i); + HFree (joysticks); +#endif /* HAVE_JOYSTICK */ +} + +void +VControl_Init (void) +{ + key_init (); +} + +void +VControl_Uninit (void) +{ + key_uninit (); +} + +int +VControl_SetJoyThreshold (int port, int threshold) +{ +#ifdef HAVE_JOYSTICK + if (port >= 0 && (unsigned int) port < joycount) + { + joysticks[port].threshold = threshold; + return 0; + } + else +#else + (void) port; + (void) threshold; +#endif /* HAVE_JOYSTICK */ + { + // log_add (log_Warning, "VControl_SetJoyThreshold passed illegal port %d", port); + return -1; + } +} + + +static void +add_binding (keybinding **newptr, int *target, sdl_key_t keycode) +{ + keybinding *newbinding; + keypool *searchbase; + int i; + + /* Acquire a pointer to the keybinding * that we'll be + * overwriting. Along the way, ensure we haven't already + * bound this symbol to this target. If we have, return.*/ + while (*newptr != NULL) + { + if (((*newptr)->target == target) && ((*newptr)->keycode == keycode)) + { + return; + } + newptr = &((*newptr)->next); + } + + /* Now hunt through the binding pool for a free binding. */ + + /* First, find a chunk with free spots in it */ + + searchbase = pool; + while (searchbase->remaining == 0) + { + /* If we're completely full, allocate a new chunk */ + if (searchbase->next == NULL) + { + searchbase->next = allocate_key_chunk (); + } + searchbase = searchbase->next; + } + + /* Now find a free binding within it */ + + newbinding = NULL; + for (i = 0; i < POOL_CHUNK_SIZE; i++) + { + if (searchbase->pool[i].target == NULL) + { + newbinding = &searchbase->pool[i]; + break; + } + } + + /* Sanity check. */ + if (!newbinding) + { + log_add (log_Warning, "add_binding failed to find a free binding slot!"); + return; + } + + newbinding->target = target; + newbinding->keycode = keycode; + newbinding->next = NULL; + *newptr = newbinding; + searchbase->remaining--; +} + +static void +remove_binding (keybinding **ptr, int *target, sdl_key_t keycode) +{ + if (!(*ptr)) + { + /* Nothing bound to symbol; return. */ + return; + } + else if (((*ptr)->target == target) && ((*ptr)->keycode == keycode)) + { + keybinding *todel = *ptr; + *ptr = todel->next; + todel->target = NULL; + todel->keycode = SDLK_UNKNOWN; + todel->next = NULL; + todel->parent->remaining++; + } + else + { + keybinding *prev = *ptr; + while (prev && prev->next != NULL) + { + if (prev->next->target == target) + { + keybinding *todel = prev->next; + prev->next = todel->next; + todel->target = NULL; + todel->keycode = SDLK_UNKNOWN; + todel->next = NULL; + todel->parent->remaining++; + } + prev = prev->next; + } + } +} + +static void +activate (keybinding *i, sdl_key_t keycode) +{ + while (i != NULL) + { + if (i->keycode == keycode) + { + *(i->target) = (*(i->target)+1) | VCONTROL_STARTBIT; + } + i = i->next; + } +} + +static void +deactivate (keybinding *i, sdl_key_t keycode) +{ + while (i != NULL) + { + int v = *(i->target) & VCONTROL_MASK; + if ((i->keycode == keycode) && (v > 0)) + { + *(i->target) = (v-1) | (*(i->target) & VCONTROL_STARTBIT); + } + i = i->next; + } +} + +static void +event2gesture (SDL_Event *e, VCONTROL_GESTURE *g) +{ + switch (e->type) + { + case SDL_KEYDOWN: + g->type = VCONTROL_KEY; + g->gesture.key = e->key.keysym.sym; + break; + case SDL_JOYAXISMOTION: + g->type = VCONTROL_JOYAXIS; + g->gesture.axis.port = e->jaxis.which; + g->gesture.axis.index = e->jaxis.axis; + g->gesture.axis.polarity = (e->jaxis.value < 0) ? -1 : 1; + break; + case SDL_JOYHATMOTION: + g->type = VCONTROL_JOYHAT; + g->gesture.hat.port = e->jhat.which; + g->gesture.hat.index = e->jhat.hat; + g->gesture.hat.dir = e->jhat.value; + break; + case SDL_JOYBUTTONDOWN: + g->type = VCONTROL_JOYBUTTON; + g->gesture.button.port = e->jbutton.which; + g->gesture.button.index = e->jbutton.button; + break; + + default: + g->type = VCONTROL_NONE; + break; + } +} + +int +VControl_AddGestureBinding (VCONTROL_GESTURE *g, int *target) +{ + int result = -1; + switch (g->type) + { + case VCONTROL_KEY: + result = VControl_AddKeyBinding (g->gesture.key, target); + break; + + case VCONTROL_JOYAXIS: +#ifdef HAVE_JOYSTICK + result = VControl_AddJoyAxisBinding (g->gesture.axis.port, g->gesture.axis.index, (g->gesture.axis.polarity < 0) ? -1 : 1, target); + break; +#endif + case VCONTROL_JOYHAT: +#ifdef HAVE_JOYSTICK + result = VControl_AddJoyHatBinding (g->gesture.hat.port, g->gesture.hat.index, g->gesture.hat.dir, target); + break; +#endif + case VCONTROL_JOYBUTTON: +#ifdef HAVE_JOYSTICK + result = VControl_AddJoyButtonBinding (g->gesture.button.port, g->gesture.button.index, target); + break; +#endif /* HAVE_JOYSTICK */ + case VCONTROL_NONE: + /* Do nothing */ + break; + + default: + log_add (log_Warning, "VControl_AddGestureBinding didn't understand argument gesture"); + result = -1; + break; + } + return result; +} + +void +VControl_RemoveGestureBinding (VCONTROL_GESTURE *g, int *target) +{ + switch (g->type) + { + case VCONTROL_KEY: + VControl_RemoveKeyBinding (g->gesture.key, target); + break; + + case VCONTROL_JOYAXIS: +#ifdef HAVE_JOYSTICK + VControl_RemoveJoyAxisBinding (g->gesture.axis.port, g->gesture.axis.index, (g->gesture.axis.polarity < 0) ? -1 : 1, target); + break; +#endif /* HAVE_JOYSTICK */ + case VCONTROL_JOYHAT: +#ifdef HAVE_JOYSTICK + VControl_RemoveJoyHatBinding (g->gesture.hat.port, g->gesture.hat.index, g->gesture.hat.dir, target); + break; +#endif /* HAVE_JOYSTICK */ + case VCONTROL_JOYBUTTON: +#ifdef HAVE_JOYSTICK + VControl_RemoveJoyButtonBinding (g->gesture.button.port, g->gesture.button.index, target); + break; +#endif /* HAVE_JOYSTICK */ + case VCONTROL_NONE: + break; + default: + log_add (log_Warning, "VControl_RemoveGestureBinding didn't understand argument gesture"); + break; + } +} + +int +VControl_AddKeyBinding (sdl_key_t symbol, int *target) +{ + add_binding(&bindings[symbol % KEYBOARD_INPUT_BUCKETS], target, symbol); + return 0; +} + +void +VControl_RemoveKeyBinding (sdl_key_t symbol, int *target) +{ + remove_binding (&bindings[symbol % KEYBOARD_INPUT_BUCKETS], target, symbol); +} + +int +VControl_AddJoyAxisBinding (int port, int axis, int polarity, int *target) +{ +#ifdef HAVE_JOYSTICK + if (port >= 0 && (unsigned int) port < joycount) + { + joystick *j = &joysticks[port]; + if (!(j->stick)) + create_joystick (port); + if ((axis >= 0) && (axis < j->numaxes)) + { + if (polarity < 0) + { + add_binding(&joysticks[port].axes[axis].neg, target, SDLK_UNKNOWN); + } + else if (polarity > 0) + { + add_binding(&joysticks[port].axes[axis].pos, target, SDLK_UNKNOWN); + } + else + { + log_add (log_Debug, "VControl: Attempted to bind to polarity zero"); + return -1; + } + } + else + { + // log_add (log_Debug, "VControl: Attempted to bind to illegal axis %d", axis); + return -1; + } + } + else +#else + (void) port; + (void) axis; + (void) polarity; + (void) target; +#endif /* HAVE_JOYSTICK */ + { + // log_add (log_Debug, "VControl: Attempted to bind to illegal port %d", port); + return -1; + } + return 0; +} + +void +VControl_RemoveJoyAxisBinding (int port, int axis, int polarity, int *target) +{ +#ifdef HAVE_JOYSTICK + if (port >= 0 && (unsigned int) port < joycount) + { + joystick *j = &joysticks[port]; + if (!(j->stick)) + create_joystick (port); + if ((axis >= 0) && (axis < j->numaxes)) + { + if (polarity < 0) + { + remove_binding(&joysticks[port].axes[axis].neg, target, SDLK_UNKNOWN); + } + else if (polarity > 0) + { + remove_binding(&joysticks[port].axes[axis].pos, target, SDLK_UNKNOWN); + } + else + { + log_add (log_Debug, "VControl: Attempted to unbind from polarity zero"); + } + } + else + { + log_add (log_Debug, "VControl: Attempted to unbind from illegal axis %d", axis); + } + } + else +#else + (void) port; + (void) axis; + (void) polarity; + (void) target; +#endif /* HAVE_JOYSTICK */ + { + log_add (log_Debug, "VControl: Attempted to unbind from illegal port %d", port); + } +} + +int +VControl_AddJoyButtonBinding (int port, int button, int *target) +{ +#ifdef HAVE_JOYSTICK + if (port >= 0 && (unsigned int) port < joycount) + { + joystick *j = &joysticks[port]; + if (!(j->stick)) + create_joystick (port); + if ((button >= 0) && (button < j->numbuttons)) + { + add_binding(&joysticks[port].buttons[button], target, SDLK_UNKNOWN); + return 0; + } + else + { + // log_add (log_Debug, "VControl: Attempted to bind to illegal button %d", button); + return -1; + } + } + else +#else + (void) port; + (void) button; + (void) target; +#endif /* HAVE_JOYSTICK */ + { + // log_add (log_Debug, "VControl: Attempted to bind to illegal port %d", port); + return -1; + } +} + +void +VControl_RemoveJoyButtonBinding (int port, int button, int *target) +{ +#ifdef HAVE_JOYSTICK + if (port >= 0 && (unsigned int) port < joycount) + { + joystick *j = &joysticks[port]; + if (!(j->stick)) + create_joystick (port); + if ((button >= 0) && (button < j->numbuttons)) + { + remove_binding (&joysticks[port].buttons[button], target, SDLK_UNKNOWN); + } + else + { + log_add (log_Debug, "VControl: Attempted to unbind from illegal button %d", button); + } + } + else +#else + (void) port; + (void) button; + (void) target; +#endif /* HAVE_JOYSTICK */ + { + log_add (log_Debug, "VControl: Attempted to unbind from illegal port %d", port); + } +} + +int +VControl_AddJoyHatBinding (int port, int which, Uint8 dir, int *target) +{ +#ifdef HAVE_JOYSTICK + if (port >= 0 && (unsigned int) port < joycount) + { + joystick *j = &joysticks[port]; + if (!(j->stick)) + create_joystick (port); + if ((which >= 0) && (which < j->numhats)) + { + if (dir == SDL_HAT_LEFT) + { + add_binding(&joysticks[port].hats[which].left, target, SDLK_UNKNOWN); + } + else if (dir == SDL_HAT_RIGHT) + { + add_binding(&joysticks[port].hats[which].right, target, SDLK_UNKNOWN); + } + else if (dir == SDL_HAT_UP) + { + add_binding(&joysticks[port].hats[which].up, target, SDLK_UNKNOWN); + } + else if (dir == SDL_HAT_DOWN) + { + add_binding(&joysticks[port].hats[which].down, target, SDLK_UNKNOWN); + } + else + { + // log_add (log_Debug, "VControl: Attempted to bind to illegal direction"); + return -1; + } + return 0; + } + else + { + // log_add (log_Debug, "VControl: Attempted to bind to illegal hat %d", which); + return -1; + } + } + else +#else + (void) port; + (void) which; + (void) dir; + (void) target; +#endif /* HAVE_JOYSTICK */ + { + // log_add (log_Debug, "VControl: Attempted to bind to illegal port %d", port); + return -1; + } +} + +void +VControl_RemoveJoyHatBinding (int port, int which, Uint8 dir, int *target) +{ +#ifdef HAVE_JOYSTICK + if (port >= 0 && (unsigned int) port < joycount) + { + joystick *j = &joysticks[port]; + if (!(j->stick)) + create_joystick (port); + if ((which >= 0) && (which < j->numhats)) + { + if (dir == SDL_HAT_LEFT) + { + remove_binding(&joysticks[port].hats[which].left, target, SDLK_UNKNOWN); + } + else if (dir == SDL_HAT_RIGHT) + { + remove_binding(&joysticks[port].hats[which].right, target, SDLK_UNKNOWN); + } + else if (dir == SDL_HAT_UP) + { + remove_binding(&joysticks[port].hats[which].up, target, SDLK_UNKNOWN); + } + else if (dir == SDL_HAT_DOWN) + { + remove_binding(&joysticks[port].hats[which].down, target, SDLK_UNKNOWN); + } + else + { + log_add (log_Debug, "VControl: Attempted to unbind from illegal direction"); + } + } + else + { + log_add (log_Debug, "VControl: Attempted to unbind from illegal hat %d", which); + } + } + else +#else + (void) port; + (void) which; + (void) dir; + (void) target; +#endif /* HAVE_JOYSTICK */ + { + log_add (log_Debug, "VControl: Attempted to unbind from illegal port %d", port); + } +} + +void +VControl_RemoveAllBindings (void) +{ + key_uninit (); + key_init (); +} + +void +VControl_ProcessKeyDown (sdl_key_t symbol) +{ + activate (bindings[symbol % KEYBOARD_INPUT_BUCKETS], symbol); +} + +void +VControl_ProcessKeyUp (sdl_key_t symbol) +{ + deactivate (bindings[symbol % KEYBOARD_INPUT_BUCKETS], symbol); +} + +void +VControl_ProcessJoyButtonDown (int port, int button) +{ +#ifdef HAVE_JOYSTICK + if (!joysticks[port].stick) + return; + activate (joysticks[port].buttons[button], SDLK_UNKNOWN); +#else + (void) port; + (void) button; +#endif /* HAVE_JOYSTICK */ +} + +void +VControl_ProcessJoyButtonUp (int port, int button) +{ +#ifdef HAVE_JOYSTICK + if (!joysticks[port].stick) + return; + deactivate (joysticks[port].buttons[button], SDLK_UNKNOWN); +#else + (void) port; + (void) button; +#endif /* HAVE_JOYSTICK */ +} + +void +VControl_ProcessJoyAxis (int port, int axis, int value) +{ +#ifdef HAVE_JOYSTICK + int t; + if (!joysticks[port].stick) + return; + t = joysticks[port].threshold; + if (value > t) + { + if (joysticks[port].axes[axis].polarity != 1) + { + if (joysticks[port].axes[axis].polarity == -1) + { + deactivate (joysticks[port].axes[axis].neg, SDLK_UNKNOWN); + } + joysticks[port].axes[axis].polarity = 1; + activate (joysticks[port].axes[axis].pos, SDLK_UNKNOWN); + } + } + else if (value < -t) + { + if (joysticks[port].axes[axis].polarity != -1) + { + if (joysticks[port].axes[axis].polarity == 1) + { + deactivate (joysticks[port].axes[axis].pos, SDLK_UNKNOWN); + } + joysticks[port].axes[axis].polarity = -1; + activate (joysticks[port].axes[axis].neg, SDLK_UNKNOWN); + } + } + else + { + if (joysticks[port].axes[axis].polarity == -1) + { + deactivate (joysticks[port].axes[axis].neg, SDLK_UNKNOWN); + } + else if (joysticks[port].axes[axis].polarity == 1) + { + deactivate (joysticks[port].axes[axis].pos, SDLK_UNKNOWN); + } + joysticks[port].axes[axis].polarity = 0; + } +#else + (void) port; + (void) axis; + (void) value; +#endif /* HAVE_JOYSTICK */ +} + +void +VControl_ProcessJoyHat (int port, int which, Uint8 value) +{ +#ifdef HAVE_JOYSTICK + Uint8 old; + if (!joysticks[port].stick) + return; + old = joysticks[port].hats[which].last; + if (!(old & SDL_HAT_LEFT) && (value & SDL_HAT_LEFT)) + activate (joysticks[port].hats[which].left, SDLK_UNKNOWN); + if (!(old & SDL_HAT_RIGHT) && (value & SDL_HAT_RIGHT)) + activate (joysticks[port].hats[which].right, SDLK_UNKNOWN); + if (!(old & SDL_HAT_UP) && (value & SDL_HAT_UP)) + activate (joysticks[port].hats[which].up, SDLK_UNKNOWN); + if (!(old & SDL_HAT_DOWN) && (value & SDL_HAT_DOWN)) + activate (joysticks[port].hats[which].down, SDLK_UNKNOWN); + if ((old & SDL_HAT_LEFT) && !(value & SDL_HAT_LEFT)) + deactivate (joysticks[port].hats[which].left, SDLK_UNKNOWN); + if ((old & SDL_HAT_RIGHT) && !(value & SDL_HAT_RIGHT)) + deactivate (joysticks[port].hats[which].right, SDLK_UNKNOWN); + if ((old & SDL_HAT_UP) && !(value & SDL_HAT_UP)) + deactivate (joysticks[port].hats[which].up, SDLK_UNKNOWN); + if ((old & SDL_HAT_DOWN) && !(value & SDL_HAT_DOWN)) + deactivate (joysticks[port].hats[which].down, SDLK_UNKNOWN); + joysticks[port].hats[which].last = value; +#else + (void) port; + (void) which; + (void) value; +#endif /* HAVE_JOYSTICK */ +} + +void +VControl_ResetInput (void) +{ + /* Step through every valid entry in the binding pool and zero + * them out. This will probably zero entries multiple times; + * oh well, no harm done. */ + + keypool *base = pool; + while (base != NULL) + { + int i; + for (i = 0; i < POOL_CHUNK_SIZE; i++) + { + if(base->pool[i].target) + { + *(base->pool[i].target) = 0; + } + } + base = base->next; + } +} + +void +VControl_BeginFrame (void) +{ + /* Step through every valid entry in the binding pool and zero + * out the frame-start bit. This will probably zero entries + * multiple times; oh well, no harm done. */ + + keypool *base = pool; + while (base != NULL) + { + int i; + for (i = 0; i < POOL_CHUNK_SIZE; i++) + { + if(base->pool[i].target) + { + *(base->pool[i].target) &= VCONTROL_MASK; + } + } + base = base->next; + } +} + +void +VControl_HandleEvent (const SDL_Event *e) +{ + switch (e->type) + { + case SDL_KEYDOWN: +#if SDL_MAJOR_VERSION > 1 + if (!e->key.repeat) +#endif + { + VControl_ProcessKeyDown (e->key.keysym.sym); + last_interesting = *e; + event_ready = 1; + } + break; + case SDL_KEYUP: + VControl_ProcessKeyUp (e->key.keysym.sym); + break; + +#ifdef HAVE_JOYSTICK + case SDL_JOYAXISMOTION: + VControl_ProcessJoyAxis (e->jaxis.which, e->jaxis.axis, e->jaxis.value); + if ((e->jaxis.value > 15000) || (e->jaxis.value < -15000)) + { + last_interesting = *e; + event_ready = 1; + } + break; + case SDL_JOYHATMOTION: + VControl_ProcessJoyHat (e->jhat.which, e->jhat.hat, e->jhat.value); + last_interesting = *e; + event_ready = 1; + break; + case SDL_JOYBUTTONDOWN: + VControl_ProcessJoyButtonDown (e->jbutton.which, e->jbutton.button); + last_interesting = *e; + event_ready = 1; + break; + case SDL_JOYBUTTONUP: + VControl_ProcessJoyButtonUp (e->jbutton.which, e->jbutton.button); + break; +#endif /* HAVE_JOYSTICK */ + + default: + break; + } +} + +/* Tracking the last interesting event */ + +void +VControl_ClearGesture (void) +{ + event_ready = 0; +} + +int +VControl_GetLastGesture (VCONTROL_GESTURE *g) +{ + if (event_ready && g != NULL) + { + event2gesture(&last_interesting, g); + } + return event_ready; +} + +/* Configuration file grammar is as follows: One command per line, + * hashes introduce comments that persist to end of line. Blank lines + * are ignored. + * + * Terminals are represented here as quoted strings, e.g. "foo" for + * the literal string foo. These are matched case-insensitively. + * Special terminals are: + * + * KEYNAME: This names a key, as defined in keynames.c. + * IDNAME: This is an arbitrary string of alphanumerics, + * case-insensitive, and ending with a colon. This + * names an application-specific control value. + * NUM: This is an unsigned integer. + * EOF: End of file + * + * Nonterminals (the grammar itself) have the following productions: + * + * configline <- IDNAME binding + * | "joystick" NUM "threshold" NUM + * | "version" NUM + * + * binding <- "key" KEYNAME + * | "joystick" NUM joybinding + * + * joybinding <- "axis" NUM polarity + * | "button" NUM + * | "hat" NUM direction + * + * polarity <- "positive" | "negative" + * + * dir <- "up" | "down" | "left" | "right" + * + * This grammar is amenable to simple recursive descent parsing; + * in fact, it's fully LL(1). */ + +/* Actual maximum line and token sizes are two less than this, since + * we need space for the \n\0 at the end */ +#define LINE_SIZE 256 +#define TOKEN_SIZE 64 + +typedef struct vcontrol_parse_state { + char line[LINE_SIZE]; + char token[TOKEN_SIZE]; + int index; + int error; + int linenum; +} parse_state; + +static void +next_token (parse_state *state) +{ + int index, base; + + state->token[0] = 0; + /* skip preceding whitespace */ + base = state->index; + while (state->line[base] && isspace (state->line[base])) + { + base++; + } + + index = 0; + while (index < (TOKEN_SIZE-1) && state->line[base+index] && !isspace (state->line[base+index])) + { + state->token[index] = state->line[base+index]; + index++; + } + state->token[index] = 0; + + /* If the token was too long, skip ahead until we get to whitespace */ + while (state->line[base+index] && !isspace (state->line[base+index])) + { + index++; + } + + state->index = base+index; +} + +static void +expected_error (parse_state *state, const char *expected) +{ + log_add (log_Warning, "VControl: Expected '%s' on config file line %d", + expected, state->linenum); + state->error = 1; +} + +static void +consume (parse_state *state, const char *expected) +{ + if (strcasecmp (expected, state->token)) + { + expected_error (state, expected); + } + next_token (state); +} + +static int +consume_keyname (parse_state *state) +{ + int keysym = VControl_name2code (state->token); + if (!keysym) + { + log_add (log_Warning, "VControl: Illegal key name '%s' on config file line %d", + state->token, state->linenum); + state->error = 1; + } + next_token (state); + return keysym; +} + +static int +consume_num (parse_state *state) +{ + char *end; + int result = strtol (state->token, &end, 10); + if (*end != '\0') + { + log_add (log_Warning, "VControl: Expected integer on config line %d", + state->linenum); + state->error = 1; + } + next_token (state); + return result; +} + +static int +consume_polarity (parse_state *state) +{ + int result = 0; + if (!strcasecmp (state->token, "positive")) + { + result = 1; + } + else if (!strcasecmp (state->token, "negative")) + { + result = -1; + } + else + { + expected_error (state, "positive' or 'negative"); + } + next_token (state); + return result; +} + +static Uint8 +consume_dir (parse_state *state) +{ + Uint8 result = 0; + if (!strcasecmp (state->token, "left")) + { + result = SDL_HAT_LEFT; + } + else if (!strcasecmp (state->token, "right")) + { + result = SDL_HAT_RIGHT; + } + else if (!strcasecmp (state->token, "up")) + { + result = SDL_HAT_UP; + } + else if (!strcasecmp (state->token, "down")) + { + result = SDL_HAT_DOWN; + } + else + { + expected_error (state, "left', 'right', 'up' or 'down"); + } + next_token (state); + return result; +} + +static void +parse_joybinding (parse_state *state, VCONTROL_GESTURE *gesture) +{ + int sticknum; + consume (state, "joystick"); + sticknum = consume_num (state); + if (!state->error) + { + if (!strcasecmp (state->token, "axis")) + { + int axisnum; + consume (state, "axis"); + axisnum = consume_num (state); + if (!state->error) + { + int polarity = consume_polarity (state); + if (!state->error) + { + gesture->type = VCONTROL_JOYAXIS; + gesture->gesture.axis.port = sticknum; + gesture->gesture.axis.index = axisnum; + gesture->gesture.axis.polarity = polarity; + } + } + } + else if (!strcasecmp (state->token, "button")) + { + int buttonnum; + consume (state, "button"); + buttonnum = consume_num (state); + if (!state->error) + { + gesture->type = VCONTROL_JOYBUTTON; + gesture->gesture.button.port = sticknum; + gesture->gesture.button.index = buttonnum; + } + } + else if (!strcasecmp (state->token, "hat")) + { + int hatnum; + consume (state, "hat"); + hatnum = consume_num (state); + if (!state->error) + { + Uint8 dir = consume_dir (state); + if (!state->error) + { + gesture->type = VCONTROL_JOYHAT; + gesture->gesture.hat.port = sticknum; + gesture->gesture.hat.index = hatnum; + gesture->gesture.hat.dir = dir; + } + } + } + else + { + expected_error (state, "axis', 'button', or 'hat"); + } + } +} + +static void +parse_gesture (parse_state *state, VCONTROL_GESTURE *gesture) +{ + gesture->type = VCONTROL_NONE; /* Default to error */ + if (!strcasecmp (state->token, "key")) + { + /* Parse key binding */ + int keysym; + consume (state, "key"); + keysym = consume_keyname (state); + if (!state->error) + { + gesture->type = VCONTROL_KEY; + gesture->gesture.key = keysym; + } + } + else if (!strcasecmp (state->token, "joystick")) + { + parse_joybinding (state, gesture); + } + else + { + expected_error (state, "key' or 'joystick"); + } +} + +void +VControl_ParseGesture (VCONTROL_GESTURE *g, const char *spec) +{ + parse_state ps; + + strncpy (ps.line, spec, LINE_SIZE); + ps.line[LINE_SIZE - 1] = '\0'; + ps.index = ps.error = 0; + ps.linenum = -1; + + next_token (&ps); + parse_gesture (&ps, g); + if (ps.error) + printf ("Error parsing %s\n", spec); +} + +int +VControl_DumpGesture (char *buf, int n, VCONTROL_GESTURE *g) +{ + switch (g->type) + { + case VCONTROL_KEY: + return snprintf (buf, n, "key %s", VControl_code2name (g->gesture.key)); + case VCONTROL_JOYAXIS: + return snprintf (buf, n, "joystick %d axis %d %s", g->gesture.axis.port, g->gesture.axis.index, + (g->gesture.axis.polarity > 0) ? "positive" : "negative"); + case VCONTROL_JOYBUTTON: + return snprintf (buf, n, "joystick %d button %d", g->gesture.button.port, g->gesture.button.index); + case VCONTROL_JOYHAT: + return snprintf (buf, n, "joystick %d hat %d %s", g->gesture.hat.port, g->gesture.hat.index, + (g->gesture.hat.dir == SDL_HAT_UP) ? "up" : + ((g->gesture.hat.dir == SDL_HAT_DOWN) ? "down" : + ((g->gesture.hat.dir == SDL_HAT_LEFT) ? "left" : "right"))); + default: + buf[0] = '\0'; + return 0; + } +} diff --git a/src/libs/input/sdl/vcontrol.h b/src/libs/input/sdl/vcontrol.h new file mode 100644 index 0000000..9c33445 --- /dev/null +++ b/src/libs/input/sdl/vcontrol.h @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#ifndef LIBS_INPUT_SDL_VCONTROL_H_ +#define LIBS_INPUT_SDL_VCONTROL_H_ + +#include "port.h" +#include SDL_INCLUDE(SDL.h) + +#if SDL_MAJOR_VERSION == 1 +typedef SDLKey sdl_key_t; +#else +typedef SDL_Keycode sdl_key_t; +#endif + +/* Initialization routines */ +void VControl_Init (void); +void VControl_Uninit (void); + +/* Structures for representing actual VControl Inputs. Returned by + iterators and used to construct bindings. */ + +typedef enum { + VCONTROL_NONE, + VCONTROL_KEY, + VCONTROL_JOYAXIS, + VCONTROL_JOYBUTTON, + VCONTROL_JOYHAT, + NUM_VCONTROL_GESTURES +} VCONTROL_GESTURE_TYPE; + +typedef struct { + VCONTROL_GESTURE_TYPE type; + union { + sdl_key_t key; + struct { int port, index, polarity; } axis; + struct { int port, index; } button; + struct { int port, index; Uint8 dir; } hat; + } gesture; +} VCONTROL_GESTURE; + +/* Control of bindings */ +int VControl_AddGestureBinding (VCONTROL_GESTURE *g, int *target); +void VControl_RemoveGestureBinding (VCONTROL_GESTURE *g, int *target); + +int VControl_AddKeyBinding (sdl_key_t symbol, int *target); +void VControl_RemoveKeyBinding (sdl_key_t symbol, int *target); +int VControl_AddJoyAxisBinding (int port, int axis, int polarity, int *target); +void VControl_RemoveJoyAxisBinding (int port, int axis, int polarity, int *target); +int VControl_SetJoyThreshold (int port, int threshold); +int VControl_AddJoyButtonBinding (int port, int button, int *target); +void VControl_RemoveJoyButtonBinding (int port, int button, int *target); +int VControl_AddJoyHatBinding (int port, int which, Uint8 dir, int *target); +void VControl_RemoveJoyHatBinding (int port, int which, Uint8 dir, int *target); + +void VControl_RemoveAllBindings (void); + +/* Signal to VControl that a frame is about to begin. */ +void VControl_BeginFrame (void); + +/* The listener. Routines besides HandleEvent may be used to 'fake' inputs without + * fabricating an SDL_Event. + */ +void VControl_HandleEvent (const SDL_Event *e); +void VControl_ProcessKeyDown (sdl_key_t symbol); +void VControl_ProcessKeyUp (sdl_key_t symbol); +void VControl_ProcessJoyButtonDown (int port, int button); +void VControl_ProcessJoyButtonUp (int port, int button); +void VControl_ProcessJoyAxis (int port, int axis, int value); +void VControl_ProcessJoyHat (int port, int which, Uint8 value); + +/* Force the input into the blank state. For preventing "sticky" keys. */ +void VControl_ResetInput (void); + +/* Translate between gestures and string representations thereof. */ +void VControl_ParseGesture (VCONTROL_GESTURE *g, const char *spec); +int VControl_DumpGesture (char *buf, int n, VCONTROL_GESTURE *g); + +/* Tracking the "last interesting gesture." Used to poll to find new + control keys. */ + +void VControl_ClearGesture (void); +int VControl_GetLastGesture (VCONTROL_GESTURE *g); + +/* Constants for handling the "Start bit." If a gesture is made, and + * then ends, within a single frame, it will still, for one frame, + * have a nonzero value. This is because Bit 16 will be on for the + * first frame a gesture is struck. This bit is cleared when + * VControl_BeginFrame() is called. These constants are used to mask + * out results if necessary. */ + +#define VCONTROL_STARTBIT 0x10000 +#define VCONTROL_MASK 0x0FFFF + +#endif |