diff --git a/Windows/Gopher/ConfigFile.cpp b/Windows/Gopher/ConfigFile.cpp index 5fe7343..65a8d4d 100644 --- a/Windows/Gopher/ConfigFile.cpp +++ b/Windows/Gopher/ConfigFile.cpp @@ -135,13 +135,10 @@ void ConfigFile::ExtractKeys() outfile << "GAMEPAD_TRIGGER_LEFT = 0" << std::endl; outfile << "GAMEPAD_TRIGGER_RIGHT = 0" << std::endl; outfile << "\n" << std::endl; - outfile << "# ADVANCED CONFIGURATION SETTINGS" << std::endl; outfile << "# ALLOWED CURSOR SPEEDS, FIRST WILL BE CHOSEN BY DEFAULT. VALUES > 1.0 WILL BE IGNORED. NO SPACES." << std::endl; outfile << "CURSOR_SPEED = ULTRALOW=0.005,LOW=0.015,MED=0.025,HIGH=0.04" << std::endl; outfile << "# SET ACCELERATION FACTOR FOR NON-LINEAR CURSOR SPEED" << std::endl; outfile << "# ACCELERATION_FACTOR = 3" << std::endl; - outfile << "# Swaps the function of the thumbsticks. Set to 0 for default behavior or set to 1 to have the mouse movement on the right stick and scrolling on the left stick." << std::endl; - outfile << "SWAP_THUMBSTICKS = 0" << std::endl; // End config dump outfile.close(); diff --git a/Windows/Gopher/Gopher.cpp b/Windows/Gopher/Gopher.cpp index 32b47f0..f008336 100644 --- a/Windows/Gopher/Gopher.cpp +++ b/Windows/Gopher/Gopher.cpp @@ -184,9 +184,6 @@ void Gopher::loadConfigFile() } speed = speeds[0]; // Initialize the speed to the first speed stored. TODO: Set the speed to a saved speed that was last used when the application was closed last. - // Swap stick functions - SWAP_THUMBSTICKS = strtol(cfg.getValueOfKey("SWAP_THUMBSTICKS").c_str(), 0, 0); - // Set the initial window visibility setWindowVisibility(_hidden); } @@ -499,21 +496,8 @@ void Gopher::handleMouseMovement() POINT cursor; GetCursorPos(&cursor); - short tx; - short ty; - - if (SWAP_THUMBSTICKS == 0) - { - // Use left stick - tx = _currentState.Gamepad.sThumbLX; - ty = _currentState.Gamepad.sThumbLY; - } - else - { - // Use right stick - tx = _currentState.Gamepad.sThumbRX; - ty = _currentState.Gamepad.sThumbRY; - } + short tx = _currentState.Gamepad.sThumbLX; + short ty = _currentState.Gamepad.sThumbLY; float x = cursor.x + _xRest; float y = cursor.y + _yRest; @@ -544,22 +528,9 @@ void Gopher::handleMouseMovement() // Controls the scroll wheel movement by reading the right thumbstick. void Gopher::handleScrolling() { - float tx; - float ty; + float tx = getDelta(_currentState.Gamepad.sThumbRX); + float ty = getDelta(_currentState.Gamepad.sThumbRY); - if (SWAP_THUMBSTICKS == 0) - { - // Use right stick - tx = getDelta(_currentState.Gamepad.sThumbRX); - ty = getDelta(_currentState.Gamepad.sThumbRY); - } - else - { - // Use left stick - tx = getDelta(_currentState.Gamepad.sThumbLX); - ty = getDelta(_currentState.Gamepad.sThumbLY); - } - // Handle dead zone float magnitude = sqrt(tx * tx + ty * ty); diff --git a/Windows/Gopher/Gopher.h b/Windows/Gopher/Gopher.h index bf4aebe..ae96eb8 100644 --- a/Windows/Gopher/Gopher.h +++ b/Windows/Gopher/Gopher.h @@ -24,7 +24,6 @@ class Gopher float SCROLL_SPEED = 0.1f; // Speed at which you scroll. const int FPS = 150; // Update rate of the main Gopher loop. Interpreted as cycles-per-second. const int SLEEP_AMOUNT = 1000 / FPS; // Number of milliseconds to sleep per iteration. - int SWAP_THUMBSTICKS = 0; // Swaps the function of the thumbsticks when not equal to 0. XINPUT_STATE _currentState; diff --git a/Windows/Gopher/Gopher/CXBOXController.cpp b/Windows/Gopher/Gopher/CXBOXController.cpp new file mode 100644 index 0000000..2edf05e --- /dev/null +++ b/Windows/Gopher/Gopher/CXBOXController.cpp @@ -0,0 +1,37 @@ +#include "CXBOXController.h" + +CXBOXController::CXBOXController(int playerNumber) +{ + _controllerNum = playerNumber - 1; //set number +} + +XINPUT_STATE CXBOXController::GetState() +{ + ZeroMemory(&this->_controllerState, sizeof(XINPUT_STATE)); + XInputGetState(_controllerNum, &this->_controllerState); + return _controllerState; +} + +bool CXBOXController::IsConnected() +{ + ZeroMemory(&this->_controllerState, sizeof(XINPUT_STATE)); + DWORD Result = XInputGetState(_controllerNum, &this->_controllerState); + + return (Result == ERROR_SUCCESS); +} + +void CXBOXController::Vibrate(int leftVal, int rightVal) +{ + // Create a Vibraton State + XINPUT_VIBRATION Vibration; + + // Zeroise the Vibration + ZeroMemory(&Vibration, sizeof(XINPUT_VIBRATION)); + + // Set the Vibration Values + Vibration.wLeftMotorSpeed = leftVal; + Vibration.wRightMotorSpeed = rightVal; + + // Vibrate the controller + XInputSetState(_controllerNum, &Vibration); +} \ No newline at end of file diff --git a/Windows/Gopher/Gopher/CXBOXController.h b/Windows/Gopher/Gopher/CXBOXController.h new file mode 100644 index 0000000..01fe634 --- /dev/null +++ b/Windows/Gopher/Gopher/CXBOXController.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +class CXBOXController +{ +private: + XINPUT_STATE _controllerState; + int _controllerNum; +public: + CXBOXController(int playerNumber); + XINPUT_STATE GetState(); + bool IsConnected(); + void CXBOXController::Vibrate(int leftVal, int rightVal); +}; diff --git a/Windows/Gopher/Gopher/Gopher.cpp b/Windows/Gopher/Gopher/Gopher.cpp new file mode 100644 index 0000000..41bdd2f --- /dev/null +++ b/Windows/Gopher/Gopher/Gopher.cpp @@ -0,0 +1,312 @@ +#include "Gopher.h" + +void inputKeyboard(WORD cmd, DWORD flag) +{ + INPUT input; + input.type = INPUT_KEYBOARD; + input.ki.wScan = 0; + input.ki.time = 0; + input.ki.dwExtraInfo = 0; + input.ki.wVk = cmd; + input.ki.dwFlags = flag; + SendInput(1, &input, sizeof(INPUT)); +} + +void inputKeyboardDown(WORD cmd) +{ + inputKeyboard(cmd, 0); +} + +void inputKeyboardUp(WORD cmd) +{ + inputKeyboard(cmd, KEYEVENTF_KEYUP); +} + +void mouseEvent(DWORD dwFlags, DWORD mouseData=0) +{ + INPUT input; + input.type = INPUT_MOUSE; + input.mi.mouseData = 0; + input.mi.dwFlags = dwFlags; + input.mi.time = 0; + SendInput(1, &input, sizeof(INPUT)); +} + +Gopher::Gopher(CXBOXController * controller) + : _controller(controller) +{ +} + +void Gopher::loop() { + Sleep(SLEEP_AMOUNT); + + _currentState = _controller->GetState(); + + handleDisableButton(); + + if (_disabled) + { + return; + } + + handleMouseMovement(); + handleScrolling(); + handleTriggers(VK_SPACE,VK_BACK); + + mapMouseClick(XINPUT_GAMEPAD_A, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP); + mapMouseClick(XINPUT_GAMEPAD_X, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP); + mapMouseClick(XINPUT_GAMEPAD_LEFT_THUMB, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP); + + mapKeyboard(XINPUT_GAMEPAD_DPAD_UP, VK_UP); + mapKeyboard(XINPUT_GAMEPAD_DPAD_DOWN, VK_DOWN); + mapKeyboard(XINPUT_GAMEPAD_DPAD_LEFT, VK_LEFT); + mapKeyboard(XINPUT_GAMEPAD_DPAD_RIGHT, VK_RIGHT); + + setXboxClickState(XINPUT_GAMEPAD_Y); + if (_xboxClickIsDown[XINPUT_GAMEPAD_Y]) + { + toggleWindowVisibility(); + } + + mapKeyboard(XINPUT_GAMEPAD_START, VK_LWIN); + mapKeyboard(XINPUT_GAMEPAD_B, VK_RETURN); + mapKeyboard(XINPUT_GAMEPAD_RIGHT_SHOULDER, VK_BROWSER_FORWARD); + mapKeyboard(XINPUT_GAMEPAD_LEFT_SHOULDER, VK_BROWSER_BACK); + mapKeyboard(XINPUT_GAMEPAD_BACK, VK_BROWSER_REFRESH); + + //Left and Right Shoulders will change speed. + setXboxClickState(XINPUT_GAMEPAD_LEFT_SHOULDER | XINPUT_GAMEPAD_RIGHT_SHOULDER); + if (_xboxClickIsDown[XINPUT_GAMEPAD_LEFT_SHOULDER | XINPUT_GAMEPAD_RIGHT_SHOULDER]) { + + if (speed == SPEED_LOW) + { + speed = SPEED_MED; + _controller->Vibrate(0, 30000); + Sleep(400); + _controller->Vibrate(0, 0); + } + else if (speed == SPEED_MED) + { + speed = SPEED_HIGH; + _controller->Vibrate(0, 65000); + Sleep(400); + _controller->Vibrate(0, 0); + } + else if (speed == SPEED_HIGH) + { + speed = SPEED_LOW; + _controller->Vibrate(0, 10000); + Sleep(400); + _controller->Vibrate(0, 0); + } + } +} + +void Gopher::handleDisableButton() +{ + //Select + Start will disable. + setXboxClickState(XINPUT_GAMEPAD_BACK | XINPUT_GAMEPAD_START); + if (_xboxClickIsDown[XINPUT_GAMEPAD_BACK | XINPUT_GAMEPAD_START]) + { + _disabled = !_disabled; + + if (_disabled) { + _controller->Vibrate(10000, 10000); + Sleep(400); + _controller->Vibrate(0, 0); + } + else { + _controller->Vibrate(65000, 65000); + Sleep(400); + _controller->Vibrate(0, 0); + } + } +} + +void Gopher::toggleWindowVisibility() +{ + _hidden = !_hidden; + + if (_hidden) + { + HWND hWnd = GetConsoleWindow(); + ShowWindow(hWnd, SW_HIDE); + printf("Window hidden\n"); + } + else + { + HWND hWnd = GetConsoleWindow(); + ShowWindow(hWnd, SW_SHOW); + printf("Window unhidden\n"); + } +} + +template +int sgn(T val) +{ + return (T(0) < val) - (val < T(0)); +} + +float Gopher::getDelta(short t) +{ + //filter non-32768 and 32767, wireless ones can glitch sometimes and send it to the edge of the screen, it'll toss out some HUGE integer even when it's centered + if (t > 32767) t = 0; + if (t < -32768) t = 0; + + float delta = 0.0; + + if (abs(t) > DEAD_ZONE) + { + t = sgn(t) * (abs(t) - DEAD_ZONE); + delta = speed * t / FPS; + } + + return delta; +} + +void Gopher::handleMouseMovement() +{ + POINT cursor; + GetCursorPos(&cursor); + + short tx = _currentState.Gamepad.sThumbLX; + short ty = _currentState.Gamepad.sThumbLY; + + float x = cursor.x + _xRest; + float y = cursor.y + _yRest; + + float dx = getDelta(tx); + float dy = getDelta(ty); + + x += dx; + _xRest = x - (float)((int)x); + + y -= dy; + _yRest = y - (float)((int)y); + + SetCursorPos((int)x, (int)y); //after all click input processing +} + +void Gopher::handleScrolling() +{ + bool holdScrollUp = _currentState.Gamepad.sThumbRY > SCROLL_DEAD_ZONE; + bool holdScrollDown = _currentState.Gamepad.sThumbRY < -SCROLL_DEAD_ZONE; + + if (holdScrollUp) + { + INPUT input; + input.type = INPUT_MOUSE; + input.mi.mouseData = SCROLL_SPEED; + input.mi.dwFlags = MOUSEEVENTF_WHEEL; + input.mi.time = 0; + SendInput(1, &input, sizeof(INPUT)); + } + + if (holdScrollDown) + { + INPUT input; + input.type = INPUT_MOUSE; + input.mi.mouseData = -SCROLL_SPEED; + input.mi.dwFlags = MOUSEEVENTF_WHEEL; + input.mi.time = 0; + SendInput(1, &input, sizeof(INPUT)); + } +} + +void Gopher::handleTriggers(WORD lKey, WORD rKey) +{ + bool lTriggerIsDown = _currentState.Gamepad.bLeftTrigger > TRIGGER_DEAD_ZONE; + bool rTriggerIsDown = _currentState.Gamepad.bRightTrigger > TRIGGER_DEAD_ZONE; + + + if(lTriggerIsDown != _lTriggerPrevious) + { + _lTriggerPrevious = lTriggerIsDown; + if (lTriggerIsDown) + { + inputKeyboardDown(lKey); + }else + { + inputKeyboardUp(lKey); + } + } + + if(rTriggerIsDown != _rTriggerPrevious) + { + _rTriggerPrevious = rTriggerIsDown; + if (rTriggerIsDown) + { + inputKeyboardDown(rKey); + } + else + { + inputKeyboardUp(rKey); + } + } +} + +void Gopher::setXboxClickState(DWORD STATE) +{ + _xboxClickIsDown[STATE] = false; + _xboxClickIsUp[STATE] = false; + + if (!this->xboxClickStateExists(STATE)) + { + _xboxClickStateLastIteration[STATE] = false; + } + + bool isDown = _currentState.Gamepad.wButtons == STATE; + + if (isDown && !_xboxClickStateLastIteration[STATE]) + { + _xboxClickStateLastIteration[STATE] = true; + _xboxClickIsDown[STATE] = true; + } + + if (!isDown && _xboxClickStateLastIteration[STATE]) + { + _xboxClickStateLastIteration[STATE] = false; + _xboxClickIsUp[STATE] = true; + } + + _xboxClickStateLastIteration[STATE] = isDown; +} + +bool Gopher::xboxClickStateExists(DWORD xinput) +{ + auto it = _xboxClickStateLastIteration.find(xinput); + if (it == _xboxClickStateLastIteration.end()) + { + return false; + } + + return true; +} + +void Gopher::mapKeyboard(DWORD STATE, WORD key) +{ + setXboxClickState(STATE); + if (_xboxClickIsDown[STATE]) + { + inputKeyboardDown(key); + } + + if (_xboxClickIsUp[STATE]) + { + inputKeyboardUp(key); + } +} + +void Gopher::mapMouseClick(DWORD STATE, DWORD keyDown, DWORD keyUp) +{ + setXboxClickState(STATE); + if (_xboxClickIsDown[STATE]) + { + mouseEvent(keyDown); + } + + if (_xboxClickIsUp[STATE]) + { + mouseEvent(keyUp); + } +} diff --git a/Windows/Gopher/Gopher/Gopher.h b/Windows/Gopher/Gopher/Gopher.h new file mode 100644 index 0000000..fc359c0 --- /dev/null +++ b/Windows/Gopher/Gopher/Gopher.h @@ -0,0 +1,70 @@ +#include //for Beep() +#include +#include //controller +#include //for printf +#include //for abs() +#include //vol +#include //vol + +#include + +#include "CXBOXController.h" + +#pragma once +class Gopher +{ +private: + const int DEAD_ZONE = 9000; //X and Y minimum, below this is ignored since all controllers have some stick to them + const int SCROLL_DEAD_ZONE = 9000; // Right thumbstick should be less sensitive. + const int TRIGGER_DEAD_ZONE = 0; + const int SCROLL_SPEED = 20; // Speed at which you scroll page. + const int FPS = 150; + const int SLEEP_AMOUNT = 1000/FPS; // number of milliseconds to sleep per iteration + + XINPUT_STATE _currentState; + + const float SPEED_LOW = 0.032f; + const float SPEED_MED = 0.04f; + const float SPEED_HIGH = 0.072f; + float speed = SPEED_MED; + + float _xRest = 0.0f; + float _yRest = 0.0f; + + bool _disabled = false; //use for Select sleep mode + bool _hidden = false; //press Y to hide, check this var + bool _lTriggerPrevious = false; + bool _rTriggerPrevious = false; + + std::map _xboxClickStateLastIteration; + std::map _xboxClickIsDown; + std::map _xboxClickIsUp; + + CXBOXController* _controller; + +public: + + Gopher(CXBOXController* controller); + + void loop(); + + void toggleWindowVisibility(); + + float getDelta(short tx); + + void handleMouseMovement(); + + void handleDisableButton(); + + void handleScrolling(); + + void handleTriggers(WORD lKey, WORD rKey); + + bool xboxClickStateExists(DWORD xinput); + + void mapKeyboard(DWORD STATE, WORD key); + + void mapMouseClick(DWORD STATE, DWORD keyDown, DWORD keyUp); + + void setXboxClickState(DWORD state); +};