Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Feature: Joystick support for up to 4 Devices. #39

Open
wants to merge 9 commits into from

2 participants

@seclorum

This patch adds Joystick support to LOAD81. It allows up to 4 devices to be used within the Lua environment, and for that purpose adds a new global Lua table called 'joystick', with fields x, y, and name, where:

x = X-axis value of the joystick
y = Y-axis value of the joystick
name = name of the joystick device

This patch has been mainly targeted at Open Pandora users, where the onboard nubs are featured as independent high-resolution joysticks. For that reason, complete button support is not finished - but will be added in another patch shortly.

Tested on Open Pandora hardware, and Macbook Pro with paired iControlpad controllers. On Open Pandora hardware, joysticks will be named thus:

joystick[1].name = "gpio-keys" (Special feature of Open Pandora, for hardware hackers mostly)
joystick[2].name = "nub0" (The Left nub)
joystick[3].name = "nub1" (The Right nub)

@seclorum

Also, please see new examples/joysticks.lua for demo purposes.

@antirez
Owner

I definitely like the idea of supporting joysticks, but I think the exported API may be more consistent with what we have currently, more info ASAP, but I've some ideas :) I wonder if there is a way for me to simulate a joystick on osx or Linux...

Thanks!

@seclorum

I'm sorry I don't quite understand what you mean by the exported API - do you mean the joysticks[] table, or something? I'm rather eager to get this merged so I can move on to other things while using joysticks (this works really nicely on Open Pandora and OSX with iControlpad) so if you could clear up whats blocking this merge, it'd be helpful.

@antirez
Owner

This is what I need to merge it:

  • Removal of additional newlines before return in sdlInit() and createFrameBuffer() that was out of the scope of the patch.
  • updateJoystick(State|Name)() should use setTableField() instead, that is now generically able to set fields of global tables. If this is not possible at least the two functions should be unified because they do a very similar thing.
  • initJoysticks() should contain the code to properly initialize joystick[...] up to MAX_JOYSTICKS. Static initialization of first four joysticks should be removed.
  • initJoysticks() should not contain commented #if 0 / #endif blocks.
  • API: there should not be global JOYSTICKS, it should use a joystick table field, like joystick.num_joysticks.
  • API: Non existing joysticks should probably have the 'name' field set to nil instead of "none" that may conceptually be a valid joystick name.
  • API: Support for buttons is needed since day 0 IMHO, but this is not blocking.

Thanks!
Salvatore

@seclorum

Okay I will work on these points and submit a new patch shortly. Thanks for the feedback.

@seclorum

Re: Removal of additional newlines before return in sdlInit() and createFrameBuffer() that was out of the scope of the patch.

Can we come up with an indent config, or comment-string for vim or something, that solves this code-formatting issue? I'm an "indent -kr -ts4" kind of guy, and those returns might be the result of that .. maybe you have a rule for the indent cmd that we should use to enforce formatting? (this way I could run the rule before checkins, and still have our own local preferences for indentation/layout..)

@seclorum

Re: updateJoystick(State|Name)() should use setTableField() instead, that is now generically able to set fields of global tables. If this is not possible at least the two functions should be unified because they do a very similar thing.

There is a difference between the API's. " setTableFieldNumber sets a "table.field = number", whereas updateJoystickState goes "table[int].field = number".

Alas, there is probably a way to nest the lua atoms, I confess to not having the fortitude to know how to do that yet, but perhaps it would instead be better to refactor updateJoystickState[Name,Number] to be API calls "setTableIndexedFieldNumber" and "setTableIndexedFieldString" and use them elsewhere as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 12, 2012
  1. Feature: Joystick support for up to 4 devices.

    Jay Vaughan authored
Commits on Mar 13, 2012
  1. Slightly cleaner demo

    Jay Vaughan authored
  2. Fix bug with joysticks closing too soon.

    Jay Vaughan authored
  3. Sexier joystick demo.

    Jay Vaughan authored
  4. Attribution.

    Jay Vaughan authored
  5. Comments for joystick demo.

    Jay Vaughan authored
Commits on Mar 14, 2012
  1. Properly pop the Lua stack in Joysticks

    Jay Vaughan authored
  2. Nope, that wasn't right.

    Jay Vaughan authored
This page is out of date. Refresh to see the latest.
Showing with 207 additions and 4 deletions.
  1. +67 −0 examples/joysticks.lua
  2. +3 −1 framebuffer.c
  3. +4 −1 framebuffer.h
  4. +133 −2 load81.c
View
67 examples/joysticks.lua
@@ -0,0 +1,67 @@
+--
+-- simple demo of joystick input
+-- by torpor (seclorum@me.com)
+--
+-- the joystick[] table is global, and contains .x/.y/.name fields
+-- describing the joystick. the JOYSTICK global contains the
+-- number of physical joysticks detected by LOAD81 at startup.
+--
+-- the maximum number of joysticks available is 4.
+--
+
+-- each joystick will have its own color, and since there
+-- a maximum of 4 joysticks available then we have 4
+-- unique colors to use
+colors = {}
+colors = {
+{r=255,g=0,b=255,a=1},
+{r=0,g=0,b=255,a=1},
+{r=255,g=0,b=0,a=1},
+{r=0,g=255,b=0,a=1}
+}
+
+function setup()
+ background(0,0,0,0)
+ -- divide the screen in 4, one quad for each joystick device
+ joy_quad = HEIGHT / 4;
+end
+
+-- simple map function
+function map(x, in_min, in_max, out_min, out_max)
+ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+end
+
+function draw_joystick_info(joynum)
+ -- each joystick gets its own color
+ fill(colors[joynum].r,colors[joynum].g,colors[joynum].b,colors[joynum].a);
+
+ -- text explaining joystick name and x/y values goes in a 'quarter' of the screen
+ text(10, joynum * joy_quad, string.format("# %d %s x:%d/y:%d",
+ joynum, joystick[joynum].name,
+ joystick[joynum].x, joystick[joynum].y));
+
+ -- each joystick returns x/y axes values from -32767 to 32767, so we map to
+ -- the screen size
+ dot_x, dot_y = 0;
+ dot_x = map(joystick[joynum].x, 32767, -32767, WIDTH, 0);
+ dot_y = map(joystick[joynum].y, 32767, -32767, 0, HEIGHT);
+
+ -- draw the joystick dot
+ ellipse(dot_x, dot_y, 10, 10);
+end
+
+
+function draw()
+ -- clear the screen
+ background(0,0,0,0);
+
+ if JOYSTICKS == 0 then
+ fill (255,0,0,1);
+ text(10, joy_quad, string.format("No joysticks detected .. plug one in and try again!"));
+ end
+
+ -- draw the info for each joystick
+ for jn = 1, JOYSTICKS, 1 do
+ draw_joystick_info(jn);
+ end
+end
View
4 framebuffer.c
@@ -8,7 +8,7 @@ SDL_Surface *sdlInit(int width, int height, int bpp, int fullscreen) {
SDL_Surface *screen;
if (fullscreen) flags |= SDL_FULLSCREEN;
- if (SDL_Init(SDL_INIT_VIDEO) == -1) {
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1) {
fprintf(stderr, "SDL Init error: %s\n", SDL_GetError());
return NULL;
}
@@ -22,6 +22,7 @@ SDL_Surface *sdlInit(int width, int height, int bpp, int fullscreen) {
* keys are translated into characters with automatic support for modifiers
* (for instance shift modifier to print capital letters and symbols). */
SDL_EnableUNICODE(SDL_ENABLE);
+
return screen;
}
@@ -34,6 +35,7 @@ frameBuffer *createFrameBuffer(int width, int height, int bpp, int fullscreen) {
SDL_initFramerate(&fb->fps_mgr);
/* Load the bitmap font */
bfLoadFont((char**)BitmapFont);
+
return fb;
}
View
5 framebuffer.h
@@ -9,11 +9,14 @@
#define FONT_HEIGHT 16
#define FONT_KERNING 10
+#define MAX_JOYSTICKS 4
+
typedef struct frameBuffer {
int width;
int height;
- SDL_Surface *screen;
FPSmanager fps_mgr;
+ SDL_Surface *screen;
+ SDL_Joystick *joysticks[MAX_JOYSTICKS];
} frameBuffer;
frameBuffer *createFrameBuffer(int width, int height, int bpp, int fullscreen);
View
135 load81.c
@@ -276,6 +276,68 @@ void draw(void) {
}
}
+/* update the joystick[] table, by given field
+ e.g. joystick[1].x = 100
+ stack:
+ joystick global
+ [1] joynum
+ .x field
+ = 100 value
+*/
+void updateJoystickState(int joynum, char *field, int value)
+{
+ lua_getglobal(l81.L, "joystick");
+
+ if (lua_isnil(l81.L,-1)) {
+ lua_pop(l81.L,1);
+ lua_newtable(l81.L);
+ lua_setglobal(l81.L,"joystick");
+ lua_getglobal(l81.L,"joystick");
+ }
+
+ if (lua_istable(l81.L, -1)) {
+ lua_pushnumber(l81.L, joynum);
+ lua_gettable(l81.L, -2);
+ lua_pushstring(l81.L, field);
+ lua_pushnumber(l81.L, value);
+ lua_settable(l81.L, -3);
+ }
+
+ lua_pop(l81.L, 2);
+}
+
+/*
+ update the joystick[] table, .name field
+ e.g. joystick[1].name = "nub0"
+ stack:
+ joystick global
+ [1] joynum
+ .name field
+ = "nub0" value
+*/
+void updateJoystickName(int joynum, const char *name)
+{
+ lua_getglobal(l81.L, "joystick");
+
+ if (lua_isnil(l81.L,-1)) {
+ lua_pop(l81.L,1);
+ lua_newtable(l81.L);
+ lua_setglobal(l81.L,"joystick");
+ lua_getglobal(l81.L,"joystick");
+ }
+
+ if (lua_istable(l81.L, -1)) {
+ lua_pushnumber(l81.L, joynum);
+ lua_gettable(l81.L, -2);
+ lua_pushstring(l81.L, "name");
+ lua_pushstring(l81.L, name);
+ lua_settable(l81.L, -3);
+ }
+
+ lua_pop(l81.L, 2);
+}
+
+
/* Update the keyboard.pressed and mouse.pressed Lua table. */
void updatePressedState(char *object, char *keyname, int pressed) {
lua_getglobal(l81.L,object); /* $keyboard */
@@ -306,6 +368,18 @@ void mouseMovedEvent(int x, int y, int xrel, int yrel) {
setTableFieldNumber("mouse","yrel",-yrel);
}
+void joystickXMovedEvent(int joy_num, Sint16 x) {
+ if (joy_num < MAX_JOYSTICKS) {
+ updateJoystickState(joy_num, "x", x);
+ }
+}
+
+void joystickYMovedEvent(int joy_num, Sint16 y) {
+ if (joy_num < MAX_JOYSTICKS) {
+ updateJoystickState(joy_num, "y", y);
+ }
+}
+
void mouseButtonEvent(int button, int pressed) {
char buttonname[32];
@@ -357,6 +431,16 @@ int processSdlEvents(void) {
case SDL_MOUSEBUTTONUP:
mouseButtonEvent(event.button.button,0);
break;
+ case SDL_JOYAXISMOTION: /* Handle Joystick Motion */
+ //printf("joystick %d moved on %d axis\n", event.jaxis.which, event.jaxis.axis);
+ if( event.jaxis.axis == 0) { /* x-axis */
+ joystickXMovedEvent(event.jaxis.which + 1, event.jaxis.value); /* C vs. Lua offsets */
+ }
+ if( event.jaxis.axis == 1) { /* y-axis */
+ joystickYMovedEvent(event.jaxis.which + 1, event.jaxis.value); /* C vs. Lua offsets */
+ }
+ break;
+
case SDL_QUIT:
exit(0);
break;
@@ -422,7 +506,44 @@ int loadProgram(void) {
editorClearError();
return 0;
}
+
+void initJoysticks(frameBuffer *fb) {
+ int cur_joy;
+ /* Initialize Joysticks */
+ SDL_JoystickEventState(SDL_ENABLE);
+
+ for(cur_joy=0; cur_joy < SDL_NumJoysticks(); cur_joy++ ) {
+ fb->joysticks[cur_joy] = SDL_JoystickOpen(cur_joy);
+
+ updateJoystickName(cur_joy + 1, SDL_JoystickName(cur_joy));
+#if 0
+ {
+ printf("joy %d init: %p\n", cur_joy, fb->joysticks[cur_joy]);
+ printf("Joystick name: %s\n ", SDL_JoystickName(cur_joy));
+ printf("Axis: %d\nt", SDL_JoystickNumAxes(fb->joysticks[cur_joy]));
+ printf("Trackballs:%d\n", SDL_JoystickNumBalls(fb->joysticks[cur_joy]));
+ printf("Hats: %d\n", SDL_JoystickNumHats(fb->joysticks[cur_joy]));
+ printf("Buttons: %d\n", SDL_JoystickNumButtons(fb->joysticks[cur_joy]));
+ }
+#endif
+
+ }
+}
+/*
+ Close joysticks.
+*/
+void closeJoysticks(frameBuffer *fb) {
+ int cur_joy;
+ for(cur_joy=0; cur_joy < SDL_NumJoysticks(); cur_joy++) {
+ if (fb->joysticks[cur_joy]) {
+ SDL_JoystickClose( fb->joysticks[cur_joy]);
+ updateJoystickName(cur_joy + 1, "none");
+ }
+ }
+ setNumber("JOYSTICKS", 0);
+}
+
void initScreen(void) {
l81.fb = createFrameBuffer(l81.width,l81.height,
l81.bpp,l81.opt_full_screen);
@@ -430,8 +551,14 @@ void initScreen(void) {
void resetProgram(void) {
char *initscript =
- "keyboard={}; keyboard['pressed']={};"
- "mouse={}; mouse['pressed']={};";
+ "keyboard={}; keyboard['pressed']={};" \
+ "mouse={}; mouse['pressed']={};" \
+ "joystick={};" \
+ "joystick[1]={x=0;y=0;name='none'};" \
+ "joystick[2]={x=0;y=0;name='none'};" \
+ "joystick[3]={x=0;y=0;name='none'};" \
+ "joystick[4]={x=0;y=0;name='none'};";
+ /* !J! should set joystick[] from MAX_JOYSTICKS */
l81.epoch = 0;
if (l81.L) lua_close(l81.L);
@@ -443,6 +570,7 @@ void resetProgram(void) {
luaopen_debug(l81.L);
setNumber("WIDTH",l81.width);
setNumber("HEIGHT",l81.height);
+ setNumber("JOYSTICKS", SDL_NumJoysticks());
luaL_loadbuffer(l81.L,initscript,strlen(initscript),"initscript");
lua_pcall(l81.L,0,0,0);
@@ -453,6 +581,8 @@ void resetProgram(void) {
setTableFieldNumber("mouse","xrel",0);
setTableFieldNumber("mouse","yrel",0);
+ initJoysticks(l81.fb);
+
/* Register API */
lua_pushcfunction(l81.L,fillBinding);
lua_setglobal(l81.L,"fill");
@@ -552,5 +682,6 @@ int main(int argc, char **argv) {
}
editorRun();
}
+ closeJoysticks(l81.fb);
return 0;
}
Something went wrong with that request. Please try again.