💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

704 lines
15 KiB

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "input.hpp"
#include <iostream>
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<Gamepad*>* 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;
}
}