1
0
mirror of git://projects.qi-hardware.com/gmenu2x.git synced 2024-09-28 19:48:33 +03:00
gmenu2x/src/inputmanager.cpp
Maarten ter Huurne c1689e41fb Dismantled Singleton pattern of PowerSaver
The instance-on-demand didn't really work, since we needed explicit
control over this object's destruction to ensure the timer is stopped
when launching an application. And trying to combine getInstance() with
explicit external delete was just ugly.
2014-08-14 09:47:05 +02:00

360 lines
9.6 KiB
C++

/***************************************************************************
* Copyright (C) 2006 by Massimiliano Torromeo *
* massimiliano.torromeo@gmail.com *
* *
* 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., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "debug.h"
#include "inputmanager.h"
#include "gmenu2x.h"
#include "utilities.h"
#include "powersaver.h"
#include "menu.h"
#include <iostream>
#include <fstream>
using namespace std;
bool InputManager::init(GMenu2X *gmenu2x, Menu *menu) {
this->gmenu2x = gmenu2x;
this->menu = menu;
repeatRateChanged();
for (int i = 0; i < BUTTON_TYPE_SIZE; i++) {
buttonMap[i].js_mapped = false;
buttonMap[i].kb_mapped = false;
}
/* If a user-specified input.conf file exists, we load it;
* otherwise, we load the default one. */
string input_file = gmenu2x->getHome() + "/input.conf";
DEBUG("Loading user-specific input.conf file: %s.\n", input_file.c_str());
if (!readConfFile(input_file)) {
input_file = GMENU2X_SYSTEM_DIR "/input.conf";
DEBUG("Loading system input.conf file: %s.\n", input_file.c_str());
if (!readConfFile(input_file)) {
ERROR("InputManager: failed to open config file\n");
return false;
}
}
return true;
}
InputManager::InputManager(PowerSaver& powerSaver)
: powerSaver(powerSaver)
{
#ifndef SDL_JOYSTICK_DISABLED
int i;
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) {
ERROR("Unable to init joystick subsystem\n");
return;
}
for (i = 0; i < SDL_NumJoysticks(); i++) {
struct Joystick joystick = {
SDL_JoystickOpen(i), false, false, false, false,
SDL_HAT_CENTERED, nullptr, this,
};
joysticks.push_back(joystick);
}
DEBUG("Opening %i joysticks\n", i);
#endif
}
InputManager::~InputManager()
{
#ifndef SDL_JOYSTICK_DISABLED
for (auto it : joysticks)
SDL_JoystickClose(it.joystick);
#endif
}
bool InputManager::readConfFile(const string &conffile) {
ifstream inf(conffile.c_str(), ios_base::in);
if (inf.is_open()) {
string line;
while (getline(inf, line, '\n')) {
string::size_type pos = line.find("=");
string name = trim(line.substr(0,pos));
line = trim(line.substr(pos+1,line.length()));
Button button;
if (name == "up") button = UP;
else if (name == "down") button = DOWN;
else if (name == "left") button = LEFT;
else if (name == "right") button = RIGHT;
else if (name == "accept") button = ACCEPT;
else if (name == "cancel") button = CANCEL;
else if (name == "altleft") button = ALTLEFT;
else if (name == "altright") button = ALTRIGHT;
else if (name == "menu") button = MENU;
else if (name == "settings") button = SETTINGS;
else {
WARNING("InputManager: Ignoring unknown button name \"%s\"\n",
name.c_str());
continue;
}
pos = line.find(",");
string sourceStr = trim(line.substr(0,pos));
line = trim(line.substr(pos+1, line.length()));
if (sourceStr == "keyboard") {
buttonMap[button].kb_mapped = true;
buttonMap[button].kb_code = atoi(line.c_str());
#ifndef SDL_JOYSTICK_DISABLED
} else if (sourceStr == "joystick") {
buttonMap[button].js_mapped = true;
buttonMap[button].js_code = atoi(line.c_str());
#endif
} else {
WARNING("InputManager: Ignoring unknown button source \"%s\"\n",
sourceStr.c_str());
continue;
}
}
inf.close();
return true;
} else {
return false;
}
}
InputManager::Button InputManager::waitForPressedButton() {
Button button;
while (!getButton(&button, true));
return button;
}
static int repeatRateMs(int repeatRate)
{
return repeatRate == 0 ? 0 : 1000 / repeatRate;
}
void InputManager::repeatRateChanged() {
int ms = repeatRateMs(gmenu2x->confInt["buttonRepeatRate"]);
if (ms == 0) {
SDL_EnableKeyRepeat(0, 0);
} else {
SDL_EnableKeyRepeat(INPUT_KEY_REPEAT_DELAY, ms);
}
}
bool InputManager::pollButton(Button *button) {
return getButton(button, false);
}
bool InputManager::getButton(Button *button, bool wait) {
//TODO: when an event is processed, program a new event
//in some time, and when it occurs, do a key repeat
#ifndef SDL_JOYSTICK_DISABLED
if (joysticks.size() > 0)
SDL_JoystickUpdate();
#endif
SDL_Event event;
if (wait)
SDL_WaitEvent(&event);
else if (!SDL_PollEvent(&event))
return false;
bool is_kb = false, is_js = false;
switch(event.type) {
case SDL_KEYDOWN:
is_kb = true;
break;
#ifndef SDL_JOYSTICK_DISABLED
case SDL_JOYHATMOTION: {
Joystick *joystick = &joysticks[event.jaxis.which];
joystick->hatState = event.jhat.value;
switch (event.jhat.value) {
case SDL_HAT_CENTERED:
stopTimer(joystick);
return false;
case SDL_HAT_UP:
*button = UP;
break;
case SDL_HAT_DOWN:
*button = DOWN;
break;
case SDL_HAT_LEFT:
*button = LEFT;
break;
case SDL_HAT_RIGHT:
*button = RIGHT;
break;
}
startTimer(joystick);
}
case SDL_JOYBUTTONDOWN:
is_js = true;
break;
case SDL_JOYAXISMOTION: {
is_js = true;
unsigned int axis = event.jaxis.axis;
/* We only handle the first joystick */
if (axis > 1)
return false;
Joystick *joystick = &joysticks[event.jaxis.which];
bool *axisState = joystick->axisState[axis];
if (event.jaxis.value < -20000) {
if (axisState[AXIS_STATE_NEGATIVE])
return false;
axisState[AXIS_STATE_NEGATIVE] = true;
axisState[AXIS_STATE_POSITIVE] = false;
*button = axis ? UP : LEFT;
} else if (event.jaxis.value > 20000) {
if (axisState[AXIS_STATE_POSITIVE])
return false;
axisState[AXIS_STATE_NEGATIVE] = false;
axisState[AXIS_STATE_POSITIVE] = true;
*button = axis ? DOWN : RIGHT;
} else {
bool *otherAxisState = joystick->axisState[!axis];
if (!otherAxisState[AXIS_STATE_NEGATIVE] &&
!otherAxisState[AXIS_STATE_POSITIVE] && (
axisState[AXIS_STATE_NEGATIVE] ||
axisState[AXIS_STATE_POSITIVE]))
stopTimer(joystick);
axisState[0] = axisState[1] = false;
return false;
}
startTimer(joystick);
break;
}
#endif
case SDL_USEREVENT:
switch ((enum EventCode) event.user.code) {
#ifdef HAVE_LIBOPK
case REMOVE_LINKS:
menu->removePackageLink((const char *) event.user.data1);
break;
case OPEN_PACKAGE:
menu->openPackage((const char *) event.user.data1);
break;
case OPEN_PACKAGES_FROM_DIR:
menu->openPackagesFromDir(
((string) (const char *) event.user.data1
+ "/apps").c_str());
break;
#endif /* HAVE_LIBOPK */
case REPAINT_MENU:
default:
break;
}
if (event.user.data1)
free(event.user.data1);
*button = REPAINT;
return true;
default:
return false;
}
int i = 0;
if (is_kb) {
for (i = 0; i < BUTTON_TYPE_SIZE; i++) {
if (buttonMap[i].kb_mapped
&& (unsigned int)event.key.keysym.sym == buttonMap[i].kb_code) {
*button = static_cast<Button>(i);
break;
}
}
#ifndef SDL_JOYSTICK_DISABLED
} else if (is_js && event.type == SDL_JOYBUTTONDOWN) {
for (i = 0; i < BUTTON_TYPE_SIZE; i++) {
if (buttonMap[i].js_mapped
&& (unsigned int)event.jbutton.button == buttonMap[i].js_code) {
*button = static_cast<Button>(i);
break;
}
}
#endif
}
if (i == BUTTON_TYPE_SIZE)
return false;
if (wait) {
powerSaver.resetScreenTimer();
}
return true;
}
Uint32 keyRepeatCallback(Uint32 timeout, void *d)
{
struct Joystick *joystick = (struct Joystick *) d;
return joystick->inputManager->joystickRepeatCallback(timeout, joystick);
}
void InputManager::startTimer(Joystick *joystick)
{
if (joystick->timer)
return;
joystick->timer = SDL_AddTimer(INPUT_KEY_REPEAT_DELAY,
keyRepeatCallback, joystick);
}
Uint32 InputManager::joystickRepeatCallback(Uint32 timeout __attribute__((unused)), struct Joystick *joystick)
{
Uint8 hatState;
if (joystick->axisState[1][AXIS_STATE_NEGATIVE])
hatState = SDL_HAT_UP;
else if (joystick->axisState[1][AXIS_STATE_POSITIVE])
hatState = SDL_HAT_DOWN;
else if (joystick->axisState[0][AXIS_STATE_NEGATIVE])
hatState = SDL_HAT_LEFT;
else if (joystick->axisState[0][AXIS_STATE_POSITIVE])
hatState = SDL_HAT_RIGHT;
else
hatState = joystick->hatState;
SDL_JoyHatEvent e = {
.type = SDL_JOYHATMOTION,
.which = (Uint8) SDL_JoystickIndex(joystick->joystick),
.hat = 0,
.value = hatState,
};
SDL_PushEvent((SDL_Event *) &e);
return repeatRateMs(gmenu2x->confInt["buttonRepeatRate"]);
}
void InputManager::stopTimer(Joystick *joystick)
{
if (joystick->timer) {
SDL_RemoveTimer(joystick->timer);
joystick->timer = nullptr;
}
}