mirror of
git://projects.qi-hardware.com/gmenu2x.git
synced 2024-09-28 19:48:33 +03:00
c1689e41fb
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.
360 lines
9.6 KiB
C++
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;
|
|
}
|
|
}
|