summaryrefslogtreecommitdiff
path: root/src/libs/input
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/input')
-rw-r--r--src/libs/input/Makeinfo6
-rw-r--r--src/libs/input/inpintrn.h25
-rw-r--r--src/libs/input/input_common.c20
-rw-r--r--src/libs/input/input_common.h39
-rw-r--r--src/libs/input/sdl/Makeinfo2
-rw-r--r--src/libs/input/sdl/input.c625
-rw-r--r--src/libs/input/sdl/input.h27
-rw-r--r--src/libs/input/sdl/keynames.c229
-rw-r--r--src/libs/input/sdl/keynames.h22
-rw-r--r--src/libs/input/sdl/vcontrol.c1300
-rw-r--r--src/libs/input/sdl/vcontrol.h108
11 files changed, 2403 insertions, 0 deletions
diff --git a/src/libs/input/Makeinfo b/src/libs/input/Makeinfo
new file mode 100644
index 0000000..b76604f
--- /dev/null
+++ b/src/libs/input/Makeinfo
@@ -0,0 +1,6 @@
+if [ "$uqm_GFXMODULE" = "sdl" ]; then
+ uqm_SUBDIRS="sdl"
+fi
+
+uqm_CFILES="input_common.c"
+uqm_HFILES="inpintrn.h input_common.h"
diff --git a/src/libs/input/inpintrn.h b/src/libs/input/inpintrn.h
new file mode 100644
index 0000000..1d48ccc
--- /dev/null
+++ b/src/libs/input/inpintrn.h
@@ -0,0 +1,25 @@
+//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 LIBS_INPUT_INPINTRN_H_
+#define LIBS_INPUT_INPINTRN_H_
+
+#include "libs/inplib.h"
+#include "libs/input/input_common.h"
+
+#endif /* LIBS_INPUT_INPINTRN_H_ */
diff --git a/src/libs/input/input_common.c b/src/libs/input/input_common.c
new file mode 100644
index 0000000..7a0bbc1
--- /dev/null
+++ b/src/libs/input/input_common.c
@@ -0,0 +1,20 @@
+//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 "port.h"
+#include "inpintrn.h"
diff --git a/src/libs/input/input_common.h b/src/libs/input/input_common.h
new file mode 100644
index 0000000..5979320
--- /dev/null
+++ b/src/libs/input/input_common.h
@@ -0,0 +1,39 @@
+//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_COMMON_H
+#define INPUT_COMMON_H
+
+// driver for TFB_InitInput
+enum
+{
+ TFB_INPUTDRIVER_SDL
+};
+
+// flags for TFB_InitInput
+//#define TFB_INPUTFLAGS_ETC (1<<0)
+
+extern int TFB_InitInput (int driver, int flags);
+extern void TFB_UninitInput (void);
+
+#define MAX_FLIGHT_ALTERNATES 2
+
+extern void TFB_SetInputVectors (volatile int menu[], int num_menu,
+ volatile int flight[], int num_templ, int num_flight);
+
+#endif
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