/* * Copyright (C) 2017 Christopher J. Howard * * This file is part of Antkeeper Source Code. * * Antkeeper Source Code 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 3 of the License, or * (at your option) any later version. * * Antkeeper Source Code 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 Antkeeper Source Code. If not, see . */ #include "input.hpp" #include InputDevice::InputDevice(const std::string& name): name(name), disconnected(true) {} void InputDevice::setDisconnected(bool disconnected) { this->disconnected = disconnected; } Keyboard::Keyboard(const std::string& name): InputDevice(name) {} Keyboard::~Keyboard() {} void Keyboard::addKeyObserver(KeyObserver* observer) { keyObservers.push_back(observer); } void Keyboard::removeKeyObserver(KeyObserver* observer) { keyObservers.remove(observer); } void Keyboard::removeKeyObservers() { keyObservers.clear(); } void Keyboard::press(int scancode) { for (auto observer: keyObservers) { observer->keyPressed(scancode); } } void Keyboard::release(int scancode) { for (auto observer: keyObservers) { observer->keyReleased(scancode); } } Mouse::Mouse(const std::string& name): InputDevice(name), notifyingMotionObservers(false), notifyingButtonObservers(false) {} Mouse::~Mouse() {} void Mouse::addMouseMotionObserver(MouseMotionObserver* observer) { if (notifyingMotionObservers) { additionFlaggedMotionObservers.push_back(observer); } else { motionObservers.push_back(observer); } } void Mouse::addMouseButtonObserver(MouseButtonObserver* observer) { if (notifyingButtonObservers) { additionFlaggedButtonObservers.push_back(observer); } else { buttonObservers.push_back(observer); } } void Mouse::removeMouseMotionObserver(MouseMotionObserver* observer) { if (notifyingMotionObservers) { removalFlaggedMotionObservers.push_back(observer); } else { motionObservers.remove(observer); } } void Mouse::removeMouseButtonObserver(MouseButtonObserver* observer) { if (notifyingButtonObservers) { removalFlaggedButtonObservers.push_back(observer); } else { buttonObservers.remove(observer); } } void Mouse::removeMouseMotionObservers() { motionObservers.clear(); } void Mouse::removeMouseButtonObservers() { buttonObservers.clear(); } void Mouse::press(int button, int x, int y) { // Notify observers notifyingButtonObservers = true; for (auto observer: buttonObservers) { observer->mouseButtonPressed(button, x, y); } notifyingButtonObservers = false; // Process flags processFlaggedButtonObservers(); } void Mouse::release(int button, int x, int y) { // Notify observers notifyingButtonObservers = true; for (auto observer: buttonObservers) { observer->mouseButtonReleased(button, x, y); } notifyingButtonObservers = false; // Process flags processFlaggedButtonObservers(); } void Mouse::move(int x, int y) { previousPosition = currentPosition; currentPosition = glm::ivec2(x, y); // Notify observers notifyingMotionObservers = true; for (auto observer: motionObservers) { observer->mouseMoved(x, y); } notifyingMotionObservers = false; // Process flags processFlaggedMotionObservers(); } void Mouse::processFlaggedMotionObservers() { // Remove observers which are flagged for removal for (auto observer: removalFlaggedMotionObservers) { motionObservers.remove(observer); } removalFlaggedMotionObservers.clear(); // Add observers which are flagged for addition for (auto observer: additionFlaggedMotionObservers) { motionObservers.push_back(observer); } additionFlaggedMotionObservers.clear(); } void Mouse::processFlaggedButtonObservers() { // Remove observers which are flagged for removal for (auto observer: removalFlaggedButtonObservers) { buttonObservers.remove(observer); } removalFlaggedButtonObservers.clear(); // Add observers which are flagged for addition for (auto observer: additionFlaggedButtonObservers) { buttonObservers.push_back(observer); } additionFlaggedButtonObservers.clear(); } Gamepad::Gamepad(const std::string& name): InputDevice(name) {} Gamepad::~Gamepad() {} void Gamepad::addGamepadButtonObserver(GamepadButtonObserver* observer) { buttonObservers.push_back(observer); } void Gamepad::removeGamepadButtonObserver(GamepadButtonObserver* observer) { buttonObservers.remove(observer); } void Gamepad::removeGamepadButtonObservers() { buttonObservers.clear(); } void Gamepad::addGamepadAxisObserver(GamepadAxisObserver* observer) { axisObservers.push_back(observer); } void Gamepad::removeGamepadAxisObserver(GamepadAxisObserver* observer) { axisObservers.remove(observer); } void Gamepad::removeGamepadAxisObservers() { axisObservers.clear(); } void Gamepad::press(int button) { for (auto observer: buttonObservers) { observer->gamepadButtonPressed(button); } } void Gamepad::release(int button) { for (auto observer: buttonObservers) { observer->gamepadButtonReleased(button); } } void Gamepad::move(int axis, bool negative, float value) { for (auto observer: axisObservers) { observer->gamepadAxisMoved(axis, negative, value); } } InputEvent::InputEvent(): type(InputEvent::Type::NONE) {} InputManager::InputManager(): closed(false) {} void InputManager::addWindowObserver(WindowObserver* observer) { windowObservers.push_back(observer); } void InputManager::removeWindowObserver(WindowObserver* observer) { windowObservers.remove(observer); } void InputManager::removeWindowObservers() { windowObservers.clear(); } void InputManager::registerKeyboard(Keyboard* keyboard) { keyboards.push_back(keyboard); } void InputManager::registerMouse(Mouse* mouse) { mice.push_back(mouse); } void InputManager::registerGamepad(Gamepad* gamepad) { gamepads.push_back(gamepad); } void InputManager::unregisterKeyboard(Keyboard* keyboard) { keyboards.remove(keyboard); } void InputManager::unregisterMouse(Mouse* mouse) { mice.remove(mouse); } void InputManager::unregisterGamepad(Gamepad* gamepad) { gamepads.remove(gamepad); } bool InputManager::isRegistered(const Keyboard* keyboard) const { for (auto it = keyboards.begin(); it != keyboards.end(); ++it) { if (*it == keyboard) return true; } return false; } bool InputManager::isRegistered(const Mouse* mouse) const { for (auto it = mice.begin(); it != mice.end(); ++it) { if (*it == mouse) return true; } return false; } bool InputManager::isRegistered(const Gamepad* gamepad) const { for (auto it = gamepads.begin(); it != gamepads.end(); ++it) { if (*it == gamepad) return true; } return false; } const Gamepad* InputManager::getGamepad(const std::string& name) const { for (auto gamepad: gamepads) { if (gamepad->getName() == name) return gamepad; } return nullptr; } Gamepad* InputManager::getGamepad(const std::string& name) { for (auto gamepad: gamepads) { if (gamepad->getName() == name) return gamepad; } return nullptr; } SDLInputManager::SDLInputManager() { keyboard = new Keyboard("Default Keyboard"); mouse = new Mouse("Default Mouse"); registerKeyboard(keyboard); registerMouse(mouse); keyboard->setDisconnected(false); mouse->setDisconnected(false); } SDLInputManager::~SDLInputManager() { unregisterKeyboard(keyboard); unregisterMouse(mouse); for (auto gamepad: allocatedGamepads) { unregisterGamepad(gamepad); delete gamepad; } delete keyboard; delete mouse; } void SDLInputManager::update() { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_KEYDOWN: { int scancode = event.key.keysym.scancode; keyboard->press(scancode); break; } case SDL_KEYUP: { int scancode = event.key.keysym.scancode; keyboard->release(scancode); break; } case SDL_MOUSEMOTION: { int x = event.motion.x; int y = event.motion.y; mouse->move(x, y); break; } case SDL_MOUSEBUTTONDOWN: { int button = event.button.button; mouse->press(button, event.button.x, event.button.y); break; } case SDL_MOUSEBUTTONUP: { int button = event.button.button; mouse->release(button, event.button.x, event.button.y); break; } case SDL_CONTROLLERBUTTONDOWN: { int instanceID = event.cbutton.which; auto it = gamepadMap.find(instanceID); if (it == gamepadMap.end()) { std::cerr << "Received event from invalid gamepad" << std::endl; break; } Gamepad* gamepad = it->second; int button = event.cbutton.button; gamepad->press(button); break; } case SDL_CONTROLLERBUTTONUP: { int instanceID = event.cbutton.which; auto it = gamepadMap.find(instanceID); if (it == gamepadMap.end()) { std::cerr << "Received event from invalid gamepad" << std::endl; break; } Gamepad* gamepad = it->second; int button = event.cbutton.button; gamepad->release(button); break; } case SDL_CONTROLLERAXISMOTION: { int instanceID = event.caxis.which; auto it = gamepadMap.find(instanceID); if (it == gamepadMap.end()) { std::cerr << "Received event from invalid gamepad" << std::endl; break; } Gamepad* gamepad = it->second; int axis = event.caxis.axis; bool negative; float value; if (event.caxis.value < 0) { negative = true; value = (float)event.caxis.value / -32768.0f; } else { negative = false; value = (float)event.caxis.value / 32767.0f; } gamepad->move(axis, negative, value); break; } case SDL_CONTROLLERDEVICEADDED: { SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which); if (controller != nullptr) { // Find controller's joystick instance ID SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller); int instanceID = SDL_JoystickInstanceID(joystick); // Determine gamepad name std::string name = SDL_GameControllerName(controller); if (name.empty()) { name = "Unknown Gamepad"; } bool reconnected = false; const std::list* gamepads = getGamepads(); for (auto it = gamepads->begin(); it != gamepads->end(); ++it) { // Check if this gamepad was previously connected if ((*it)->isDisconnected() && (*it)->getName() == name) { // Map to new instance ID Gamepad* gamepad = *it; gamepadMap[instanceID] = gamepad; gamepad->setDisconnected(false); reconnected = true; std::cout << "Reconnected gamepad \"" << name << "\" with ID " << instanceID << std::endl; break; } } if (!reconnected) { // Create new gamepad Gamepad* gamepad = new Gamepad(name); // Add to list of allocated gamepads allocatedGamepads.push_back(gamepad); // Register with the input manager registerGamepad(gamepad); // Map instance ID to gamepad pointer gamepadMap[instanceID] = gamepad; // Connect gamepad gamepad->setDisconnected(false); std::cout << "Connected gamepad \"" << name << "\" with ID " << instanceID << std::endl; } } break; } case SDL_CONTROLLERDEVICEREMOVED: { int instanceID = event.cdevice.which; // Find gamepad auto mapIt = gamepadMap.find(instanceID); if (mapIt == gamepadMap.end()) { std::cerr << "Attempted to remove nonexistent gamepad with ID " << instanceID << std::endl; break; } Gamepad* gamepad = mapIt->second; // Remove from gamepad map gamepadMap.erase(mapIt); // Set disconnected flag gamepad->setDisconnected(true); std::cout << "Disconnected gamepad \"" << gamepad->getName() << "\" with ID " << instanceID << std::endl; break; } case SDL_WINDOWEVENT: { if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { for (auto observer: windowObservers) { observer->windowResized(event.window.data1, event.window.data2); } } else if (event.window.event == SDL_WINDOWEVENT_CLOSE) { closed = true; for (auto observer: windowObservers) { observer->windowClosed(); } } break; } case SDL_QUIT: { closed = true; for (auto observer: windowObservers) { observer->windowClosed(); } break; } default: break; } } } void SDLInputManager::listen(InputEvent* inputEvent) { int eventCount; // Gather events SDL_PumpEvents(); // Check for key events eventCount = SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_KEYDOWN); if (eventCount) { int scancode = event.key.keysym.scancode; inputEvent->type = InputEvent::Type::KEY; inputEvent->key.first = keyboard; inputEvent->key.second = scancode; return; } // Check for mouse button events eventCount = SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONDOWN); if (eventCount) { int button = event.button.button; inputEvent->type = InputEvent::Type::MOUSE_BUTTON; inputEvent->mouseButton.first = mouse; inputEvent->mouseButton.second = button; return; } // Check for gamepad button events eventCount = SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_CONTROLLERBUTTONDOWN, SDL_CONTROLLERBUTTONDOWN); if (eventCount) { int instanceID = event.cbutton.which; auto it = gamepadMap.find(instanceID); if (it == gamepadMap.end()) { std::cerr << "Received event from invalid gamepad" << std::endl; return; } Gamepad* gamepad = it->second; int button = event.cbutton.button; inputEvent->type = InputEvent::Type::GAMEPAD_BUTTON; inputEvent->gamepadButton.first = gamepad; inputEvent->gamepadButton.second = button; return; } // Check for gamepad axis events eventCount = SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERAXISMOTION); if (eventCount) { int instanceID = event.caxis.which; auto it = gamepadMap.find(instanceID); if (it == gamepadMap.end()) { std::cerr << "Received event from invalid gamepad" << std::endl; return; } Gamepad* gamepad = it->second; int axis = event.caxis.axis; bool negative = event.caxis.value < 0; inputEvent->type = InputEvent::Type::GAMEPAD_AXIS; std::get<0>(inputEvent->gamepadAxis) = gamepad; std::get<1>(inputEvent->gamepadAxis) = axis; std::get<2>(inputEvent->gamepadAxis) = negative; return; } }