summaryrefslogtreecommitdiff
path: root/src/libs/input/sdl/vcontrol.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/input/sdl/vcontrol.c')
-rw-r--r--src/libs/input/sdl/vcontrol.c1300
1 files changed, 1300 insertions, 0 deletions
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;
+ }
+}