diff --git a/scripts/javascript/examples/joystick.js b/scripts/javascript/examples/joystick.js index b21862c1..81143236 100644 --- a/scripts/javascript/examples/joystick.js +++ b/scripts/javascript/examples/joystick.js @@ -11,11 +11,15 @@ joy.axismotion = function(which, axis, value) { echo("joystick " + which + " axis " + axis + " value " + value); -} +}; joy.button = function (which, button, state) { echo("joystick " + which + " button " + button + " state " + state); -} +}; + +joy.init_rumble("/dev/input/event9"); +joy.rumble(0); + diff --git a/src/include/joy_ctrl.h b/src/include/joy_ctrl.h index 5b77d63b..329b1aae 100644 --- a/src/include/joy_ctrl.h +++ b/src/include/joy_ctrl.h @@ -24,6 +24,20 @@ #include +#include + +#ifdef HAVE_LINUX + // joystick rumble +#include +#include +#include +#include +#include +#include +#include +#include +#endif + class JoyController : public SdlController { public: @@ -40,6 +54,17 @@ class JoyController : public SdlController { virtual int button_down(int device, int button); virtual int button_up(int device, int button); +#ifdef HAVE_LINUX + // joystick rumble + +#define N_EFFECTS 6 + bool init_rumble(char *devpath); + bool rumble(int intensity); + int rumble_fd; + struct ff_effect effects[N_EFFECTS]; + struct input_event play, stop; +#endif + private: SDL_Joystick *joy[4]; int num; @@ -48,6 +73,7 @@ class JoyController : public SdlController { int balls; int hats; + }; #endif diff --git a/src/include/jsparser_data.h b/src/include/jsparser_data.h index 085d63fe..c60397e4 100644 --- a/src/include/jsparser_data.h +++ b/src/include/jsparser_data.h @@ -322,7 +322,11 @@ JS(controller_activate); JS(js_mouse_grab); JS(js_vimo_open); JS(js_vimo_close); - +#ifdef HAVE_LINUX +// joystick rumble is supported only under linux so far... +JS(js_joy_init_rumble); +JS(js_joy_rumble); +#endif //////////////////////////////// // parent Layer methods diff --git a/src/joy_ctrl.cpp b/src/joy_ctrl.cpp index fb89e5ea..65816ea2 100644 --- a/src/joy_ctrl.cpp +++ b/src/joy_ctrl.cpp @@ -39,6 +39,10 @@ JS(js_joy_ctrl_constructor); DECLARE_CLASS_GC("JoystickController",js_joy_ctrl_class, js_joy_ctrl_constructor,js_ctrl_gc); JSFunctionSpec js_joy_ctrl_methods[] = { +#ifdef HAVE_LINUX + {"init_rumble", js_joy_init_rumble, 1}, + {"rumble", js_joy_rumble, 1}, +#endif {0} }; @@ -171,6 +175,194 @@ int JoyController::dispatch() { return 0; } + +#ifdef HAVE_LINUX + + +#define BITS_PER_LONG (sizeof(long) * 8) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<> OFF(bit)) & 1) + + +static char* effect_names[] = { + (const char*)"Sine vibration", + (const char*)"Constant Force", + (const char*)"Spring Condition", + (const char*)"Damping Condition", + (const char*)"Strong Rumble", + (const char*)"Weak Rumble" +}; + +bool JoyController::init_rumble(char *devfile) { + unsigned long features[4]; + int n_effects; /* Number of effects the device can play at the same time */ + int i; + + /* Open device */ + rumble_fd = open(devfile, O_RDWR); + if (rumble_fd == -1) { + error("Cannot open rumble event device"); + return(false); + } + + act("Joystick rumble device open"); + + /* Query device */ + if (ioctl(rumble_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features) == -1) { + perror("Ioctl query"); + exit(1); + } + if (ioctl(rumble_fd, EVIOCGEFFECTS, &n_effects) == -1) { + error("Error on IOctl to get number of effects"); + } + + /* download a periodic sinusoidal effect */ + effects[0].type = FF_PERIODIC; + effects[0].id = -1; + effects[0].u.periodic.waveform = FF_SINE; + effects[0].u.periodic.period = 0.1*0x100; /* 0.1 second */ + effects[0].u.periodic.magnitude = 0x4000; /* 0.5 * Maximum magnitude */ + effects[0].u.periodic.offset = 0; + effects[0].u.periodic.phase = 0; + effects[0].direction = 0x4000; /* Along X axis */ + effects[0].u.periodic.envelope.attack_length = 0x100; + effects[0].u.periodic.envelope.attack_level = 0; + effects[0].u.periodic.envelope.fade_length = 0x100; + effects[0].u.periodic.envelope.fade_level = 0; + effects[0].trigger.button = 0; + effects[0].trigger.interval = 0; + effects[0].replay.length = 20000; /* 20 seconds */ + effects[0].replay.delay = 0; + + if (ioctl(rumble_fd, EVIOCSFF, &effects[0]) == -1) { + error("Error uploading effect 0"); + } + + /* download a constant effect */ + effects[1].type = FF_CONSTANT; + effects[1].id = -1; + effects[1].u.constant.level = 0x2000; /* Strength : 25 % */ + effects[1].direction = 0x6000; /* 135 degrees */ + effects[1].u.constant.envelope.attack_length = 0x100; + effects[1].u.constant.envelope.attack_level = 0; + effects[1].u.constant.envelope.fade_length = 0x100; + effects[1].u.constant.envelope.fade_level = 0; + effects[1].trigger.button = 0; + effects[1].trigger.interval = 0; + effects[1].replay.length = 20000; /* 20 seconds */ + effects[1].replay.delay = 0; + + if (ioctl(rumble_fd, EVIOCSFF, &effects[1]) == -1) { + error("Error uploading effect 1"); + } + + /* download an condition spring effect */ + effects[2].type = FF_SPRING; + effects[2].id = -1; + effects[2].u.condition[0].right_saturation = 0x7fff; + effects[2].u.condition[0].left_saturation = 0x7fff; + effects[2].u.condition[0].right_coeff = 0x2000; + effects[2].u.condition[0].left_coeff = 0x2000; + effects[2].u.condition[0].deadband = 0x0; + effects[2].u.condition[0].center = 0x0; + effects[2].u.condition[1] = effects[2].u.condition[0]; + effects[2].trigger.button = 0; + effects[2].trigger.interval = 0; + effects[2].replay.length = 20000; /* 20 seconds */ + effects[2].replay.delay = 0; + + if (ioctl(rumble_fd, EVIOCSFF, &effects[2]) == -1) { + error("Error uploading effect 2"); + } + + /* download an condition damper effect */ + effects[3].type = FF_DAMPER; + effects[3].id = -1; + effects[3].u.condition[0].right_saturation = 0x7fff; + effects[3].u.condition[0].left_saturation = 0x7fff; + effects[3].u.condition[0].right_coeff = 0x2000; + effects[3].u.condition[0].left_coeff = 0x2000; + effects[3].u.condition[0].deadband = 0x0; + effects[3].u.condition[0].center = 0x0; + effects[3].u.condition[1] = effects[3].u.condition[0]; + effects[3].trigger.button = 0; + effects[3].trigger.interval = 0; + effects[3].replay.length = 20000; /* 20 seconds */ + effects[3].replay.delay = 0; + + if (ioctl(rumble_fd, EVIOCSFF, &effects[3]) == -1) { + error("Error uploading effect 3"); + } + + /* a strong rumbling effect */ + effects[4].type = FF_RUMBLE; + effects[4].id = -1; + effects[4].u.rumble.strong_magnitude = 0x8000; + effects[4].u.rumble.weak_magnitude = 0; + effects[4].replay.length = 5000; + effects[4].replay.delay = 1000; + + if (ioctl(rumble_fd, EVIOCSFF, &effects[4]) == -1) { + error("Error uploading effect 4"); + } + + /* a weak rumbling effect */ + effects[5].type = FF_RUMBLE; + effects[5].id = -1; + effects[5].u.rumble.strong_magnitude = 0; + effects[5].u.rumble.weak_magnitude = 0xc000; + effects[5].replay.length = 5000; + effects[5].replay.delay = 0; + + if (ioctl(rumble_fd, EVIOCSFF, &effects[5]) == -1) { + error("Error uploading effect 5"); + } + + act("5 rumble effects initialised"); + return(true); +} + +bool JoyController::rumble(int intensity) { + + int i; + if(intensity <0) { + /* Stop the effects */ + for (i=0; i N_EFFECTS) { + error("effect %u is out of bounds",intensity); + return(false); + } + + play.type = EV_FF; + play.code = effects[intensity].id; + play.value = 1; + + if (write(rumble_fd, (const void*) &play, sizeof(play)) == -1) { + error("Error playing joystick rumble effect %u", intensity); + return(false); + } + + return(true); + +} +#endif + + + + JS(js_joy_ctrl_constructor) { func("%u:%s:%s",__LINE__,__FILE__,__FUNCTION__); char excp_msg[MAX_ERR_MSG + 1]; @@ -205,4 +397,40 @@ error: delete joy; return JS_FALSE; } +#ifdef HAVE_LINUX +// joystick rumble is supported only under linux so far... +JS(js_joy_init_rumble) { + func("%s",__PRETTY_FUNCTION__); + + JoyController *joy = (JoyController*) JS_GetPrivate(cx, obj); + if(!joy) JS_ERROR("JOY code data is NULL"); + + JS_CHECK_ARGC(1); + + char *devfile; + JS_ARG_STRING(devfile,0); + + if( joy->init_rumble(devfile) ) + act("Joystick controller opened rumble device %s", devfile); + + return JS_TRUE; +} + +JS(js_joy_rumble) { + func("%s", __PRETTY_FUNCTION__); + + JoyController *joy = (JoyController*) JS_GetPrivate(cx, obj); + if(!joy) JS_ERROR("JOY code data is NULL"); + + JS_CHECK_ARGC(1); + + uint16_t val; + JS_ValueToUint16(cx, argv[0], &val); + + joy->rumble(val); + + return JS_TRUE; +} + +#endif diff --git a/src/kbd_ctrl.cpp b/src/kbd_ctrl.cpp index 82bbb2a9..09dc207e 100644 --- a/src/kbd_ctrl.cpp +++ b/src/kbd_ctrl.cpp @@ -180,4 +180,3 @@ int KbdController::dispatch() { } return key_event(state, shift, ctrl, alt, num, keyname); } -