/* OnePAD - author: arcum42(@gmail.com) * Copyright (C) 2009 * * Based on ZeroPAD, author zerofrog@gmail.com * Copyright (C) 2006-2007 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "joystick.h" #include // sigaction ////////////////////////// // Joystick definitions // ////////////////////////// static u32 s_bSDLInit = false; void JoystickInfo::UpdateReleaseState() { vector::iterator itjoy = s_vgamePad.begin(); SDL_JoystickUpdate(); // Save everything in the vector s_vjoysticks. while (itjoy != s_vgamePad.end()) { (*itjoy)->SaveState(); ++itjoy; } } // opens handles to all possible joysticks void JoystickInfo::EnumerateJoysticks(vector &vjoysticks) { if (!s_bSDLInit) { // Tell SDL to catch event even if the windows isn't focussed SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_EVENTS) < 0) return; // WTF! Give me back the control of my system struct sigaction action = {0}; action.sa_handler = SIG_DFL; sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); SDL_JoystickEventState(SDL_QUERY); s_bSDLInit = true; } vector::iterator it = vjoysticks.begin(); // Delete everything in the vector vjoysticks. while (it != vjoysticks.end()) { delete *it; ++it; } vjoysticks.resize(SDL_NumJoysticks()); for (int i = 0; i < (int)vjoysticks.size(); ++i) { vjoysticks[i] = new JoystickInfo(); vjoysticks[i]->Init(i); } } void JoystickInfo::GenerateDefaultEffect() { for (int i = 0; i < NB_EFFECT; i++) { SDL_HapticEffect effect; memset(&effect, 0, sizeof(SDL_HapticEffect)); // 0 is safe default SDL_HapticDirection direction; direction.type = SDL_HAPTIC_POLAR; // We'll be using polar direction encoding. direction.dir[0] = 18000; effect.periodic.direction = direction; effect.periodic.period = 10; effect.periodic.magnitude = (Sint16)(conf->get_ff_intensity()); // Effect at maximum instensity effect.periodic.offset = 0; effect.periodic.phase = 18000; effect.periodic.length = 125; // 125ms feels quite near to original effect.periodic.delay = 0; effect.periodic.attack_length = 0; effects[i] = effect; } } void JoystickInfo::Rumble(int type, int pad) { if (type > 1) return; if (!(conf->pad_options[pad].forcefeedback)) return; if (haptic == NULL) return; if (first) { // If done multiple times, device memory will be filled first = 0; GenerateDefaultEffect(); /** Sine and triangle are quite probably the best, don't change that lightly and if you do * keep effects ordered by type **/ /** Effect for small motor **/ /** Sine seems to be the only effect making little motor from DS3/4 react * Intensity has pretty much no effect either(which is coherent with what is explain in hid_sony driver **/ effects[0].type = SDL_HAPTIC_SINE; effects_id[0] = SDL_HapticNewEffect(haptic, &effects[0]); if (effects_id[0] < 0) { fprintf(stderr, "ERROR: Effect is not uploaded! %s, id is %d\n", SDL_GetError(), effects_id[0]); } /** Effect for big motor **/ effects[1].type = SDL_HAPTIC_TRIANGLE; effects_id[1] = SDL_HapticNewEffect(haptic, &effects[1]); if (effects_id[1] < 0) { fprintf(stderr, "ERROR: Effect is not uploaded! %s, id is %d\n", SDL_GetError(), effects_id[1]); } } int id; id = effects_id[type]; if (SDL_HapticRunEffect(haptic, id, 1) != 0) { fprintf(stderr, "ERROR: Effect is not working! %s, id is %d\n", SDL_GetError(), id); } } void JoystickInfo::Destroy() { if (joy != NULL) { // Haptic must be closed before the joystick if (haptic != NULL) { SDL_HapticClose(haptic); haptic = NULL; } #if SDL_MINOR_VERSION >= 4 // Version before 2.0.4 are bugged, JoystickClose crashes randomly if (joy) SDL_JoystickClose(joy); #endif joy = NULL; } } bool JoystickInfo::Init(int id) { Destroy(); _id = id; joy = SDL_JoystickOpen(id); if (joy == NULL) { PAD_LOG("failed to open joystick %d\n", id); return false; } numaxes = SDL_JoystickNumAxes(joy); numbuttons = SDL_JoystickNumButtons(joy); numhats = SDL_JoystickNumHats(joy); devname = SDL_JoystickName(joy); vaxisstate.resize(numaxes); vbuttonstate.resize(numbuttons); vhatstate.resize(numhats); // Sixaxis, dualshock3 hack // Most buttons are actually axes due to analog pressure support. Only the first 4 buttons // are digital (select, start, l3, r3). To avoid conflict just forget the others. // Keep the 4 hat buttons too (usb driver). (left pressure does not work with recent kernel). Moreover the pressure // work sometime on half axis neg others time in fulll axis. So better keep them as button for the moment auto found_hack = devname.find("PLAYSTATION(R)3"); // FIXME: people need to restart the plugin to take the option into account. bool hack_enabled = (conf->pad_options[0].sixaxis_pressure) || (conf->pad_options[1].sixaxis_pressure); if (found_hack != string::npos && numaxes > 4 && hack_enabled) { numbuttons = 4; // (select, start, l3, r3) // Enable this hack in bluetooth too. It avoid to restart the onepad gui numbuttons += 4; // the 4 hat buttons } if (haptic == NULL) { if (!SDL_JoystickIsHaptic(joy)) { PAD_LOG("Haptic devices not supported!\n"); } else { haptic = SDL_HapticOpenFromJoystick(joy); first = true; } } //PAD_LOG("There are %d buttons, %d axises, and %d hats.\n", numbuttons, numaxes, numhats); return true; } void JoystickInfo::SaveState() { for (int i = 0; i < numbuttons; ++i) SetButtonState(i, SDL_JoystickGetButton(joy, i)); for (int i = 0; i < numaxes; ++i) SetAxisState(i, SDL_JoystickGetAxis(joy, i)); for (int i = 0; i < numhats; ++i) SetHatState(i, SDL_JoystickGetHat(joy, i)); } bool JoystickInfo::TestForce(float strength = 0.60) { // This code just use standard rumble to check that SDL handles the pad correctly! --3kinox if (haptic == NULL) return false; // Otherwise, core dump! SDL_HapticRumbleInit(haptic); // Make the haptic pad rumble 60% strength for half a second, shoudld be enough for user to see if it works or not if (SDL_HapticRumblePlay(haptic, strength, 400) != 0) { fprintf(stderr, "ERROR: Rumble is not working! %s\n", SDL_GetError()); return false; } return true; } bool JoystickInfo::PollButtons(u32 &pkey) { // MAKE sure to look for changes in the state!! for (int i = 0; i < GetNumButtons(); ++i) { int but = SDL_JoystickGetButton(GetJoy(), i); if (but != GetButtonState(i)) { // Pressure sensitive button are detected as both button (digital) and axes (analog). So better // drop the button to emulate the pressure sensiblity of the ds2 :) // Trick: detect the release of the button. It avoid all races condition between axes and buttons :) // If the button support pressure it will be detected as an axis when it is pressed. if (but) { SetButtonState(i, but); return false; } pkey = button_to_key(i); return true; } } return false; } bool JoystickInfo::PollAxes(u32 &pkey) { auto found_hack = devname.find("PLAYSTATION(R)3"); for (int i = 0; i < GetNumAxes(); ++i) { // Sixaxis, dualshock3 hack if (found_hack != string::npos) { // The analog mode of the hat button is quite erratic. Values can be in half- axis // or full axis... So better keep them as button for the moment -- gregory if (i >= 8 && i <= 11 && (conf->pad_options[pad].sixaxis_usb)) continue; // Disable accelerometer if ((i >= 4 && i <= 6)) continue; } s32 value = SDL_JoystickGetAxis(GetJoy(), i); s32 old_value = GetAxisState(i); if (abs(value - old_value) < 0x1000) continue; if (value != old_value) { PAD_LOG("Change in joystick %d: %d.\n", i, value); // There are several kinds of axes // Half+: 0 (release) -> 32768 // Half-: 0 (release) -> -32768 // Full (like dualshock 3): -32768 (release) ->32768 const s32 full_axis_ceil = -0x6FFF; const s32 half_axis_ceil = 0x1FFF; // Normally, old_value contains the release state so it can be used to detect the types of axis. bool is_full_axis = (old_value < full_axis_ceil); if ((!is_full_axis && abs(value) <= half_axis_ceil) || (is_full_axis && value <= full_axis_ceil)) // we don't want this { continue; } if ((!is_full_axis && abs(value) > half_axis_ceil) || (is_full_axis && value > full_axis_ceil)) { bool sign = (value < 0); pkey = axis_to_key(is_full_axis, sign, i); return true; } } } return false; } bool JoystickInfo::PollHats(u32 &pkey) { for (int i = 0; i < GetNumHats(); ++i) { int value = SDL_JoystickGetHat(GetJoy(), i); if ((value != GetHatState(i)) && (value != SDL_HAT_CENTERED)) { switch (value) { case SDL_HAT_UP: case SDL_HAT_RIGHT: case SDL_HAT_DOWN: case SDL_HAT_LEFT: pkey = hat_to_key(value, i); PAD_LOG("Hat Pressed!"); return true; default: break; } } } return false; } int JoystickInfo::GetHat(int key_to_axis) { return SDL_JoystickGetHat(GetJoy(), key_to_axis); } int JoystickInfo::GetButton(int key_to_button) { return SDL_JoystickGetButton(GetJoy(), key_to_button); } int JoystickInfo::GetAxisFromKey(int pad, int index) { return SDL_JoystickGetAxis(GetJoy(), key_to_axis(pad, index)); }