diff options
Diffstat (limited to 'backends/events/sdl/sdl-events.cpp')
-rw-r--r-- | backends/events/sdl/sdl-events.cpp | 312 |
1 files changed, 252 insertions, 60 deletions
diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index 2480e7c370..acc1ff5dce 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -49,22 +49,55 @@ #define JOY_BUT_SPACE 4 #define JOY_BUT_F5 5 +#if SDL_VERSION_ATLEAST(2, 0, 0) +static uint32 convUTF8ToUTF32(const char *src) { + uint32 utf32 = 0; + + char *dst = SDL_iconv_string( +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + "UTF-32BE", +#else + "UTF-32LE", +#endif + "UTF-8", src, SDL_strlen(src) + 1); + + if (dst) { + utf32 = *((uint32 *)dst); + SDL_free(dst); + } + + return utf32; +} +#endif + SdlEventSource::SdlEventSource() - : EventSource(), _scrollLock(false), _joystick(0), _lastScreenID(0), _graphicsManager(0) { + : EventSource(), _scrollLock(false), _joystick(0), _lastScreenID(0), _graphicsManager(0) +#if SDL_VERSION_ATLEAST(2, 0, 0) + , _queuedFakeKeyUp(false), _fakeKeyUp() +#endif + { // Reset mouse state memset(&_km, 0, sizeof(_km)); int joystick_num = ConfMan.getInt("joystick_num"); - if (joystick_num > -1) { + if (joystick_num >= 0) { // Initialize SDL joystick subsystem if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) { error("Could not initialize SDL: %s", SDL_GetError()); } // Enable joystick - if (SDL_NumJoysticks() > 0) { - debug("Using joystick: %s", SDL_JoystickName(0)); + if (SDL_NumJoysticks() > joystick_num) { _joystick = SDL_JoystickOpen(joystick_num); + debug("Using joystick: %s", +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_JoystickName(_joystick) +#else + SDL_JoystickName(joystick_num) +#endif + ); + } else { + warning("Invalid joystick: %d", joystick_num); } } } @@ -74,21 +107,24 @@ SdlEventSource::~SdlEventSource() { SDL_JoystickClose(_joystick); } -int SdlEventSource::mapKey(SDLKey key, SDLMod mod, Uint16 unicode) { - if (key >= SDLK_F1 && key <= SDLK_F9) { - return key - SDLK_F1 + Common::ASCII_F1; - } else if (key >= SDLK_KP0 && key <= SDLK_KP9) { - return key - SDLK_KP0 + '0'; - } else if (key >= SDLK_UP && key <= SDLK_PAGEDOWN) { +int SdlEventSource::mapKey(SDLKey sdlKey, SDLMod mod, Uint16 unicode) { + Common::KeyCode key = SDLToOSystemKeycode(sdlKey); + + if (key >= Common::KEYCODE_F1 && key <= Common::KEYCODE_F9) { + return key - Common::KEYCODE_F1 + Common::ASCII_F1; + } else if (key >= Common::KEYCODE_KP0 && key <= Common::KEYCODE_KP9) { + return key - Common::KEYCODE_KP0 + '0'; + } else if (key >= Common::KEYCODE_UP && key <= Common::KEYCODE_PAGEDOWN) { return key; } else if (unicode) { return unicode; } else if (key >= 'a' && key <= 'z' && (mod & KMOD_SHIFT)) { return key & ~0x20; - } else if (key >= SDLK_NUMLOCK && key <= SDLK_EURO) { + } else if (key >= Common::KEYCODE_NUMLOCK && key <= Common::KEYCODE_EURO) { return 0; + } else { + return key; } - return key; } void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y) { @@ -171,7 +207,9 @@ void SdlEventSource::handleKbdMouse() { _km.y_down_count = 1; } - SDL_WarpMouse((Uint16)_km.x, (Uint16)_km.y); + if (_graphicsManager) { + _graphicsManager->getWindow()->warpMouseInWindow((Uint16)_km.x, (Uint16)_km.y); + } } } } @@ -276,7 +314,7 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDLKey key) { case SDLK_y: return Common::KEYCODE_y; case SDLK_z: return Common::KEYCODE_z; case SDLK_DELETE: return Common::KEYCODE_DELETE; -#if SDL_VERSION_ATLEAST(1, 3, 0) +#if SDL_VERSION_ATLEAST(2, 0, 0) case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_GRAVE): return Common::KEYCODE_TILDE; #else case SDLK_WORLD_16: return Common::KEYCODE_TILDE; @@ -338,7 +376,9 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDLKey key) { case SDLK_HELP: return Common::KEYCODE_HELP; case SDLK_PRINT: return Common::KEYCODE_PRINT; case SDLK_SYSREQ: return Common::KEYCODE_SYSREQ; +#if !SDL_VERSION_ATLEAST(2, 0, 0) case SDLK_BREAK: return Common::KEYCODE_BREAK; +#endif case SDLK_MENU: return Common::KEYCODE_MENU; case SDLK_POWER: return Common::KEYCODE_POWER; case SDLK_UNDO: return Common::KEYCODE_UNDO; @@ -349,6 +389,16 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDLKey key) { bool SdlEventSource::pollEvent(Common::Event &event) { handleKbdMouse(); +#if SDL_VERSION_ATLEAST(2, 0, 0) + // In case we still need to send a key up event for a key down from a + // TEXTINPUT event we do this immediately. + if (_queuedFakeKeyUp) { + event = _fakeKeyUp; + _queuedFakeKeyUp = false; + return true; + } +#endif + // If the screen changed, send an Common::EVENT_SCREEN_CHANGED int screenID = ((OSystem_SDL *)g_system)->getGraphicsManager()->getScreenChangeID(); if (screenID != _lastScreenID) { @@ -385,24 +435,73 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { case SDL_JOYAXISMOTION: return handleJoyAxisMotion(ev, event); +#if SDL_VERSION_ATLEAST(2, 0, 0) + case SDL_MOUSEWHEEL: { + Sint32 yDir = ev.wheel.y; +#if SDL_VERSION_ATLEAST(2, 0, 4) + if (ev.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) { + yDir *= -1; + } +#endif + // HACK: It seems we want the mouse coordinates supplied + // with a mouse wheel event. However, SDL2 does not supply + // these, thus we use whatever we got last time. It seems + // these are always stored in _km.x, _km.y. + processMouseEvent(event, _km.x, _km.y); + if (yDir < 0) { + event.type = Common::EVENT_WHEELDOWN; + return true; + } else if (yDir > 0) { + event.type = Common::EVENT_WHEELUP; + return true; + } else { + return false; + } + } + + case SDL_TEXTINPUT: { + // When we get a TEXTINPUT event it means we got some user input for + // which no KEYDOWN exists. SDL 1.2 introduces a "fake" key down+up + // in such cases. We will do the same to mimic it's behavior. + event.type = Common::EVENT_KEYDOWN; + + event.kbd = Common::KeyState(Common::KEYCODE_INVALID, convUTF8ToUTF32(ev.text.text), 0); + + SDLModToOSystemKeyFlags(SDL_GetModState(), event); + // Set the scroll lock sticky flag + if (_scrollLock) + event.kbd.flags |= Common::KBD_SCRL; + + // Fake a key up when we have a proper ascii value. + _queuedFakeKeyUp = (event.kbd.ascii != 0); + _fakeKeyUp = event; + _fakeKeyUp.type = Common::EVENT_KEYUP; + + return _queuedFakeKeyUp; + } + + case SDL_WINDOWEVENT: + switch (ev.window.event) { + case SDL_WINDOWEVENT_EXPOSED: + if (_graphicsManager) + _graphicsManager->notifyVideoExpose(); + return false; + + case SDL_WINDOWEVENT_RESIZED: + return handleResizeEvent(event, ev.window.data1, ev.window.data2); + + default: + return false; + } +#else case SDL_VIDEOEXPOSE: if (_graphicsManager) _graphicsManager->notifyVideoExpose(); return false; case SDL_VIDEORESIZE: - if (_graphicsManager) { - _graphicsManager->notifyResize(ev.resize.w, ev.resize.h); - - // If the screen changed, send an Common::EVENT_SCREEN_CHANGED - int screenID = ((OSystem_SDL *)g_system)->getGraphicsManager()->getScreenChangeID(); - if (screenID != _lastScreenID) { - _lastScreenID = screenID; - event.type = Common::EVENT_SCREEN_CHANGED; - return true; - } - } - return false; + return handleResizeEvent(event, ev.resize.w, ev.resize.h); +#endif case SDL_QUIT: event.type = Common::EVENT_QUIT; @@ -418,41 +517,45 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) { SDLModToOSystemKeyFlags(SDL_GetModState(), event); + SDLKey sdlKeycode = obtainKeycode(ev.key.keysym); + // Handle scroll lock as a key modifier - if (ev.key.keysym.sym == SDLK_SCROLLOCK) + if (sdlKeycode == SDLK_SCROLLOCK) _scrollLock = !_scrollLock; if (_scrollLock) event.kbd.flags |= Common::KBD_SCRL; // Ctrl-m toggles mouse capture - if (event.kbd.hasFlags(Common::KBD_CTRL) && ev.key.keysym.sym == 'm') { - toggleMouseGrab(); + if (event.kbd.hasFlags(Common::KBD_CTRL) && sdlKeycode == 'm') { + if (_graphicsManager) { + _graphicsManager->getWindow()->toggleMouseGrab(); + } return false; } #if defined(MACOSX) // On Macintosh, Cmd-Q quits - if ((ev.key.keysym.mod & KMOD_META) && ev.key.keysym.sym == 'q') { + if ((ev.key.keysym.mod & KMOD_META) && sdlKeycode == 'q') { event.type = Common::EVENT_QUIT; return true; } #elif defined(POSIX) // On other *nix systems, Control-Q quits - if ((ev.key.keysym.mod & KMOD_CTRL) && ev.key.keysym.sym == 'q') { + if ((ev.key.keysym.mod & KMOD_CTRL) && sdlKeycode == 'q') { event.type = Common::EVENT_QUIT; return true; } #else - // Ctrl-z and Alt-X quit - if ((event.kbd.hasFlags(Common::KBD_CTRL) && ev.key.keysym.sym == 'z') || (event.kbd.hasFlags(Common::KBD_ALT) && ev.key.keysym.sym == 'x')) { + // Ctrl-z quits + if ((event.kbd.hasFlags(Common::KBD_CTRL) && sdlKeycode == 'z')) { event.type = Common::EVENT_QUIT; return true; } #ifdef WIN32 // On Windows, also use the default Alt-F4 quit combination - if ((ev.key.keysym.mod & KMOD_ALT) && ev.key.keysym.sym == SDLK_F4) { + if ((ev.key.keysym.mod & KMOD_ALT) && sdlKeycode == SDLK_F4) { event.type = Common::EVENT_QUIT; return true; } @@ -460,7 +563,7 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) { #endif // Ctrl-u toggles mute - if ((ev.key.keysym.mod & KMOD_CTRL) && ev.key.keysym.sym == 'u') { + if ((ev.key.keysym.mod & KMOD_CTRL) && sdlKeycode == 'u') { event.type = Common::EVENT_MUTE; return true; } @@ -469,8 +572,8 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) { return true; event.type = Common::EVENT_KEYDOWN; - event.kbd.keycode = SDLToOSystemKeycode(ev.key.keysym.sym); - event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, (Uint16)ev.key.keysym.unicode); + event.kbd.keycode = SDLToOSystemKeycode(sdlKeycode); + event.kbd.ascii = mapKey(sdlKeycode, (SDLMod)ev.key.keysym.mod, obtainUnicode(ev.key.keysym)); return true; } @@ -479,6 +582,7 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) { if (remapKey(ev, event)) return true; + SDLKey sdlKeycode = obtainKeycode(ev.key.keysym); SDLMod mod = SDL_GetModState(); // Check if this is an event handled by handleKeyDown(), and stop if it is @@ -486,35 +590,30 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) { // Check if the Ctrl key is down, so that we can trap cases where the // user has the Ctrl key down, and has just released a special key if (mod & KMOD_CTRL) { - if (ev.key.keysym.sym == 'm' || // Ctrl-m toggles mouse capture + if (sdlKeycode == 'm' || // Ctrl-m toggles mouse capture #if defined(MACOSX) // Meta - Q, handled below #elif defined(POSIX) - ev.key.keysym.sym == 'q' || // On other *nix systems, Control-Q quits + sdlKeycode == 'q' || // On other *nix systems, Control-Q quits #else - ev.key.keysym.sym == 'z' || // Ctrl-z quit + sdlKeycode == 'z' || // Ctrl-z quit #endif - ev.key.keysym.sym == 'u') // Ctrl-u toggles mute + sdlKeycode == 'u') // Ctrl-u toggles mute return false; } // Same for other keys (Meta and Alt) #if defined(MACOSX) - if ((mod & KMOD_META) && ev.key.keysym.sym == 'q') + if ((mod & KMOD_META) && sdlKeycode == 'q') return false; // On Macintosh, Cmd-Q quits -#elif defined(POSIX) - // Control Q has already been handled above -#else - if ((mod & KMOD_ALT) && ev.key.keysym.sym == 'x') - return false; // Alt-x quit #endif // If we reached here, this isn't an event handled by handleKeyDown(), thus // continue normally event.type = Common::EVENT_KEYUP; - event.kbd.keycode = SDLToOSystemKeycode(ev.key.keysym.sym); - event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, (Uint16)ev.key.keysym.unicode); + event.kbd.keycode = SDLToOSystemKeycode(sdlKeycode); + event.kbd.ascii = mapKey(sdlKeycode, (SDLMod)ev.key.keysym.mod, 0); // Ctrl-Alt-<key> will change the GFX mode SDLModToOSystemKeyFlags(mod, event); @@ -636,16 +735,16 @@ bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) { bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) { int axis = ev.jaxis.value; - if ( axis > JOY_DEADZONE) { + if (axis > JOY_DEADZONE) { axis -= JOY_DEADZONE; event.type = Common::EVENT_MOUSEMOVE; - } else if ( axis < -JOY_DEADZONE ) { + } else if (axis < -JOY_DEADZONE) { axis += JOY_DEADZONE; event.type = Common::EVENT_MOUSEMOVE; } else axis = 0; - if ( ev.jaxis.axis == JOY_XAXIS) { + if (ev.jaxis.axis == JOY_XAXIS) { #ifdef JOY_ANALOG _km.x_vel = axis / 2000; _km.x_down_count = 0; @@ -658,7 +757,6 @@ bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) { _km.x_down_count = 0; } #endif - } else if (ev.jaxis.axis == JOY_YAXIS) { #ifndef JOY_INVERT_Y axis = -axis; @@ -750,13 +848,6 @@ bool SdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) { return false; } -void SdlEventSource::toggleMouseGrab() { - if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) - SDL_WM_GrabInput(SDL_GRAB_ON); - else - SDL_WM_GrabInput(SDL_GRAB_OFF); -} - void SdlEventSource::resetKeyboadEmulation(int16 x_max, int16 y_max) { _km.x_max = x_max; _km.y_max = y_max; @@ -764,4 +855,105 @@ void SdlEventSource::resetKeyboadEmulation(int16 x_max, int16 y_max) { _km.last_time = 0; } +bool SdlEventSource::handleResizeEvent(Common::Event &event, int w, int h) { + if (_graphicsManager) { + _graphicsManager->notifyResize(w, h); + + // If the screen changed, send an Common::EVENT_SCREEN_CHANGED + int screenID = ((OSystem_SDL *)g_system)->getGraphicsManager()->getScreenChangeID(); + if (screenID != _lastScreenID) { + _lastScreenID = screenID; + event.type = Common::EVENT_SCREEN_CHANGED; + return true; + } + } + + return false; +} + +SDLKey SdlEventSource::obtainKeycode(const SDL_keysym keySym) { +#if !SDL_VERSION_ATLEAST(2, 0, 0) && defined(WIN32) && !defined(_WIN32_WCE) + // WORKAROUND: SDL 1.2 on Windows does not use the user configured keyboard layout, + // resulting in "keySym.sym" values to always be those expected for an US keyboard. + // For example, SDL returns SDLK_Q when pressing the 'A' key on an AZERTY keyboard. + // This defeats the purpose of keycodes which is to be able to refer to a key without + // knowing where it is physically located. + // We work around this issue by querying the currently active Windows keyboard layout + // using the scancode provided by SDL. + + if (keySym.sym >= SDLK_0 && keySym.sym <= SDLK_9) { + // The keycode returned by SDL is kept for the number keys. + // Querying the keyboard layout for those would return the base key values + // for AZERTY keyboards, which are not numbers. For example, SDLK_1 would + // map to SDLK_AMPERSAND. This is theoretically correct but practically unhelpful, + // because it makes it impossible to handle key combinations such as "ctrl-1". + return keySym.sym; + } + + int vk = MapVirtualKey(keySym.scancode, MAPVK_VSC_TO_VK); + if (vk) { + int ch = (MapVirtualKey(vk, MAPVK_VK_TO_CHAR) & 0x7FFF); + // The top bit of the result of MapVirtualKey with MAPVK_VSC_TO_VK signals + // a dead key was pressed. In that case we keep the value of the accent alone. + if (ch) { + if (ch >= 'A' && ch <= 'Z') { + // Windows returns uppercase ASCII whereas SDL expects lowercase + return (SDLKey)(SDLK_a + (ch - 'A')); + } else { + return (SDLKey)ch; + } + } + } +#endif + + return keySym.sym; +} + +uint32 SdlEventSource::obtainUnicode(const SDL_keysym keySym) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Event events[2]; + + // Update the event queue here to give SDL a chance to insert TEXTINPUT + // events for KEYDOWN events. Otherwise we have a high chance that on + // Windows the TEXTINPUT event is not in the event queue at this point. + // In this case we will get two events with ascii values due to mapKey + // and dispatchSDLEvent. This results in nasty double input of characters + // in the GUI. + // + // FIXME: This is all a bit fragile because in mapKey we derive the ascii + // value from the key code if no unicode value is given. This is legacy + // behavior and should be removed anyway. If that is removed, we might not + // even need to do this peeking here but instead can rely on the + // SDL_TEXTINPUT case in dispatchSDLEvent to introduce keydown/keyup with + // proper ASCII values (but with KEYCODE_INVALID as keycode). + SDL_PumpEvents(); + + // In SDL2, the unicode field has been removed from the keysym struct. + // Instead a SDL_TEXTINPUT event is generated on key combinations that + // generates unicode. + // Here we peek into the event queue for the event to see if it exists. + int n = SDL_PeepEvents(events, 2, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_TEXTINPUT); + // Make sure that the TEXTINPUT event belongs to this KEYDOWN + // event and not another pending one. + if ((n > 0 && events[0].type == SDL_TEXTINPUT) + || (n > 1 && events[0].type != SDL_KEYDOWN && events[1].type == SDL_TEXTINPUT)) { + // Remove the text input event we associate with the key press. This + // makes sure we never get any SDL_TEXTINPUT events which do "belong" + // to SDL_KEYDOWN events. + n = SDL_PeepEvents(events, 1, SDL_GETEVENT, SDL_TEXTINPUT, SDL_TEXTINPUT); + // This is basically a paranoia safety check because we know there + // must be a text input event in the queue. + if (n > 0) { + return convUTF8ToUTF32(events[0].text.text); + } else { + return 0; + } + } else { + return 0; + } +#else + return keySym.unicode; +#endif +} + #endif |