| 1 | /** |
| 2 | * Copyright (c) 2006-2023 LOVE Development Team |
| 3 | * |
| 4 | * This software is provided 'as-is', without any express or implied |
| 5 | * warranty. In no event will the authors be held liable for any damages |
| 6 | * arising from the use of this software. |
| 7 | * |
| 8 | * Permission is granted to anyone to use this software for any purpose, |
| 9 | * including commercial applications, and to alter it and redistribute it |
| 10 | * freely, subject to the following restrictions: |
| 11 | * |
| 12 | * 1. The origin of this software must not be misrepresented; you must not |
| 13 | * claim that you wrote the original software. If you use this software |
| 14 | * in a product, an acknowledgment in the product documentation would be |
| 15 | * appreciated but is not required. |
| 16 | * 2. Altered source versions must be plainly marked as such, and must not be |
| 17 | * misrepresented as being the original software. |
| 18 | * 3. This notice may not be removed or altered from any source distribution. |
| 19 | **/ |
| 20 | |
| 21 | #include "Event.h" |
| 22 | |
| 23 | #include "filesystem/DroppedFile.h" |
| 24 | #include "filesystem/Filesystem.h" |
| 25 | #include "keyboard/sdl/Keyboard.h" |
| 26 | #include "joystick/JoystickModule.h" |
| 27 | #include "joystick/sdl/Joystick.h" |
| 28 | #include "touch/sdl/Touch.h" |
| 29 | #include "graphics/Graphics.h" |
| 30 | #include "window/Window.h" |
| 31 | #include "common/Exception.h" |
| 32 | #include "audio/Audio.h" |
| 33 | #include "common/config.h" |
| 34 | #include "timer/Timer.h" |
| 35 | |
| 36 | #include <cmath> |
| 37 | |
| 38 | namespace love |
| 39 | { |
| 40 | namespace event |
| 41 | { |
| 42 | namespace sdl |
| 43 | { |
| 44 | |
| 45 | // SDL reports mouse coordinates in the window coordinate system in OS X, but |
| 46 | // we want them in pixel coordinates (may be different with high-DPI enabled.) |
| 47 | static void windowToDPICoords(double *x, double *y) |
| 48 | { |
| 49 | auto window = Module::getInstance<window::Window>(Module::M_WINDOW); |
| 50 | if (window) |
| 51 | window->windowToDPICoords(x, y); |
| 52 | } |
| 53 | |
| 54 | #ifndef LOVE_MACOSX |
| 55 | static void normalizedToDPICoords(double *x, double *y) |
| 56 | { |
| 57 | double w = 1.0, h = 1.0; |
| 58 | |
| 59 | auto window = Module::getInstance<window::Window>(Module::M_WINDOW); |
| 60 | if (window) |
| 61 | { |
| 62 | w = window->getWidth(); |
| 63 | h = window->getHeight(); |
| 64 | window->windowToDPICoords(&w, &h); |
| 65 | } |
| 66 | |
| 67 | if (x) |
| 68 | *x = ((*x) * w); |
| 69 | if (y) |
| 70 | *y = ((*y) * h); |
| 71 | } |
| 72 | #endif |
| 73 | |
| 74 | // SDL's event watch callbacks trigger when the event is actually posted inside |
| 75 | // SDL, unlike with SDL_PollEvents. This is useful for some events which require |
| 76 | // handling inside the function which triggered them on some backends. |
| 77 | static int SDLCALL watchAppEvents(void * /*udata*/, SDL_Event *event) |
| 78 | { |
| 79 | auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS); |
| 80 | |
| 81 | switch (event->type) |
| 82 | { |
| 83 | // On iOS, calling any OpenGL ES function after the function which triggers |
| 84 | // SDL_APP_DIDENTERBACKGROUND is called will kill the app, so we handle it |
| 85 | // with an event watch callback, which will be called inside that function. |
| 86 | case SDL_APP_DIDENTERBACKGROUND: |
| 87 | case SDL_APP_WILLENTERFOREGROUND: |
| 88 | if (gfx) |
| 89 | gfx->setActive(event->type == SDL_APP_WILLENTERFOREGROUND); |
| 90 | break; |
| 91 | default: |
| 92 | break; |
| 93 | } |
| 94 | |
| 95 | return 1; |
| 96 | } |
| 97 | |
| 98 | const char *Event::getName() const |
| 99 | { |
| 100 | return "love.event.sdl" ; |
| 101 | } |
| 102 | |
| 103 | Event::Event() |
| 104 | { |
| 105 | if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) |
| 106 | throw love::Exception("Could not initialize SDL events subsystem (%s)" , SDL_GetError()); |
| 107 | |
| 108 | SDL_AddEventWatch(watchAppEvents, this); |
| 109 | } |
| 110 | |
| 111 | Event::~Event() |
| 112 | { |
| 113 | SDL_DelEventWatch(watchAppEvents, this); |
| 114 | SDL_QuitSubSystem(SDL_INIT_EVENTS); |
| 115 | } |
| 116 | |
| 117 | void Event::pump() |
| 118 | { |
| 119 | exceptionIfInRenderPass("love.event.pump" ); |
| 120 | |
| 121 | SDL_Event e; |
| 122 | |
| 123 | while (SDL_PollEvent(&e)) |
| 124 | { |
| 125 | Message *msg = convert(e); |
| 126 | if (msg) |
| 127 | { |
| 128 | push(msg); |
| 129 | msg->release(); |
| 130 | } |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | Message *Event::wait() |
| 135 | { |
| 136 | exceptionIfInRenderPass("love.event.wait" ); |
| 137 | |
| 138 | SDL_Event e; |
| 139 | |
| 140 | if (SDL_WaitEvent(&e) != 1) |
| 141 | return nullptr; |
| 142 | |
| 143 | return convert(e); |
| 144 | } |
| 145 | |
| 146 | void Event::clear() |
| 147 | { |
| 148 | exceptionIfInRenderPass("love.event.clear" ); |
| 149 | |
| 150 | SDL_Event e; |
| 151 | |
| 152 | while (SDL_PollEvent(&e)) |
| 153 | { |
| 154 | // Do nothing with 'e' ... |
| 155 | } |
| 156 | |
| 157 | love::event::Event::clear(); |
| 158 | } |
| 159 | |
| 160 | void Event::exceptionIfInRenderPass(const char *name) |
| 161 | { |
| 162 | // Some core OS graphics functionality (e.g. swap buffers on some platforms) |
| 163 | // happens inside SDL_PumpEvents - which is called by SDL_PollEvent and |
| 164 | // friends. It's probably a bad idea to call those functions while a Canvas |
| 165 | // is active. |
| 166 | auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS); |
| 167 | if (gfx != nullptr && gfx->isCanvasActive()) |
| 168 | throw love::Exception("%s cannot be called while a Canvas is active in love.graphics." , name); |
| 169 | } |
| 170 | |
| 171 | Message *Event::convert(const SDL_Event &e) |
| 172 | { |
| 173 | Message *msg = nullptr; |
| 174 | |
| 175 | std::vector<Variant> vargs; |
| 176 | vargs.reserve(4); |
| 177 | |
| 178 | love::filesystem::Filesystem *filesystem = nullptr; |
| 179 | |
| 180 | love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_UNKNOWN; |
| 181 | love::keyboard::Keyboard::Scancode scancode = love::keyboard::Keyboard::SCANCODE_UNKNOWN; |
| 182 | |
| 183 | const char *txt; |
| 184 | const char *txt2; |
| 185 | std::map<SDL_Keycode, love::keyboard::Keyboard::Key>::const_iterator keyit; |
| 186 | |
| 187 | #ifndef LOVE_MACOSX |
| 188 | love::touch::sdl::Touch *touchmodule = nullptr; |
| 189 | love::touch::Touch::TouchInfo touchinfo; |
| 190 | #endif |
| 191 | |
| 192 | #ifdef LOVE_LINUX |
| 193 | static bool touchNormalizationBug = false; |
| 194 | #endif |
| 195 | |
| 196 | switch (e.type) |
| 197 | { |
| 198 | case SDL_KEYDOWN: |
| 199 | if (e.key.repeat) |
| 200 | { |
| 201 | auto kb = Module::getInstance<love::keyboard::Keyboard>(Module::M_KEYBOARD); |
| 202 | if (kb && !kb->hasKeyRepeat()) |
| 203 | break; |
| 204 | } |
| 205 | |
| 206 | keyit = keys.find(e.key.keysym.sym); |
| 207 | if (keyit != keys.end()) |
| 208 | key = keyit->second; |
| 209 | |
| 210 | if (!love::keyboard::Keyboard::getConstant(key, txt)) |
| 211 | txt = "unknown" ; |
| 212 | |
| 213 | love::keyboard::sdl::Keyboard::getConstant(e.key.keysym.scancode, scancode); |
| 214 | if (!love::keyboard::Keyboard::getConstant(scancode, txt2)) |
| 215 | txt2 = "unknown" ; |
| 216 | |
| 217 | vargs.emplace_back(txt, strlen(txt)); |
| 218 | vargs.emplace_back(txt2, strlen(txt2)); |
| 219 | vargs.emplace_back(e.key.repeat != 0); |
| 220 | msg = new Message("keypressed" , vargs); |
| 221 | break; |
| 222 | case SDL_KEYUP: |
| 223 | keyit = keys.find(e.key.keysym.sym); |
| 224 | if (keyit != keys.end()) |
| 225 | key = keyit->second; |
| 226 | |
| 227 | if (!love::keyboard::Keyboard::getConstant(key, txt)) |
| 228 | txt = "unknown" ; |
| 229 | |
| 230 | love::keyboard::sdl::Keyboard::getConstant(e.key.keysym.scancode, scancode); |
| 231 | if (!love::keyboard::Keyboard::getConstant(scancode, txt2)) |
| 232 | txt2 = "unknown" ; |
| 233 | |
| 234 | vargs.emplace_back(txt, strlen(txt)); |
| 235 | vargs.emplace_back(txt2, strlen(txt2)); |
| 236 | msg = new Message("keyreleased" , vargs); |
| 237 | break; |
| 238 | case SDL_TEXTINPUT: |
| 239 | txt = e.text.text; |
| 240 | vargs.emplace_back(txt, strlen(txt)); |
| 241 | msg = new Message("textinput" , vargs); |
| 242 | break; |
| 243 | case SDL_TEXTEDITING: |
| 244 | txt = e.edit.text; |
| 245 | vargs.emplace_back(txt, strlen(txt)); |
| 246 | vargs.emplace_back((double) e.edit.start); |
| 247 | vargs.emplace_back((double) e.edit.length); |
| 248 | msg = new Message("textedited" , vargs); |
| 249 | break; |
| 250 | case SDL_MOUSEMOTION: |
| 251 | { |
| 252 | double x = (double) e.motion.x; |
| 253 | double y = (double) e.motion.y; |
| 254 | double xrel = (double) e.motion.xrel; |
| 255 | double yrel = (double) e.motion.yrel; |
| 256 | windowToDPICoords(&x, &y); |
| 257 | windowToDPICoords(&xrel, &yrel); |
| 258 | vargs.emplace_back(x); |
| 259 | vargs.emplace_back(y); |
| 260 | vargs.emplace_back(xrel); |
| 261 | vargs.emplace_back(yrel); |
| 262 | vargs.emplace_back(e.motion.which == SDL_TOUCH_MOUSEID); |
| 263 | msg = new Message("mousemoved" , vargs); |
| 264 | } |
| 265 | break; |
| 266 | case SDL_MOUSEBUTTONDOWN: |
| 267 | case SDL_MOUSEBUTTONUP: |
| 268 | { |
| 269 | // SDL uses button 3 for the right mouse button, but we use button 2 |
| 270 | int button = e.button.button; |
| 271 | switch (button) |
| 272 | { |
| 273 | case SDL_BUTTON_RIGHT: |
| 274 | button = 2; |
| 275 | break; |
| 276 | case SDL_BUTTON_MIDDLE: |
| 277 | button = 3; |
| 278 | break; |
| 279 | } |
| 280 | |
| 281 | double px = (double) e.button.x; |
| 282 | double py = (double) e.button.y; |
| 283 | windowToDPICoords(&px, &py); |
| 284 | vargs.emplace_back(px); |
| 285 | vargs.emplace_back(py); |
| 286 | vargs.emplace_back((double) button); |
| 287 | vargs.emplace_back(e.button.which == SDL_TOUCH_MOUSEID); |
| 288 | vargs.emplace_back((double) e.button.clicks); |
| 289 | |
| 290 | bool down = e.type == SDL_MOUSEBUTTONDOWN; |
| 291 | msg = new Message(down ? "mousepressed" : "mousereleased" , vargs); |
| 292 | } |
| 293 | break; |
| 294 | case SDL_MOUSEWHEEL: |
| 295 | vargs.emplace_back((double) e.wheel.x); |
| 296 | vargs.emplace_back((double) e.wheel.y); |
| 297 | msg = new Message("wheelmoved" , vargs); |
| 298 | break; |
| 299 | case SDL_FINGERDOWN: |
| 300 | case SDL_FINGERUP: |
| 301 | case SDL_FINGERMOTION: |
| 302 | // Touch events are disabled in OS X because we only actually want touch |
| 303 | // screen events, but most touch devices in OS X aren't touch screens |
| 304 | // (and SDL doesn't differentiate.) Non-screen touch devices like Mac |
| 305 | // trackpads won't give touch coords in the window's coordinate-space. |
| 306 | #ifndef LOVE_MACOSX |
| 307 | touchinfo.id = (int64) e.tfinger.fingerId; |
| 308 | touchinfo.x = e.tfinger.x; |
| 309 | touchinfo.y = e.tfinger.y; |
| 310 | touchinfo.dx = e.tfinger.dx; |
| 311 | touchinfo.dy = e.tfinger.dy; |
| 312 | touchinfo.pressure = e.tfinger.pressure; |
| 313 | |
| 314 | #ifdef LOVE_LINUX |
| 315 | // FIXME: hacky workaround for SDL not normalizing touch coordinates in |
| 316 | // its X11 backend: https://bugzilla.libsdl.org/show_bug.cgi?id=2307 |
| 317 | if (touchNormalizationBug || fabs(touchinfo.x) >= 1.5 || fabs(touchinfo.y) >= 1.5 || fabs(touchinfo.dx) >= 1.5 || fabs(touchinfo.dy) >= 1.5) |
| 318 | { |
| 319 | touchNormalizationBug = true; |
| 320 | windowToDPICoords(&touchinfo.x, &touchinfo.y); |
| 321 | windowToDPICoords(&touchinfo.dx, &touchinfo.dy); |
| 322 | } |
| 323 | else |
| 324 | #endif |
| 325 | { |
| 326 | // SDL's coords are normalized to [0, 1], but we want screen coords. |
| 327 | normalizedToDPICoords(&touchinfo.x, &touchinfo.y); |
| 328 | normalizedToDPICoords(&touchinfo.dx, &touchinfo.dy); |
| 329 | } |
| 330 | |
| 331 | // We need to update the love.touch.sdl internal state from here. |
| 332 | touchmodule = (touch::sdl::Touch *) Module::getInstance("love.touch.sdl" ); |
| 333 | if (touchmodule) |
| 334 | touchmodule->onEvent(e.type, touchinfo); |
| 335 | |
| 336 | // This is a bit hackish and we lose the higher 32 bits of the id on |
| 337 | // 32-bit systems, but SDL only ever gives id's that at most use as many |
| 338 | // bits as can fit in a pointer (for now.) |
| 339 | // We use lightuserdata instead of a lua_Number (double) because doubles |
| 340 | // can't represent all possible id values on 64-bit systems. |
| 341 | vargs.emplace_back((void *) (intptr_t) touchinfo.id); |
| 342 | vargs.emplace_back(touchinfo.x); |
| 343 | vargs.emplace_back(touchinfo.y); |
| 344 | vargs.emplace_back(touchinfo.dx); |
| 345 | vargs.emplace_back(touchinfo.dy); |
| 346 | vargs.emplace_back(touchinfo.pressure); |
| 347 | |
| 348 | if (e.type == SDL_FINGERDOWN) |
| 349 | txt = "touchpressed" ; |
| 350 | else if (e.type == SDL_FINGERUP) |
| 351 | txt = "touchreleased" ; |
| 352 | else |
| 353 | txt = "touchmoved" ; |
| 354 | msg = new Message(txt, vargs); |
| 355 | #endif |
| 356 | break; |
| 357 | case SDL_JOYBUTTONDOWN: |
| 358 | case SDL_JOYBUTTONUP: |
| 359 | case SDL_JOYAXISMOTION: |
| 360 | case SDL_JOYBALLMOTION: |
| 361 | case SDL_JOYHATMOTION: |
| 362 | case SDL_JOYDEVICEADDED: |
| 363 | case SDL_JOYDEVICEREMOVED: |
| 364 | case SDL_CONTROLLERBUTTONDOWN: |
| 365 | case SDL_CONTROLLERBUTTONUP: |
| 366 | case SDL_CONTROLLERAXISMOTION: |
| 367 | msg = convertJoystickEvent(e); |
| 368 | break; |
| 369 | case SDL_WINDOWEVENT: |
| 370 | msg = convertWindowEvent(e); |
| 371 | break; |
| 372 | #if SDL_VERSION_ATLEAST(2, 0, 9) |
| 373 | case SDL_DISPLAYEVENT: |
| 374 | if (e.display.event == SDL_DISPLAYEVENT_ORIENTATION) |
| 375 | { |
| 376 | auto orientation = window::Window::ORIENTATION_UNKNOWN; |
| 377 | switch ((SDL_DisplayOrientation) e.display.data1) |
| 378 | { |
| 379 | case SDL_ORIENTATION_UNKNOWN: |
| 380 | default: |
| 381 | orientation = window::Window::ORIENTATION_UNKNOWN; |
| 382 | break; |
| 383 | case SDL_ORIENTATION_LANDSCAPE: |
| 384 | orientation = window::Window::ORIENTATION_LANDSCAPE; |
| 385 | break; |
| 386 | case SDL_ORIENTATION_LANDSCAPE_FLIPPED: |
| 387 | orientation = window::Window::ORIENTATION_LANDSCAPE_FLIPPED; |
| 388 | break; |
| 389 | case SDL_ORIENTATION_PORTRAIT: |
| 390 | orientation = window::Window::ORIENTATION_PORTRAIT; |
| 391 | break; |
| 392 | case SDL_ORIENTATION_PORTRAIT_FLIPPED: |
| 393 | orientation = window::Window::ORIENTATION_PORTRAIT_FLIPPED; |
| 394 | break; |
| 395 | } |
| 396 | |
| 397 | if (!window::Window::getConstant(orientation, txt)) |
| 398 | txt = "unknown" ; |
| 399 | |
| 400 | vargs.emplace_back((double)(e.display.display + 1)); |
| 401 | vargs.emplace_back(txt, strlen(txt)); |
| 402 | |
| 403 | msg = new Message("displayrotated" , vargs); |
| 404 | } |
| 405 | break; |
| 406 | #endif |
| 407 | case SDL_DROPFILE: |
| 408 | filesystem = Module::getInstance<filesystem::Filesystem>(Module::M_FILESYSTEM); |
| 409 | if (filesystem != nullptr) |
| 410 | { |
| 411 | // Allow mounting any dropped path, so zips or dirs can be mounted. |
| 412 | filesystem->allowMountingForPath(e.drop.file); |
| 413 | |
| 414 | if (filesystem->isRealDirectory(e.drop.file)) |
| 415 | { |
| 416 | vargs.emplace_back(e.drop.file, strlen(e.drop.file)); |
| 417 | msg = new Message("directorydropped" , vargs); |
| 418 | } |
| 419 | else |
| 420 | { |
| 421 | auto *file = new love::filesystem::DroppedFile(e.drop.file); |
| 422 | vargs.emplace_back(&love::filesystem::DroppedFile::type, file); |
| 423 | msg = new Message("filedropped" , vargs); |
| 424 | file->release(); |
| 425 | } |
| 426 | } |
| 427 | SDL_free(e.drop.file); |
| 428 | break; |
| 429 | case SDL_QUIT: |
| 430 | case SDL_APP_TERMINATING: |
| 431 | msg = new Message("quit" ); |
| 432 | break; |
| 433 | case SDL_APP_LOWMEMORY: |
| 434 | msg = new Message("lowmemory" ); |
| 435 | break; |
| 436 | default: |
| 437 | break; |
| 438 | } |
| 439 | |
| 440 | return msg; |
| 441 | } |
| 442 | |
| 443 | Message *Event::convertJoystickEvent(const SDL_Event &e) const |
| 444 | { |
| 445 | auto joymodule = Module::getInstance<joystick::JoystickModule>(Module::M_JOYSTICK); |
| 446 | if (!joymodule) |
| 447 | return nullptr; |
| 448 | |
| 449 | Message *msg = nullptr; |
| 450 | |
| 451 | std::vector<Variant> vargs; |
| 452 | vargs.reserve(4); |
| 453 | |
| 454 | love::Type *joysticktype = &love::joystick::Joystick::type; |
| 455 | love::joystick::Joystick *stick = nullptr; |
| 456 | love::joystick::Joystick::Hat hat; |
| 457 | love::joystick::Joystick::GamepadButton padbutton; |
| 458 | love::joystick::Joystick::GamepadAxis padaxis; |
| 459 | const char *txt; |
| 460 | |
| 461 | switch (e.type) |
| 462 | { |
| 463 | case SDL_JOYBUTTONDOWN: |
| 464 | case SDL_JOYBUTTONUP: |
| 465 | stick = joymodule->getJoystickFromID(e.jbutton.which); |
| 466 | if (!stick) |
| 467 | break; |
| 468 | |
| 469 | vargs.emplace_back(joysticktype, stick); |
| 470 | vargs.emplace_back((double)(e.jbutton.button+1)); |
| 471 | msg = new Message((e.type == SDL_JOYBUTTONDOWN) ? |
| 472 | "joystickpressed" : "joystickreleased" , |
| 473 | vargs); |
| 474 | break; |
| 475 | case SDL_JOYAXISMOTION: |
| 476 | { |
| 477 | stick = joymodule->getJoystickFromID(e.jaxis.which); |
| 478 | if (!stick) |
| 479 | break; |
| 480 | |
| 481 | vargs.emplace_back(joysticktype, stick); |
| 482 | vargs.emplace_back((double)(e.jaxis.axis+1)); |
| 483 | float value = joystick::Joystick::clampval(e.jaxis.value / 32768.0f); |
| 484 | vargs.emplace_back((double) value); |
| 485 | msg = new Message("joystickaxis" , vargs); |
| 486 | } |
| 487 | break; |
| 488 | case SDL_JOYHATMOTION: |
| 489 | if (!joystick::sdl::Joystick::getConstant(e.jhat.value, hat) || !joystick::Joystick::getConstant(hat, txt)) |
| 490 | break; |
| 491 | |
| 492 | stick = joymodule->getJoystickFromID(e.jhat.which); |
| 493 | if (!stick) |
| 494 | break; |
| 495 | |
| 496 | vargs.emplace_back(joysticktype, stick); |
| 497 | vargs.emplace_back((double)(e.jhat.hat+1)); |
| 498 | vargs.emplace_back(txt, strlen(txt)); |
| 499 | msg = new Message("joystickhat" , vargs); |
| 500 | break; |
| 501 | case SDL_CONTROLLERBUTTONDOWN: |
| 502 | case SDL_CONTROLLERBUTTONUP: |
| 503 | if (!joystick::sdl::Joystick::getConstant((SDL_GameControllerButton) e.cbutton.button, padbutton)) |
| 504 | break; |
| 505 | |
| 506 | if (!joystick::Joystick::getConstant(padbutton, txt)) |
| 507 | break; |
| 508 | |
| 509 | stick = joymodule->getJoystickFromID(e.cbutton.which); |
| 510 | if (!stick) |
| 511 | break; |
| 512 | |
| 513 | vargs.emplace_back(joysticktype, stick); |
| 514 | vargs.emplace_back(txt, strlen(txt)); |
| 515 | msg = new Message(e.type == SDL_CONTROLLERBUTTONDOWN ? |
| 516 | "gamepadpressed" : "gamepadreleased" , vargs); |
| 517 | break; |
| 518 | case SDL_CONTROLLERAXISMOTION: |
| 519 | if (joystick::sdl::Joystick::getConstant((SDL_GameControllerAxis) e.caxis.axis, padaxis)) |
| 520 | { |
| 521 | if (!joystick::Joystick::getConstant(padaxis, txt)) |
| 522 | break; |
| 523 | |
| 524 | stick = joymodule->getJoystickFromID(e.caxis.which); |
| 525 | if (!stick) |
| 526 | break; |
| 527 | |
| 528 | vargs.emplace_back(joysticktype, stick); |
| 529 | vargs.emplace_back(txt, strlen(txt)); |
| 530 | float value = joystick::Joystick::clampval(e.caxis.value / 32768.0f); |
| 531 | vargs.emplace_back((double) value); |
| 532 | msg = new Message("gamepadaxis" , vargs); |
| 533 | } |
| 534 | break; |
| 535 | case SDL_JOYDEVICEADDED: |
| 536 | // jdevice.which is the joystick device index. |
| 537 | stick = joymodule->addJoystick(e.jdevice.which); |
| 538 | if (stick) |
| 539 | { |
| 540 | vargs.emplace_back(joysticktype, stick); |
| 541 | msg = new Message("joystickadded" , vargs); |
| 542 | } |
| 543 | break; |
| 544 | case SDL_JOYDEVICEREMOVED: |
| 545 | // jdevice.which is the joystick instance ID now. |
| 546 | stick = joymodule->getJoystickFromID(e.jdevice.which); |
| 547 | if (stick) |
| 548 | { |
| 549 | joymodule->removeJoystick(stick); |
| 550 | vargs.emplace_back(joysticktype, stick); |
| 551 | msg = new Message("joystickremoved" , vargs); |
| 552 | } |
| 553 | break; |
| 554 | default: |
| 555 | break; |
| 556 | } |
| 557 | |
| 558 | return msg; |
| 559 | } |
| 560 | |
| 561 | Message *Event::convertWindowEvent(const SDL_Event &e) |
| 562 | { |
| 563 | Message *msg = nullptr; |
| 564 | |
| 565 | std::vector<Variant> vargs; |
| 566 | vargs.reserve(4); |
| 567 | |
| 568 | window::Window *win = nullptr; |
| 569 | graphics::Graphics *gfx = nullptr; |
| 570 | |
| 571 | if (e.type != SDL_WINDOWEVENT) |
| 572 | return nullptr; |
| 573 | |
| 574 | switch (e.window.event) |
| 575 | { |
| 576 | case SDL_WINDOWEVENT_FOCUS_GAINED: |
| 577 | case SDL_WINDOWEVENT_FOCUS_LOST: |
| 578 | vargs.emplace_back(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED); |
| 579 | msg = new Message("focus" , vargs); |
| 580 | break; |
| 581 | case SDL_WINDOWEVENT_ENTER: |
| 582 | case SDL_WINDOWEVENT_LEAVE: |
| 583 | vargs.emplace_back(e.window.event == SDL_WINDOWEVENT_ENTER); |
| 584 | msg = new Message("mousefocus" , vargs); |
| 585 | break; |
| 586 | case SDL_WINDOWEVENT_SHOWN: |
| 587 | case SDL_WINDOWEVENT_HIDDEN: |
| 588 | vargs.emplace_back(e.window.event == SDL_WINDOWEVENT_SHOWN); |
| 589 | msg = new Message("visible" , vargs); |
| 590 | break; |
| 591 | case SDL_WINDOWEVENT_RESIZED: |
| 592 | { |
| 593 | double width = e.window.data1; |
| 594 | double height = e.window.data2; |
| 595 | |
| 596 | gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS); |
| 597 | win = Module::getInstance<window::Window>(Module::M_WINDOW); |
| 598 | |
| 599 | // WINDOWEVENT_SIZE_CHANGED will always occur before RESIZED. |
| 600 | // The size values in the Window aren't necessarily the same as the |
| 601 | // graphics size, which is what we want to output. |
| 602 | if (gfx) |
| 603 | { |
| 604 | width = gfx->getWidth(); |
| 605 | height = gfx->getHeight(); |
| 606 | } |
| 607 | else if (win) |
| 608 | { |
| 609 | width = win->getWidth(); |
| 610 | height = win->getHeight(); |
| 611 | windowToDPICoords(&width, &height); |
| 612 | } |
| 613 | |
| 614 | vargs.emplace_back(width); |
| 615 | vargs.emplace_back(height); |
| 616 | msg = new Message("resize" , vargs); |
| 617 | } |
| 618 | break; |
| 619 | case SDL_WINDOWEVENT_SIZE_CHANGED: |
| 620 | win = Module::getInstance<window::Window>(Module::M_WINDOW); |
| 621 | if (win) |
| 622 | win->onSizeChanged(e.window.data1, e.window.data2); |
| 623 | break; |
| 624 | case SDL_WINDOWEVENT_MINIMIZED: |
| 625 | case SDL_WINDOWEVENT_RESTORED: |
| 626 | #ifdef LOVE_ANDROID |
| 627 | if (auto audio = Module::getInstance<audio::Audio>(Module::M_AUDIO)) |
| 628 | { |
| 629 | if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) |
| 630 | audio->pauseContext(); |
| 631 | else if (e.window.event == SDL_WINDOWEVENT_RESTORED) |
| 632 | audio->resumeContext(); |
| 633 | } |
| 634 | #endif |
| 635 | break; |
| 636 | } |
| 637 | |
| 638 | return msg; |
| 639 | } |
| 640 | |
| 641 | std::map<SDL_Keycode, love::keyboard::Keyboard::Key> Event::createKeyMap() |
| 642 | { |
| 643 | using love::keyboard::Keyboard; |
| 644 | |
| 645 | std::map<SDL_Keycode, Keyboard::Key> k; |
| 646 | |
| 647 | k[SDLK_UNKNOWN] = Keyboard::KEY_UNKNOWN; |
| 648 | |
| 649 | k[SDLK_RETURN] = Keyboard::KEY_RETURN; |
| 650 | k[SDLK_ESCAPE] = Keyboard::KEY_ESCAPE; |
| 651 | k[SDLK_BACKSPACE] = Keyboard::KEY_BACKSPACE; |
| 652 | k[SDLK_TAB] = Keyboard::KEY_TAB; |
| 653 | k[SDLK_SPACE] = Keyboard::KEY_SPACE; |
| 654 | k[SDLK_EXCLAIM] = Keyboard::KEY_EXCLAIM; |
| 655 | k[SDLK_QUOTEDBL] = Keyboard::KEY_QUOTEDBL; |
| 656 | k[SDLK_HASH] = Keyboard::KEY_HASH; |
| 657 | k[SDLK_PERCENT] = Keyboard::KEY_PERCENT; |
| 658 | k[SDLK_DOLLAR] = Keyboard::KEY_DOLLAR; |
| 659 | k[SDLK_AMPERSAND] = Keyboard::KEY_AMPERSAND; |
| 660 | k[SDLK_QUOTE] = Keyboard::KEY_QUOTE; |
| 661 | k[SDLK_LEFTPAREN] = Keyboard::KEY_LEFTPAREN; |
| 662 | k[SDLK_RIGHTPAREN] = Keyboard::KEY_RIGHTPAREN; |
| 663 | k[SDLK_ASTERISK] = Keyboard::KEY_ASTERISK; |
| 664 | k[SDLK_PLUS] = Keyboard::KEY_PLUS; |
| 665 | k[SDLK_COMMA] = Keyboard::KEY_COMMA; |
| 666 | k[SDLK_MINUS] = Keyboard::KEY_MINUS; |
| 667 | k[SDLK_PERIOD] = Keyboard::KEY_PERIOD; |
| 668 | k[SDLK_SLASH] = Keyboard::KEY_SLASH; |
| 669 | k[SDLK_0] = Keyboard::KEY_0; |
| 670 | k[SDLK_1] = Keyboard::KEY_1; |
| 671 | k[SDLK_2] = Keyboard::KEY_2; |
| 672 | k[SDLK_3] = Keyboard::KEY_3; |
| 673 | k[SDLK_4] = Keyboard::KEY_4; |
| 674 | k[SDLK_5] = Keyboard::KEY_5; |
| 675 | k[SDLK_6] = Keyboard::KEY_6; |
| 676 | k[SDLK_7] = Keyboard::KEY_7; |
| 677 | k[SDLK_8] = Keyboard::KEY_8; |
| 678 | k[SDLK_9] = Keyboard::KEY_9; |
| 679 | k[SDLK_COLON] = Keyboard::KEY_COLON; |
| 680 | k[SDLK_SEMICOLON] = Keyboard::KEY_SEMICOLON; |
| 681 | k[SDLK_LESS] = Keyboard::KEY_LESS; |
| 682 | k[SDLK_EQUALS] = Keyboard::KEY_EQUALS; |
| 683 | k[SDLK_GREATER] = Keyboard::KEY_GREATER; |
| 684 | k[SDLK_QUESTION] = Keyboard::KEY_QUESTION; |
| 685 | k[SDLK_AT] = Keyboard::KEY_AT; |
| 686 | |
| 687 | k[SDLK_LEFTBRACKET] = Keyboard::KEY_LEFTBRACKET; |
| 688 | k[SDLK_BACKSLASH] = Keyboard::KEY_BACKSLASH; |
| 689 | k[SDLK_RIGHTBRACKET] = Keyboard::KEY_RIGHTBRACKET; |
| 690 | k[SDLK_CARET] = Keyboard::KEY_CARET; |
| 691 | k[SDLK_UNDERSCORE] = Keyboard::KEY_UNDERSCORE; |
| 692 | k[SDLK_BACKQUOTE] = Keyboard::KEY_BACKQUOTE; |
| 693 | k[SDLK_a] = Keyboard::KEY_A; |
| 694 | k[SDLK_b] = Keyboard::KEY_B; |
| 695 | k[SDLK_c] = Keyboard::KEY_C; |
| 696 | k[SDLK_d] = Keyboard::KEY_D; |
| 697 | k[SDLK_e] = Keyboard::KEY_E; |
| 698 | k[SDLK_f] = Keyboard::KEY_F; |
| 699 | k[SDLK_g] = Keyboard::KEY_G; |
| 700 | k[SDLK_h] = Keyboard::KEY_H; |
| 701 | k[SDLK_i] = Keyboard::KEY_I; |
| 702 | k[SDLK_j] = Keyboard::KEY_J; |
| 703 | k[SDLK_k] = Keyboard::KEY_K; |
| 704 | k[SDLK_l] = Keyboard::KEY_L; |
| 705 | k[SDLK_m] = Keyboard::KEY_M; |
| 706 | k[SDLK_n] = Keyboard::KEY_N; |
| 707 | k[SDLK_o] = Keyboard::KEY_O; |
| 708 | k[SDLK_p] = Keyboard::KEY_P; |
| 709 | k[SDLK_q] = Keyboard::KEY_Q; |
| 710 | k[SDLK_r] = Keyboard::KEY_R; |
| 711 | k[SDLK_s] = Keyboard::KEY_S; |
| 712 | k[SDLK_t] = Keyboard::KEY_T; |
| 713 | k[SDLK_u] = Keyboard::KEY_U; |
| 714 | k[SDLK_v] = Keyboard::KEY_V; |
| 715 | k[SDLK_w] = Keyboard::KEY_W; |
| 716 | k[SDLK_x] = Keyboard::KEY_X; |
| 717 | k[SDLK_y] = Keyboard::KEY_Y; |
| 718 | k[SDLK_z] = Keyboard::KEY_Z; |
| 719 | |
| 720 | k[SDLK_CAPSLOCK] = Keyboard::KEY_CAPSLOCK; |
| 721 | |
| 722 | k[SDLK_F1] = Keyboard::KEY_F1; |
| 723 | k[SDLK_F2] = Keyboard::KEY_F2; |
| 724 | k[SDLK_F3] = Keyboard::KEY_F3; |
| 725 | k[SDLK_F4] = Keyboard::KEY_F4; |
| 726 | k[SDLK_F5] = Keyboard::KEY_F5; |
| 727 | k[SDLK_F6] = Keyboard::KEY_F6; |
| 728 | k[SDLK_F7] = Keyboard::KEY_F7; |
| 729 | k[SDLK_F8] = Keyboard::KEY_F8; |
| 730 | k[SDLK_F9] = Keyboard::KEY_F9; |
| 731 | k[SDLK_F10] = Keyboard::KEY_F10; |
| 732 | k[SDLK_F11] = Keyboard::KEY_F11; |
| 733 | k[SDLK_F12] = Keyboard::KEY_F12; |
| 734 | |
| 735 | k[SDLK_PRINTSCREEN] = Keyboard::KEY_PRINTSCREEN; |
| 736 | k[SDLK_SCROLLLOCK] = Keyboard::KEY_SCROLLLOCK; |
| 737 | k[SDLK_PAUSE] = Keyboard::KEY_PAUSE; |
| 738 | k[SDLK_INSERT] = Keyboard::KEY_INSERT; |
| 739 | k[SDLK_HOME] = Keyboard::KEY_HOME; |
| 740 | k[SDLK_PAGEUP] = Keyboard::KEY_PAGEUP; |
| 741 | k[SDLK_DELETE] = Keyboard::KEY_DELETE; |
| 742 | k[SDLK_END] = Keyboard::KEY_END; |
| 743 | k[SDLK_PAGEDOWN] = Keyboard::KEY_PAGEDOWN; |
| 744 | k[SDLK_RIGHT] = Keyboard::KEY_RIGHT; |
| 745 | k[SDLK_LEFT] = Keyboard::KEY_LEFT; |
| 746 | k[SDLK_DOWN] = Keyboard::KEY_DOWN; |
| 747 | k[SDLK_UP] = Keyboard::KEY_UP; |
| 748 | |
| 749 | k[SDLK_NUMLOCKCLEAR] = Keyboard::KEY_NUMLOCKCLEAR; |
| 750 | k[SDLK_KP_DIVIDE] = Keyboard::KEY_KP_DIVIDE; |
| 751 | k[SDLK_KP_MULTIPLY] = Keyboard::KEY_KP_MULTIPLY; |
| 752 | k[SDLK_KP_MINUS] = Keyboard::KEY_KP_MINUS; |
| 753 | k[SDLK_KP_PLUS] = Keyboard::KEY_KP_PLUS; |
| 754 | k[SDLK_KP_ENTER] = Keyboard::KEY_KP_ENTER; |
| 755 | k[SDLK_KP_0] = Keyboard::KEY_KP_0; |
| 756 | k[SDLK_KP_1] = Keyboard::KEY_KP_1; |
| 757 | k[SDLK_KP_2] = Keyboard::KEY_KP_2; |
| 758 | k[SDLK_KP_3] = Keyboard::KEY_KP_3; |
| 759 | k[SDLK_KP_4] = Keyboard::KEY_KP_4; |
| 760 | k[SDLK_KP_5] = Keyboard::KEY_KP_5; |
| 761 | k[SDLK_KP_6] = Keyboard::KEY_KP_6; |
| 762 | k[SDLK_KP_7] = Keyboard::KEY_KP_7; |
| 763 | k[SDLK_KP_8] = Keyboard::KEY_KP_8; |
| 764 | k[SDLK_KP_9] = Keyboard::KEY_KP_9; |
| 765 | k[SDLK_KP_PERIOD] = Keyboard::KEY_KP_PERIOD; |
| 766 | k[SDLK_KP_COMMA] = Keyboard::KEY_KP_COMMA; |
| 767 | k[SDLK_KP_EQUALS] = Keyboard::KEY_KP_EQUALS; |
| 768 | |
| 769 | k[SDLK_APPLICATION] = Keyboard::KEY_APPLICATION; |
| 770 | k[SDLK_POWER] = Keyboard::KEY_POWER; |
| 771 | k[SDLK_F13] = Keyboard::KEY_F13; |
| 772 | k[SDLK_F14] = Keyboard::KEY_F14; |
| 773 | k[SDLK_F15] = Keyboard::KEY_F15; |
| 774 | k[SDLK_F16] = Keyboard::KEY_F16; |
| 775 | k[SDLK_F17] = Keyboard::KEY_F17; |
| 776 | k[SDLK_F18] = Keyboard::KEY_F18; |
| 777 | k[SDLK_F19] = Keyboard::KEY_F19; |
| 778 | k[SDLK_F20] = Keyboard::KEY_F20; |
| 779 | k[SDLK_F21] = Keyboard::KEY_F21; |
| 780 | k[SDLK_F22] = Keyboard::KEY_F22; |
| 781 | k[SDLK_F23] = Keyboard::KEY_F23; |
| 782 | k[SDLK_F24] = Keyboard::KEY_F24; |
| 783 | k[SDLK_EXECUTE] = Keyboard::KEY_EXECUTE; |
| 784 | k[SDLK_HELP] = Keyboard::KEY_HELP; |
| 785 | k[SDLK_MENU] = Keyboard::KEY_MENU; |
| 786 | k[SDLK_SELECT] = Keyboard::KEY_SELECT; |
| 787 | k[SDLK_STOP] = Keyboard::KEY_STOP; |
| 788 | k[SDLK_AGAIN] = Keyboard::KEY_AGAIN; |
| 789 | k[SDLK_UNDO] = Keyboard::KEY_UNDO; |
| 790 | k[SDLK_CUT] = Keyboard::KEY_CUT; |
| 791 | k[SDLK_COPY] = Keyboard::KEY_COPY; |
| 792 | k[SDLK_PASTE] = Keyboard::KEY_PASTE; |
| 793 | k[SDLK_FIND] = Keyboard::KEY_FIND; |
| 794 | k[SDLK_MUTE] = Keyboard::KEY_MUTE; |
| 795 | k[SDLK_VOLUMEUP] = Keyboard::KEY_VOLUMEUP; |
| 796 | k[SDLK_VOLUMEDOWN] = Keyboard::KEY_VOLUMEDOWN; |
| 797 | |
| 798 | k[SDLK_ALTERASE] = Keyboard::KEY_ALTERASE; |
| 799 | k[SDLK_SYSREQ] = Keyboard::KEY_SYSREQ; |
| 800 | k[SDLK_CANCEL] = Keyboard::KEY_CANCEL; |
| 801 | k[SDLK_CLEAR] = Keyboard::KEY_CLEAR; |
| 802 | k[SDLK_PRIOR] = Keyboard::KEY_PRIOR; |
| 803 | k[SDLK_RETURN2] = Keyboard::KEY_RETURN2; |
| 804 | k[SDLK_SEPARATOR] = Keyboard::KEY_SEPARATOR; |
| 805 | k[SDLK_OUT] = Keyboard::KEY_OUT; |
| 806 | k[SDLK_OPER] = Keyboard::KEY_OPER; |
| 807 | k[SDLK_CLEARAGAIN] = Keyboard::KEY_CLEARAGAIN; |
| 808 | |
| 809 | k[SDLK_THOUSANDSSEPARATOR] = Keyboard::KEY_THOUSANDSSEPARATOR; |
| 810 | k[SDLK_DECIMALSEPARATOR] = Keyboard::KEY_DECIMALSEPARATOR; |
| 811 | k[SDLK_CURRENCYUNIT] = Keyboard::KEY_CURRENCYUNIT; |
| 812 | k[SDLK_CURRENCYSUBUNIT] = Keyboard::KEY_CURRENCYSUBUNIT; |
| 813 | |
| 814 | k[SDLK_LCTRL] = Keyboard::KEY_LCTRL; |
| 815 | k[SDLK_LSHIFT] = Keyboard::KEY_LSHIFT; |
| 816 | k[SDLK_LALT] = Keyboard::KEY_LALT; |
| 817 | k[SDLK_LGUI] = Keyboard::KEY_LGUI; |
| 818 | k[SDLK_RCTRL] = Keyboard::KEY_RCTRL; |
| 819 | k[SDLK_RSHIFT] = Keyboard::KEY_RSHIFT; |
| 820 | k[SDLK_RALT] = Keyboard::KEY_RALT; |
| 821 | k[SDLK_RGUI] = Keyboard::KEY_RGUI; |
| 822 | |
| 823 | k[SDLK_MODE] = Keyboard::KEY_MODE; |
| 824 | |
| 825 | k[SDLK_AUDIONEXT] = Keyboard::KEY_AUDIONEXT; |
| 826 | k[SDLK_AUDIOPREV] = Keyboard::KEY_AUDIOPREV; |
| 827 | k[SDLK_AUDIOSTOP] = Keyboard::KEY_AUDIOSTOP; |
| 828 | k[SDLK_AUDIOPLAY] = Keyboard::KEY_AUDIOPLAY; |
| 829 | k[SDLK_AUDIOMUTE] = Keyboard::KEY_AUDIOMUTE; |
| 830 | k[SDLK_MEDIASELECT] = Keyboard::KEY_MEDIASELECT; |
| 831 | k[SDLK_WWW] = Keyboard::KEY_WWW; |
| 832 | k[SDLK_MAIL] = Keyboard::KEY_MAIL; |
| 833 | k[SDLK_CALCULATOR] = Keyboard::KEY_CALCULATOR; |
| 834 | k[SDLK_COMPUTER] = Keyboard::KEY_COMPUTER; |
| 835 | k[SDLK_AC_SEARCH] = Keyboard::KEY_APP_SEARCH; |
| 836 | k[SDLK_AC_HOME] = Keyboard::KEY_APP_HOME; |
| 837 | k[SDLK_AC_BACK] = Keyboard::KEY_APP_BACK; |
| 838 | k[SDLK_AC_FORWARD] = Keyboard::KEY_APP_FORWARD; |
| 839 | k[SDLK_AC_STOP] = Keyboard::KEY_APP_STOP; |
| 840 | k[SDLK_AC_REFRESH] = Keyboard::KEY_APP_REFRESH; |
| 841 | k[SDLK_AC_BOOKMARKS] = Keyboard::KEY_APP_BOOKMARKS; |
| 842 | |
| 843 | k[SDLK_BRIGHTNESSDOWN] = Keyboard::KEY_BRIGHTNESSDOWN; |
| 844 | k[SDLK_BRIGHTNESSUP] = Keyboard::KEY_BRIGHTNESSUP; |
| 845 | k[SDLK_DISPLAYSWITCH] = Keyboard::KEY_DISPLAYSWITCH; |
| 846 | k[SDLK_KBDILLUMTOGGLE] = Keyboard::KEY_KBDILLUMTOGGLE; |
| 847 | k[SDLK_KBDILLUMDOWN] = Keyboard::KEY_KBDILLUMDOWN; |
| 848 | k[SDLK_KBDILLUMUP] = Keyboard::KEY_KBDILLUMUP; |
| 849 | k[SDLK_EJECT] = Keyboard::KEY_EJECT; |
| 850 | k[SDLK_SLEEP] = Keyboard::KEY_SLEEP; |
| 851 | |
| 852 | #ifdef LOVE_ANDROID |
| 853 | k[SDLK_AC_BACK] = Keyboard::KEY_ESCAPE; |
| 854 | #endif |
| 855 | |
| 856 | return k; |
| 857 | } |
| 858 | |
| 859 | std::map<SDL_Keycode, love::keyboard::Keyboard::Key> Event::keys = Event::createKeyMap(); |
| 860 | |
| 861 | } // sdl |
| 862 | } // event |
| 863 | } // love |
| 864 | |