/* Copyright (C) 2007, 2010 - Bit-Blot This file is part of Aquaria. Aquaria 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 "Core.h" #if defined(BBGE_BUILD_WINDOWS) && defined(BBGE_BUILD_XINPUT) #include "Xinput.h" #if defined(BBGE_BUILD_DELAYXINPUT) #include #endif /* HRESULT (WINAPI *XInputGetState)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter) = 0; if ( (winp.hInstDI = LoadLibrary( "dinput.dll" )) == 0 ) if (!pDirectInput8Create) { pDirectInput8Create = (HRESULT (__stdcall *)(HINSTANCE, DWORD ,REFIID, LPVOID *, LPUNKNOWN)) GetProcAddress(winp.hInstDI,"DirectInput8Create"); if (!pDirectInput8Create) { error(L"Couldn't get DI proc addr\n"); } } bool importXInput() { } */ bool tryXInput() { __try { XINPUT_STATE xinp; XInputGetState(0, &xinp); } __except (EXCEPTION_EXECUTE_HANDLER) { return false; } return true; } #endif #ifdef __LINUX__ #include #include #include #include #include #include #include #define BITS_TO_LONGS(x) \ (((x) + 8 * sizeof (unsigned long) - 1) / (8 * sizeof (unsigned long))) #define AQUARIA_BITS_PER_LONG (sizeof(long) * 8) #define AQUARIA_OFF(x) ((x)%AQUARIA_BITS_PER_LONG) #define AQUARIA_BIT(x) (1UL<> AQUARIA_OFF(bit)) & 1) #endif Joystick::Joystick() { xinited = false; stickIndex = -1; #ifdef BBGE_BUILD_SDL # ifdef BBGE_BUILD_SDL2 sdl_controller = NULL; sdl_haptic = NULL; # endif sdl_joy = NULL; #endif #if defined(__LINUX__) && !defined(BBGE_BUILD_SDL2) eventfd = -1; effectid = -1; #endif inited = false; for (int i = 0; i < maxJoyBtns; i++) { buttons[i] = UP; } deadZone1 = 0.3; deadZone2 = 0.3; clearRumbleTime= 0; leftThumb = rightThumb = false; leftTrigger = rightTrigger = 0; rightShoulder = leftShoulder = false; dpadRight = dpadLeft = dpadUp = dpadDown = false; btnStart = false; btnSelect = false; s1ax = 0; s1ay = 1; s2ax = 4; s2ay = 3; } void Joystick::init(int stick) { #if defined(BBGE_BUILD_SDL) || defined(__LINUX__) std::ostringstream os; #endif #ifdef BBGE_BUILD_SDL stickIndex = stick; const int numJoy = SDL_NumJoysticks(); os << "Found [" << numJoy << "] joysticks"; debugLog(os.str()); if (numJoy > stick) { #ifdef BBGE_BUILD_SDL2 if (SDL_IsGameController(stick)) { sdl_controller = SDL_GameControllerOpen(stick); if (sdl_controller) sdl_joy = SDL_GameControllerGetJoystick(sdl_controller); } if (!sdl_joy) sdl_joy = SDL_JoystickOpen(stick); if (sdl_joy && SDL_JoystickIsHaptic(sdl_joy)) { sdl_haptic = SDL_HapticOpenFromJoystick(sdl_joy); bool rumbleok = false; if (sdl_haptic && SDL_HapticRumbleSupported(sdl_haptic)) rumbleok = (SDL_HapticRumbleInit(sdl_haptic) == 0); if (!rumbleok) { SDL_HapticClose(sdl_haptic); sdl_haptic = NULL; } } #endif if (!sdl_joy) sdl_joy = SDL_JoystickOpen(stick); if (sdl_joy) { inited = true; #ifdef BBGE_BUILD_SDL2 debugLog(std::string("Initialized Joystick [") + std::string(SDL_JoystickName(sdl_joy)) + std::string("]")); if (sdl_controller) debugLog(std::string("Joystick is a Game Controller")); if (sdl_haptic) debugLog(std::string("Joystick has force feedback support")); #else debugLog(std::string("Initialized Joystick [") + std::string(SDL_JoystickName(stick)) + std::string("]")); #endif } else { std::ostringstream os; os << "Failed to init Joystick [" << stick << "]"; debugLog(os.str()); } } else { debugLog("Not enough Joystick(s) found"); } #endif #if defined(__LINUX__) && !defined(BBGE_BUILD_SDL2) os.seekp(0); os << "AQUARIA_EVENT_JOYSTICK" << stick; std::string envkey = os.str(); const char* evdevice = getenv(envkey.c_str()); if (evdevice != NULL) { eventfd = open(evdevice, O_RDWR, 0); if (eventfd < 0) { debugLog(std::string("Could not open rumble device [") + evdevice + "]: " + strerror(errno)); } else { debugLog(std::string("Successfully opened rumble device [") + evdevice + "]"); unsigned long features[BITS_TO_LONGS(FF_CNT)]; if (ioctl(eventfd, EVIOCGBIT(EV_FF, sizeof(features)), features) == -1) { debugLog(std::string("Cannot query joystick/gamepad features: ") + strerror(errno)); close(eventfd); eventfd = -1; } else if (!test_bit(FF_RUMBLE, features)) { debugLog("Rumble is not supported by your gamepad/joystick."); close(eventfd); eventfd = -1; } } } else { std::cout << "Environment varialbe " << envkey << " is not set.\n" "Set this environment variable to the device file that shall be used for joystick number " << stick << " in order to enable rumble support.\n" "Example:\n" "\texport " << envkey << "=/dev/input/event6\n\n"; } #endif #ifdef BBGE_BUILD_XINPUT debugLog("about to init xinput"); xinited = tryXInput(); if (!xinited) debugLog("XInput not found, not installed?"); debugLog("after catch"); #if !defined(BBGE_BUILD_SDL) inited = xinited; #endif #endif } void Joystick::shutdown() { #if defined(__LINUX__) && !defined(BBGE_BUILD_SDL2) if (eventfd >= 0) { if (effectid != -1 && ioctl(eventfd, EVIOCRMFF, effectid) == -1) { debugLog(std::string("Remove rumble effect: ") + strerror(errno)); } close(eventfd); eventfd = -1; } #endif #ifdef BBGE_BUILD_SDL #ifdef BBGE_BUILD_SDL2 if (sdl_haptic) { SDL_HapticClose(sdl_haptic); sdl_haptic = 0; } if (sdl_controller) { SDL_GameControllerClose(sdl_controller); sdl_controller = 0; sdl_joy = 0; // SDL_GameControllerClose() frees this } #endif if (sdl_joy) { SDL_JoystickClose(sdl_joy); sdl_joy = 0; } #endif } void Joystick::rumble(float leftMotor, float rightMotor, float time) { if (core->joystickEnabled && inited) { #ifdef BBGE_BUILD_SDL2 if (sdl_haptic) { const float power = (leftMotor + rightMotor) / 2.0f; if ((power > 0.0f) && (time > 0.0f)) { clearRumbleTime = time; SDL_HapticRumblePlay(sdl_haptic, power, (Uint32) (time * 1000.0f)); } else { clearRumbleTime = -1; SDL_HapticRumbleStop(sdl_haptic); } } #elif defined(BBGE_BUILD_WINDOWS) && defined(BBGE_BUILD_XINPUT) XINPUT_VIBRATION vib; vib.wLeftMotorSpeed = WORD(leftMotor*65535); vib.wRightMotorSpeed = WORD(rightMotor*65535); clearRumbleTime = time; DWORD d = XInputSetState(0, &vib); if (d == ERROR_SUCCESS) { //debugLog("success"); } else if (d == ERROR_DEVICE_NOT_CONNECTED) { //debugLog("joystick not connected"); } else { //unknown error } #elif defined(__LINUX__) if (eventfd >= 0) { struct ff_effect effect; struct input_event event; effect.type = FF_RUMBLE; effect.id = effectid; effect.direction = 0; effect.trigger.button = 0; effect.trigger.interval = 0; effect.replay.length = (uint16_t) (time * 1000); effect.replay.delay = 0; if (leftMotor > rightMotor) { effect.u.rumble.strong_magnitude = (uint16_t) (leftMotor * 0xffff); effect.u.rumble.weak_magnitude = (uint16_t) (rightMotor * 0xffff); } else { effect.u.rumble.strong_magnitude = (uint16_t) (rightMotor * 0xffff); effect.u.rumble.weak_magnitude = (uint16_t) (leftMotor * 0xffff); } if (ioctl(eventfd, EVIOCSFF, &effect) == -1) { debugLog(std::string("Upload rumble effect: ") + strerror(errno)); return; } event.time.tv_sec = 0; event.time.tv_usec = 0; event.type = EV_FF; event.code = effectid = effect.id; if (leftMotor == 0 && rightMotor == 0) { event.value = 0; } else { event.value = 1; } if (write(eventfd, (const void*) &event, sizeof(event)) == -1) { debugLog(std::string("Play rumble effect: ") + strerror(errno)); } } #endif } } void Joystick::callibrate(Vector &calvec, float deadZone) { //float len = position.getLength2D(); if (calvec.isLength2DIn(deadZone)) { calvec = Vector(0,0,0); } else { if (!calvec.isZero()) { Vector pos2 = calvec; pos2.setLength2D(deadZone); calvec -= pos2; float mult = 1.0f/float(1.0f-deadZone); calvec.x *= mult; calvec.y *= mult; if (calvec.x > 1) calvec.x = 1; else if (calvec.x < -1) calvec.x = -1; if (calvec.y > 1) calvec.y = 1; else if (calvec.y < -1) calvec.y = -1; } } } void Joystick::update(float dt) { #ifdef BBGE_BUILD_SDL if (core->joystickEnabled && inited && sdl_joy && stickIndex != -1) { #ifdef BBGE_BUILD_SDL2 if (!SDL_JoystickGetAttached(sdl_joy)) { debugLog("Lost Joystick"); if (sdl_haptic) { SDL_HapticClose(sdl_haptic); sdl_haptic = NULL; } if (!sdl_controller) SDL_JoystickClose(sdl_joy); else { SDL_GameControllerClose(sdl_controller); sdl_controller = NULL; } sdl_joy = NULL; return; } if (sdl_controller) { Sint16 xaxis = SDL_GameControllerGetAxis(sdl_controller, SDL_CONTROLLER_AXIS_LEFTX); Sint16 yaxis = SDL_GameControllerGetAxis(sdl_controller, SDL_CONTROLLER_AXIS_LEFTY); position.x = float(xaxis)/32768.0f; position.y = float(yaxis)/32768.0f; Sint16 xaxis2 = SDL_GameControllerGetAxis(sdl_controller, SDL_CONTROLLER_AXIS_RIGHTX); Sint16 yaxis2 = SDL_GameControllerGetAxis(sdl_controller, SDL_CONTROLLER_AXIS_RIGHTY); rightStick.x = float(xaxis2)/32768.0f; rightStick.y = float(yaxis2)/32768.0f; } else { Sint16 xaxis = SDL_JoystickGetAxis(sdl_joy, s1ax); Sint16 yaxis = SDL_JoystickGetAxis(sdl_joy, s1ay); position.x = float(xaxis)/32768.0f; position.y = float(yaxis)/32768.0f; Sint16 xaxis2 = SDL_JoystickGetAxis(sdl_joy, s2ax); Sint16 yaxis2 = SDL_JoystickGetAxis(sdl_joy, s2ay); rightStick.x = float(xaxis2)/32768.0f; rightStick.y = float(yaxis2)/32768.0f; } #else if (!SDL_JoystickOpened(stickIndex)) { debugLog("Lost Joystick"); sdl_joy = NULL; return; } Sint16 xaxis = SDL_JoystickGetAxis(sdl_joy, s1ax); Sint16 yaxis = SDL_JoystickGetAxis(sdl_joy, s1ay); position.x = xaxis/32768.0f; position.y = yaxis/32768.0f; Sint16 xaxis2 = SDL_JoystickGetAxis(sdl_joy, s2ax); Sint16 yaxis2 = SDL_JoystickGetAxis(sdl_joy, s2ay); rightStick.x = xaxis2/32768.0f; rightStick.y = yaxis2/32768.0f; #endif /* std::ostringstream os; os << "joy(" << position.x << ", " << position.y << ")"; debugLog(os.str()); */ callibrate(position, deadZone1); callibrate(rightStick, deadZone2); /* std::ostringstream os2; os2 << "joy2(" << position.x << ", " << position.y << ")"; debugLog(os2.str()); */ #ifdef BBGE_BUILD_SDL2 if (sdl_controller) { for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) buttons[i] = SDL_GameControllerGetButton(sdl_controller, (SDL_GameControllerButton)i)?DOWN:UP; for (int i = SDL_CONTROLLER_BUTTON_MAX; i < maxJoyBtns; i++) buttons[i] = UP; } else { for (int i = 0; i < maxJoyBtns; i++) buttons[i] = SDL_JoystickGetButton(sdl_joy, i)?DOWN:UP; } #else for (int i = 0; i < maxJoyBtns; i++) buttons[i] = SDL_JoystickGetButton(sdl_joy, i)?DOWN:UP; #endif /* unsigned char btns[maxJoyBtns]; glfwGetJoystickButtons(GLFW_JOYSTICK_1, btns, maxJoyBtns); for (int i = 0; i < maxJoyBtns; i++) { if (btns[i] == GLFW_PRESS) buttons[i] = DOWN; else buttons[i] = UP; } */ } #endif if (clearRumbleTime >= 0) { clearRumbleTime -= dt; if (clearRumbleTime <= 0) { rumble(0,0,0); } } #if defined(BBGE_BUILD_WINDOWS) && defined(BBGE_BUILD_XINPUT) if (inited && xinited) { XINPUT_STATE xinp; XInputGetState(0, &xinp); leftTrigger = float(xinp.Gamepad.bLeftTrigger)/255.0f; rightTrigger = float(xinp.Gamepad.bRightTrigger)/255.0f; leftShoulder = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER; rightShoulder = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER; leftThumb = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB; rightThumb = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB; dpadUp = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP; dpadDown = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN; dpadLeft = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT; dpadRight = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT; #if !defined(BBGE_BUILD_SDL) buttons[0] = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_A?DOWN:UP; buttons[1] = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_B?DOWN:UP; buttons[2] = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_X?DOWN:UP; buttons[3] = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_Y?DOWN:UP; position = Vector(xinp.Gamepad.sThumbLX, xinp.Gamepad.sThumbLY)/32768.0f; position.y = -rightStick.y; rightStick = Vector(xinp.Gamepad.sThumbRX, xinp.Gamepad.sThumbRY)/32768.0f; rightStick.y = -rightStick.y; callibrate(position, deadZone1); callibrate(rightStick, deadZone2); #endif btnStart = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_START; btnSelect = xinp.Gamepad.wButtons & XINPUT_GAMEPAD_BACK; } #endif /* std::ostringstream os; os << "j-pos(" << position.x << ", " << position.y << " - b0[" << buttons[0] << "]) - len[" << len << "]"; debugLog(os.str()); */ } bool Joystick::anyButton() { for (int i = 0; i < maxJoyBtns; i++) { if (buttons[i]) return true; } return false; }