Root/
| 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 | |
| 31 | using namespace std; |
| 32 | |
| 33 | bool 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 | |
| 60 | InputManager::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 | |
| 84 | InputManager::~InputManager() |
| 85 | { |
| 86 | #ifndef SDL_JOYSTICK_DISABLED |
| 87 | for (auto it : joysticks) |
| 88 | SDL_JoystickClose(it.joystick); |
| 89 | #endif |
| 90 | } |
| 91 | |
| 92 | bool 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 | |
| 144 | InputManager::Button InputManager::waitForPressedButton() { |
| 145 | Button button; |
| 146 | while (!getButton(&button, true)); |
| 147 | return button; |
| 148 | } |
| 149 | |
| 150 | static int repeatRateMs(int repeatRate) |
| 151 | { |
| 152 | return repeatRate == 0 ? 0 : 1000 / repeatRate; |
| 153 | } |
| 154 | |
| 155 | void 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 | |
| 164 | bool InputManager::pollButton(Button *button) { |
| 165 | return getButton(button, false); |
| 166 | } |
| 167 | |
| 168 | bool 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 | |
| 317 | Uint32 keyRepeatCallback(Uint32 timeout, void *d) |
| 318 | { |
| 319 | struct Joystick *joystick = (struct Joystick *) d; |
| 320 | return joystick->inputManager->joystickRepeatCallback(timeout, joystick); |
| 321 | } |
| 322 | |
| 323 | void 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 | |
| 332 | Uint32 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 | |
| 358 | void InputManager::stopTimer(Joystick *joystick) |
| 359 | { |
| 360 | if (joystick->timer) { |
| 361 | SDL_RemoveTimer(joystick->timer); |
| 362 | joystick->timer = nullptr; |
| 363 | } |
| 364 | } |
| 365 |
Branches:
install_locations
master
opkrun
packages
