Root/src/inputmanager.cpp

1/***************************************************************************
2 * Copyright (C) 2006 by Massimiliano Torromeo *
3 * massimiliano.torromeo@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#include "debug.h"
22#include "inputmanager.h"
23#include "gmenu2x.h"
24#include "utilities.h"
25#include "powersaver.h"
26#include "menu.h"
27
28#include <iostream>
29#include <fstream>
30
31using namespace std;
32
33bool InputManager::init(Menu *menu)
34{
35    this->menu = menu;
36
37    repeatRateChanged();
38
39    for (int i = 0; i < BUTTON_TYPE_SIZE; i++) {
40        buttonMap[i].js_mapped = false;
41        buttonMap[i].kb_mapped = false;
42    }
43
44    /* If a user-specified input.conf file exists, we load it;
45     * otherwise, we load the default one. */
46    string input_file = gmenu2x.getHome() + "/input.conf";
47    DEBUG("Loading user-specific input.conf file: %s.\n", input_file.c_str());
48    if (!readConfFile(input_file)) {
49        input_file = GMENU2X_SYSTEM_DIR "/input.conf";
50        DEBUG("Loading system input.conf file: %s.\n", input_file.c_str());
51        if (!readConfFile(input_file)) {
52            ERROR("InputManager: failed to open config file\n");
53            return false;
54        }
55    }
56
57    return true;
58}
59
60InputManager::InputManager(GMenu2X& gmenu2x, PowerSaver& powerSaver)
61    : gmenu2x(gmenu2x)
62    , powerSaver(powerSaver)
63{
64#ifndef SDL_JOYSTICK_DISABLED
65    int i;
66
67    if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) {
68        ERROR("Unable to init joystick subsystem\n");
69        return;
70    }
71
72    for (i = 0; i < SDL_NumJoysticks(); i++) {
73        struct Joystick joystick = {
74            SDL_JoystickOpen(i), false, false, false, false,
75            SDL_HAT_CENTERED, nullptr, this,
76        };
77        joysticks.push_back(joystick);
78    }
79
80    DEBUG("Opening %i joysticks\n", i);
81#endif
82}
83
84InputManager::~InputManager()
85{
86#ifndef SDL_JOYSTICK_DISABLED
87    for (auto it : joysticks)
88        SDL_JoystickClose(it.joystick);
89#endif
90}
91
92bool InputManager::readConfFile(const string &conffile) {
93    ifstream inf(conffile.c_str(), ios_base::in);
94    if (inf.is_open()) {
95        string line;
96        while (getline(inf, line, '\n')) {
97            string::size_type pos = line.find("=");
98            string name = trim(line.substr(0,pos));
99            line = trim(line.substr(pos+1,line.length()));
100
101            Button button;
102            if (name == "up") button = UP;
103            else if (name == "down") button = DOWN;
104            else if (name == "left") button = LEFT;
105            else if (name == "right") button = RIGHT;
106            else if (name == "accept") button = ACCEPT;
107            else if (name == "cancel") button = CANCEL;
108            else if (name == "altleft") button = ALTLEFT;
109            else if (name == "altright") button = ALTRIGHT;
110            else if (name == "menu") button = MENU;
111            else if (name == "settings") button = SETTINGS;
112            else {
113                WARNING("InputManager: Ignoring unknown button name \"%s\"\n",
114                        name.c_str());
115                continue;
116            }
117
118            pos = line.find(",");
119            string sourceStr = trim(line.substr(0,pos));
120            line = trim(line.substr(pos+1, line.length()));
121
122            if (sourceStr == "keyboard") {
123                buttonMap[button].kb_mapped = true;
124                buttonMap[button].kb_code = atoi(line.c_str());
125    #ifndef SDL_JOYSTICK_DISABLED
126            } else if (sourceStr == "joystick") {
127                buttonMap[button].js_mapped = true;
128                buttonMap[button].js_code = atoi(line.c_str());
129    #endif
130            } else {
131                WARNING("InputManager: Ignoring unknown button source \"%s\"\n",
132                        sourceStr.c_str());
133                continue;
134            }
135        }
136
137        inf.close();
138        return true;
139    } else {
140        return false;
141    }
142}
143
144InputManager::Button InputManager::waitForPressedButton() {
145    Button button;
146    while (!getButton(&button, true));
147    return button;
148}
149
150static int repeatRateMs(int repeatRate)
151{
152    return repeatRate == 0 ? 0 : 1000 / repeatRate;
153}
154
155void InputManager::repeatRateChanged() {
156    int ms = repeatRateMs(gmenu2x.confInt["buttonRepeatRate"]);
157    if (ms == 0) {
158        SDL_EnableKeyRepeat(0, 0);
159    } else {
160        SDL_EnableKeyRepeat(INPUT_KEY_REPEAT_DELAY, ms);
161    }
162}
163
164bool InputManager::pollButton(Button *button) {
165    return getButton(button, false);
166}
167
168bool InputManager::getButton(Button *button, bool wait) {
169    //TODO: when an event is processed, program a new event
170    //in some time, and when it occurs, do a key repeat
171
172#ifndef SDL_JOYSTICK_DISABLED
173    if (joysticks.size() > 0)
174        SDL_JoystickUpdate();
175#endif
176
177    SDL_Event event;
178    if (wait)
179        SDL_WaitEvent(&event);
180    else if (!SDL_PollEvent(&event))
181        return false;
182
183    bool is_kb = false, is_js = false;
184    switch(event.type) {
185        case SDL_KEYDOWN:
186            is_kb = true;
187            break;
188#ifndef SDL_JOYSTICK_DISABLED
189        case SDL_JOYHATMOTION: {
190                Joystick *joystick = &joysticks[event.jaxis.which];
191                joystick->hatState = event.jhat.value;
192
193                switch (event.jhat.value) {
194                    case SDL_HAT_CENTERED:
195                        stopTimer(joystick);
196                        return false;
197                    case SDL_HAT_UP:
198                        *button = UP;
199                        break;
200                    case SDL_HAT_DOWN:
201                        *button = DOWN;
202                        break;
203                    case SDL_HAT_LEFT:
204                        *button = LEFT;
205                        break;
206                    case SDL_HAT_RIGHT:
207                        *button = RIGHT;
208                        break;
209                }
210                startTimer(joystick);
211            }
212        case SDL_JOYBUTTONDOWN:
213            is_js = true;
214            break;
215        case SDL_JOYAXISMOTION: {
216                is_js = true;
217
218                unsigned int axis = event.jaxis.axis;
219                /* We only handle the first joystick */
220                if (axis > 1)
221                    return false;
222
223                Joystick *joystick = &joysticks[event.jaxis.which];
224                bool *axisState = joystick->axisState[axis];
225
226                if (event.jaxis.value < -20000) {
227                    if (axisState[AXIS_STATE_NEGATIVE])
228                        return false;
229                    axisState[AXIS_STATE_NEGATIVE] = true;
230                    axisState[AXIS_STATE_POSITIVE] = false;
231                    *button = axis ? UP : LEFT;
232                } else if (event.jaxis.value > 20000) {
233                    if (axisState[AXIS_STATE_POSITIVE])
234                        return false;
235                    axisState[AXIS_STATE_NEGATIVE] = false;
236                    axisState[AXIS_STATE_POSITIVE] = true;
237                    *button = axis ? DOWN : RIGHT;
238                } else {
239                    bool *otherAxisState = joystick->axisState[!axis];
240                    if (!otherAxisState[AXIS_STATE_NEGATIVE] &&
241                                !otherAxisState[AXIS_STATE_POSITIVE] && (
242                                    axisState[AXIS_STATE_NEGATIVE] ||
243                                    axisState[AXIS_STATE_POSITIVE]))
244                        stopTimer(joystick);
245
246                    axisState[0] = axisState[1] = false;
247                    return false;
248                }
249                startTimer(joystick);
250                break;
251            }
252#endif
253        case SDL_USEREVENT:
254            switch ((enum EventCode) event.user.code) {
255#ifdef HAVE_LIBOPK
256                case REMOVE_LINKS:
257                    menu->removePackageLink((const char *) event.user.data1);
258                    break;
259                case OPEN_PACKAGE:
260                    menu->openPackage((const char *) event.user.data1);
261                    break;
262                case OPEN_PACKAGES_FROM_DIR:
263                    menu->openPackagesFromDir(
264                                ((string) (const char *) event.user.data1
265                                 + "/apps").c_str());
266                    break;
267#endif /* HAVE_LIBOPK */
268                case REPAINT_MENU:
269                default:
270                    break;
271            }
272
273            if (event.user.data1)
274                free(event.user.data1);
275            *button = REPAINT;
276            return true;
277
278        case SDL_QUIT:
279            *button = QUIT;
280            return true;
281
282        default:
283            return false;
284    }
285
286    int i = 0;
287    if (is_kb) {
288        for (i = 0; i < BUTTON_TYPE_SIZE; i++) {
289            if (buttonMap[i].kb_mapped
290                    && (unsigned int)event.key.keysym.sym == buttonMap[i].kb_code) {
291                *button = static_cast<Button>(i);
292                break;
293            }
294        }
295#ifndef SDL_JOYSTICK_DISABLED
296    } else if (is_js && event.type == SDL_JOYBUTTONDOWN) {
297        for (i = 0; i < BUTTON_TYPE_SIZE; i++) {
298            if (buttonMap[i].js_mapped
299                    && (unsigned int)event.jbutton.button == buttonMap[i].js_code) {
300                *button = static_cast<Button>(i);
301                break;
302            }
303        }
304#endif
305    }
306
307    if (i == BUTTON_TYPE_SIZE)
308        return false;
309
310    if (wait) {
311        powerSaver.resetScreenTimer();
312    }
313
314    return true;
315}
316
317Uint32 keyRepeatCallback(Uint32 timeout, void *d)
318{
319    struct Joystick *joystick = (struct Joystick *) d;
320    return joystick->inputManager->joystickRepeatCallback(timeout, joystick);
321}
322
323void InputManager::startTimer(Joystick *joystick)
324{
325    if (joystick->timer)
326        return;
327
328    joystick->timer = SDL_AddTimer(INPUT_KEY_REPEAT_DELAY,
329                keyRepeatCallback, joystick);
330}
331
332Uint32 InputManager::joystickRepeatCallback(Uint32 timeout __attribute__((unused)), struct Joystick *joystick)
333{
334    Uint8 hatState;
335
336    if (joystick->axisState[1][AXIS_STATE_NEGATIVE])
337        hatState = SDL_HAT_UP;
338    else if (joystick->axisState[1][AXIS_STATE_POSITIVE])
339        hatState = SDL_HAT_DOWN;
340    else if (joystick->axisState[0][AXIS_STATE_NEGATIVE])
341        hatState = SDL_HAT_LEFT;
342    else if (joystick->axisState[0][AXIS_STATE_POSITIVE])
343        hatState = SDL_HAT_RIGHT;
344    else
345        hatState = joystick->hatState;
346
347    SDL_JoyHatEvent e = {
348        .type = SDL_JOYHATMOTION,
349        .which = (Uint8) SDL_JoystickIndex(joystick->joystick),
350        .hat = 0,
351        .value = hatState,
352    };
353    SDL_PushEvent((SDL_Event *) &e);
354
355    return repeatRateMs(gmenu2x.confInt["buttonRepeatRate"]);
356}
357
358void InputManager::stopTimer(Joystick *joystick)
359{
360    if (joystick->timer) {
361        SDL_RemoveTimer(joystick->timer);
362        joystick->timer = nullptr;
363    }
364}
365

Archive Download this file



interactive