/* * The ManaPlus Client * Copyright (C) 2012-2020 The ManaPlus Developers * Copyright (C) 2020-2023 The ManaVerse Developers * * This file is part of The ManaPlus Client. * * 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 * 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, see . */ #include "eventsmanager.h" #include "configuration.h" #include "client.h" #ifndef DYECMD #include "game.h" #endif // DYECMD #ifdef USE_MUMBLE #include "mumblemanager.h" #endif // USE_MUMBLE #include "sdlshared.h" #include "settings.h" #include "gui/windowmanager.h" #include "being/localplayer.h" #include "input/inputmanager.h" #ifdef USE_SDL2 #include "render/graphics.h" #else // USE_SDL2 #include "logger.h" #endif // USE_SDL2 #include "utils/process.h" #include "utils/sdlhelper.h" #ifdef USE_X11 #include "utils/x11logger.h" #endif // USE_X11 #include "debug.h" EventsManager eventsManager; EventsManager::EventsManager() : ConfigListener(), mLogInput(false) { } EventsManager::~EventsManager() { CHECKLISTENERS } void EventsManager::init() { mLogInput = config.getBoolValue("logInput"); config.addListener("logInput", this); } void EventsManager::enableEvents() { // disable unused SDL events #ifndef USE_SDL2 SDL_EventState(SDL_VIDEOEXPOSE, SDL_IGNORE); #endif // USE_SDL2 SDL_EventState(SDL_SYSWMEVENT, mLogInput ? SDL_ENABLE : SDL_IGNORE); SDL_EventState(SDL_USEREVENT, SDL_IGNORE); } void EventsManager::shutdown() { config.removeListeners(this); } bool EventsManager::handleCommonEvents(const SDL_Event &event) const { BLOCK_START("EventsManager::handleCommonEvents") if (mLogInput) logEvent(event); switch (event.type) { case SDL_QUIT: client->setState(State::EXIT); logger->log1("force exit"); BLOCK_END("EventsManager::handleCommonEvents") return true; #ifdef USE_SDL2 case SDL_WINDOWEVENT: handleSDL2WindowEvent(event); BLOCK_END("EventsManager::handleCommonEvents") return true; #else // USE_SDL2 case SDL_VIDEORESIZE: WindowManager::resizeVideo(event.resize.w, event.resize.h, false); BLOCK_END("EventsManager::handleCommonEvents") return true; case SDL_ACTIVEEVENT: handleActive(event); BLOCK_END("EventsManager::handleCommonEvents") return true; #ifdef ANDROID case SDL_KEYBOARDSHOW: WindowManager::updateScreenKeyboard(event.user.code); BLOCK_END("EventsManager::handleCommonEvents") return true; case SDL_ACCELEROMETER: break; #endif // ANDROID #endif // USE_SDL2 default: break; } if (inputManager.handleEvent(event)) { BLOCK_END("EventsManager::handleCommonEvents") return true; } BLOCK_END("EventsManager::handleCommonEvents") return false; } bool EventsManager::handleEvents() const { BLOCK_START("EventsManager::handleEvents") #ifndef DYECMD if (Game::instance() != nullptr) { // Let the game handle the events while it is active Game::instance()->handleInput(); } else #endif // DYECMD { SDL_Event event; // Handle SDL events while (SDL::PollEvent(&event)) { if (!handleCommonEvents(event)) { #ifdef ANDROID #ifndef USE_SDL2 switch (event.type) { case SDL_ACTIVEEVENT: if ((event.active.state & SDL_APPACTIVE) && !event.active.gain) { client->setState(State::EXIT); logger->log1("exit on lost focus"); } break; default: break; } #endif // USE_SDL2 #endif // ANDROID } #ifdef USE_MUMBLE if (localPlayer && mumbleManager) { mumbleManager->setPos(localPlayer->getTileX(), localPlayer->getTileY(), localPlayer->getDirection()); } #endif // USE_MUMBLE } if (client->getState() == State::EXIT) { BLOCK_END("EventsManager::handleEvents") return true; } } BLOCK_END("EventsManager::handleEvents") return false; } void EventsManager::handleGameEvents() const { BLOCK_START("EventsManager::handleGameEvents") #ifndef DYECMD Game *const game = Game::instance(); // Events SDL_Event event; while (SDL::PollEvent(&event)) { if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) game->updateHistory(event); game->checkKeys(); if (handleCommonEvents(event)) break; } // End while #endif // DYECMD BLOCK_END("EventsManager::handleGameEvents") } void EventsManager::optionChanged(const std::string &name) { if (name == "logInput") mLogInput = config.getBoolValue("logInput"); } void EventsManager::logEvent(const SDL_Event &event) { switch (event.type) { #ifdef USE_SDL2 #define winEventLog(name, name2) \ case name: \ str = name2; \ break case SDL_MOUSEMOTION: logger->log("event: SDL_MOUSEMOTION: %u, %d,%d, %d,%d", event.motion.state, event.motion.x, event.motion.y, event.motion.xrel, event.motion.yrel); break; case SDL_FINGERDOWN: { const SDL_TouchFingerEvent &touch = event.tfinger; const int w = mainGraphics->mWidth; const int h = mainGraphics->mHeight; logger->log("event: SDL_FINGERDOWN: %u,%u (%f,%f) (%f,%f) %f", CAST_U32(touch.touchId), CAST_U32(touch.fingerId), touch.x * w, touch.y * w, touch.dx * w, touch.dy * h, touch.pressure); break; } case SDL_FINGERUP: { const SDL_TouchFingerEvent &touch = event.tfinger; const int w = mainGraphics->mWidth; const int h = mainGraphics->mHeight; logger->log("event: SDL_FINGERUP: %u,%u (%f,%f) (%f,%f) %f", CAST_U32(touch.touchId), CAST_U32(touch.fingerId), touch.x * w, touch.y * w, touch.dx * w, touch.dy * h, touch.pressure); break; } case SDL_FINGERMOTION: { const SDL_TouchFingerEvent &touch = event.tfinger; const int w = mainGraphics->mWidth; const int h = mainGraphics->mHeight; logger->log("event: SDL_FINGERMOTION: %u,%u (%f,%f) (%f,%f) %f", CAST_U32(touch.touchId), CAST_U32(touch.fingerId), touch.x * w, touch.y * h, touch.dx * w, touch.dy * h, touch.pressure); break; } case SDL_MULTIGESTURE: { const SDL_MultiGestureEvent &gesture = event.mgesture; const int w = mainGraphics->mWidth; const int h = mainGraphics->mHeight; logger->log("event: SDL_MULTIGESTURE: %u %f,%f (%f,%f) %d", CAST_U32(gesture.touchId), gesture.dTheta, gesture.dDist, gesture.x * w, gesture.y * h, CAST_S32(gesture.numFingers)); break; } case SDL_KEYDOWN: logger->log("event: SDL_KEYDOWN: %s(%d) %s(%d) %d,%d,%u", SDL_GetScancodeName(event.key.keysym.scancode), event.key.keysym.scancode, SDL_GetKeyName(event.key.keysym.sym), event.key.keysym.sym, event.key.state, event.key.repeat, CAST_U32(event.key.keysym.mod)); break; case SDL_KEYUP: logger->log("event: SDL_KEYUP: %s(%d) %s(%d) %d,%d,%u", SDL_GetScancodeName(event.key.keysym.scancode), event.key.keysym.scancode, SDL_GetKeyName(event.key.keysym.sym), event.key.keysym.sym, event.key.state, event.key.repeat, CAST_U32(event.key.keysym.mod)); break; case SDL_WINDOWEVENT: { const int data1 = event.window.data1; const int data2 = event.window.data2; std::string str; switch (event.window.event) { winEventLog(SDL_WINDOWEVENT_NONE, "SDL_WINDOWEVENT_NONE"); winEventLog(SDL_WINDOWEVENT_SHOWN, "SDL_WINDOWEVENT_SHOWN"); winEventLog(SDL_WINDOWEVENT_HIDDEN, "SDL_WINDOWEVENT_HIDDEN"); winEventLog(SDL_WINDOWEVENT_EXPOSED, "SDL_WINDOWEVENT_EXPOSED"); winEventLog(SDL_WINDOWEVENT_MOVED, "SDL_WINDOWEVENT_MOVED"); winEventLog(SDL_WINDOWEVENT_RESIZED, "SDL_WINDOWEVENT_RESIZED"); winEventLog(SDL_WINDOWEVENT_SIZE_CHANGED, "SDL_WINDOWEVENT_SIZE_CHANGED"); winEventLog(SDL_WINDOWEVENT_MINIMIZED, "SDL_WINDOWEVENT_MINIMIZED"); winEventLog(SDL_WINDOWEVENT_MAXIMIZED, "SDL_WINDOWEVENT_MAXIMIZED"); winEventLog(SDL_WINDOWEVENT_RESTORED, "SDL_WINDOWEVENT_RESTORED"); winEventLog(SDL_WINDOWEVENT_ENTER, "SDL_WINDOWEVENT_ENTER"); winEventLog(SDL_WINDOWEVENT_LEAVE, "SDL_WINDOWEVENT_LEAVE"); winEventLog(SDL_WINDOWEVENT_FOCUS_GAINED, "SDL_WINDOWEVENT_FOCUS_GAINED"); winEventLog(SDL_WINDOWEVENT_FOCUS_LOST, "SDL_WINDOWEVENT_FOCUS_LOST"); winEventLog(SDL_WINDOWEVENT_CLOSE, "SDL_WINDOWEVENT_CLOSE"); #if SDL_VERSION_ATLEAST(2, 0, 5) winEventLog(SDL_WINDOWEVENT_TAKE_FOCUS, "SDL_WINDOWEVENT_TAKE_FOCUS"); winEventLog(SDL_WINDOWEVENT_HIT_TEST, "SDL_WINDOWEVENT_HIT_TEST"); #endif // SDL_VERSION_ATLEAST(2, 0, 5) default: str = strprintf("unknown: %d", event.window.event); break; } logger->log("event: SDL_WINDOWEVENT: %s: %d,%d", str.c_str(), data1, data2); break; } case SDL_TEXTINPUT: { const char *const text = event.text.text; logger->log("event: SDL_TEXTINPUT: %s", text); const size_t sz = strlen(event.text.text); for (size_t f = 0; f < sz; f ++) logger->log("dec: %d", text[f]); break; } case SDL_APP_TERMINATING: logger->log("SDL_APP_TERMINATING"); break; case SDL_APP_LOWMEMORY: logger->log("SDL_APP_LOWMEMORY"); break; case SDL_APP_WILLENTERBACKGROUND: logger->log("SDL_APP_WILLENTERBACKGROUND"); break; case SDL_APP_WILLENTERFOREGROUND: logger->log("SDL_APP_WILLENTERFOREGROUND"); break; case SDL_APP_DIDENTERFOREGROUND: logger->log("SDL_APP_DIDENTERFOREGROUND"); break; case SDL_APP_DIDENTERBACKGROUND: logger->log("SDL_APP_DIDENTERBACKGROUND"); break; case SDL_MOUSEWHEEL: #if SDL_VERSION_ATLEAST(2, 0, 4) logger->log("event: SDL_MOUSEWHEEL: %u,%u, %d,%d, %u", event.wheel.windowID, event.wheel.which, event.wheel.x, event.wheel.y, event.wheel.direction); #else // SDL_VERSION_ATLEAST(2, 0, 4) logger->log("event: SDL_MOUSEWHEEL: %u,%u, %d,%d", event.wheel.windowID, event.wheel.which, event.wheel.x, event.wheel.y); #endif // SDL_VERSION_ATLEAST(2, 0, 4) break; #if SDL_VERSION_ATLEAST(2, 0, 4) case SDL_AUDIODEVICEADDED: logger->log("event: SDL_AUDIODEVICEADDED: %u,%u", event.adevice.which, event.adevice.iscapture); break; case SDL_KEYMAPCHANGED: logger->log("event: SDL_KEYMAPCHANGED"); break; #endif // SDL_VERSION_ATLEAST(2, 0, 4) case SDL_MOUSEBUTTONDOWN: logger->log("event: SDL_MOUSEBUTTONDOWN: %u,%u,%u, %d,%d", event.button.which, CAST_U32(event.button.button), CAST_U32(event.button.state), event.button.x, event.button.y); break; case SDL_MOUSEBUTTONUP: logger->log("event: SDL_MOUSEBUTTONUP: %u,%u,%u, %d,%d", event.button.which, CAST_U32(event.button.button), CAST_U32(event.button.state), event.button.x, event.button.y); break; case SDL_JOYDEVICEADDED: logger->log("event: SDL_JOYDEVICEADDED: %d", event.jdevice.which); break; case SDL_JOYDEVICEREMOVED: logger->log("event: SDL_JOYDEVICEREMOVED: %d", event.jdevice.which); break; #else // USE_SDL2 case SDL_MOUSEMOTION: logger->log("event: SDL_MOUSEMOTION: %u,%d,%d", event.motion.state, event.motion.x, event.motion.y); break; case SDL_KEYDOWN: logger->log("event: SDL_KEYDOWN: %s(%d),%d,%d,%d", SDL_GetKeyName(event.key.keysym.sym), event.key.keysym.sym, event.key.keysym.scancode, event.key.state, event.key.keysym.unicode); break; case SDL_KEYUP: logger->log("event: SDL_KEYUP: %s(%d),%d,%d,%d", SDL_GetKeyName(event.key.keysym.sym), event.key.keysym.sym, event.key.keysym.scancode, event.key.state, event.key.keysym.unicode); break; case SDL_VIDEORESIZE: logger->log("event: SDL_VIDEORESIZE"); break; case SDL_VIDEOEXPOSE: logger->log("event: SDL_VIDEOEXPOSE"); break; case SDL_ACTIVEEVENT: logger->log("event: SDL_ACTIVEEVENT: %d %d", event.active.state, event.active.gain); break; case SDL_MOUSEBUTTONDOWN: logger->log("event: SDL_MOUSEBUTTONDOWN: %d,%d, %d,%d", event.button.button, event.button.state, event.button.x, event.button.y); break; case SDL_MOUSEBUTTONUP: logger->log("event: SDL_MOUSEBUTTONUP: %d,%d,%d,%d", event.button.button, event.button.state, event.button.x, event.button.y); break; #endif // USE_SDL2 case SDL_JOYAXISMOTION: logger->log("event: SDL_JOYAXISMOTION: %d,%d,%d", event.jaxis.which, event.jaxis.axis, event.jaxis.value); break; case SDL_JOYBALLMOTION: logger->log("event: SDL_JOYBALLMOTION: %d,%d,%d,%d", event.jball.which, event.jball.ball, event.jball.xrel, event.jball.yrel); break; case SDL_JOYHATMOTION: logger->log("event: SDL_JOYHATMOTION: %d,%d,%d", event.jhat.which, event.jhat.hat, event.jhat.value); break; case SDL_JOYBUTTONDOWN: logger->log("event: SDL_JOYBUTTONDOWN: %d,%d,%d", event.jbutton.which, event.jbutton.button, event.jbutton.state); break; case SDL_JOYBUTTONUP: logger->log("event: SDL_JOYBUTTONUP: %d,%d,%d", event.jbutton.which, event.jbutton.button, event.jbutton.state); break; case SDL_QUIT: logger->log("event: SDL_QUIT"); break; case SDL_SYSWMEVENT: { #ifdef USE_X11 const bool res = X11Logger::logEvent(event); if (res == false) logger->assertLog("event: SDL_SYSWMEVENT: not supported:"); #else // USE_X11 logger->assertLog("event: SDL_SYSWMEVENT: not supported:"); #endif // USE_X11 break; } case SDL_USEREVENT: logger->log("event: SDL_USEREVENT: %d", event.user.code); break; #ifdef ANDROID #ifndef USE_SDL2 case SDL_ACCELEROMETER: logger->log("event: SDL_ACCELEROMETER"); break; #endif // USE_SDL2 #endif // ANDROID default: logger->assertLog("event: other: %u", CAST_U32(event.type)); break; } } #ifdef USE_SDL2 void EventsManager::handleSDL2WindowEvent(const SDL_Event &event) { #ifndef DYECMD int fpsLimit = 0; #endif // DYECMD const int eventType = event.window.event; const bool inGame = (client->getState() == State::GAME); switch (eventType) { case SDL_WINDOWEVENT_RESIZED: WindowManager::resizeVideo(event.window.data1, event.window.data2, false); break; case SDL_WINDOWEVENT_ENTER: settings.mouseFocused = true; break; case SDL_WINDOWEVENT_LEAVE: settings.mouseFocused = false; break; case SDL_WINDOWEVENT_FOCUS_GAINED: settings.inputFocused = KeyboardFocus::Focused; break; #if SDL_VERSION_ATLEAST(2, 0, 5) case SDL_WINDOWEVENT_TAKE_FOCUS: settings.inputFocused = KeyboardFocus::Focused2; break; #endif // SDL_VERSION_ATLEAST(2, 0, 5) case SDL_WINDOWEVENT_FOCUS_LOST: settings.inputFocused = KeyboardFocus::Unfocused; break; case SDL_WINDOWEVENT_MINIMIZED: WindowManager::setIsMinimized(true); #ifndef DYECMD fpsLimit = config.getIntValue("altfpslimit"); if (localPlayer) localPlayer->setHalfAway(true); #endif // DYECMD setPriority(false); break; case SDL_WINDOWEVENT_RESTORED: case SDL_WINDOWEVENT_MAXIMIZED: WindowManager::setIsMinimized(false); #ifndef DYECMD fpsLimit = config.getIntValue("fpslimit"); if (localPlayer) localPlayer->setHalfAway(false); #endif // DYECMD setPriority(true); break; default: break; } if (!inGame) return; #ifndef DYECMD if (eventType == SDL_WINDOWEVENT_MINIMIZED || eventType == SDL_WINDOWEVENT_RESTORED || eventType == SDL_WINDOWEVENT_MAXIMIZED) { if (localPlayer) { localPlayer->updateStatus(); localPlayer->updateName(); } Game::instance()->updateFrameRate(fpsLimit); } #endif // DYECMD } #else // USE_SDL2 void EventsManager::handleActive(const SDL_Event &event) { #ifndef DYECMD int fpsLimit = 0; const bool inGame = (client->getState() == State::GAME); #endif // DYECMD if ((event.active.state & SDL_APPACTIVE) != 0) { if (event.active.gain != 0U) { // window restore WindowManager::setIsMinimized(false); #ifndef DYECMD if (localPlayer != nullptr) { if (!settings.awayMode) fpsLimit = config.getIntValue("fpslimit"); localPlayer->setHalfAway(false); } #endif // DYECMD setPriority(true); } else { // window minimization #ifdef ANDROID client->setState(State::EXIT); #else // ANDROID WindowManager::setIsMinimized(true); #ifndef DYECMD if (localPlayer != nullptr && !settings.awayMode) { fpsLimit = config.getIntValue("altfpslimit"); localPlayer->setHalfAway(true); } #endif // DYECMD setPriority(false); #endif // ANDROID } #ifndef DYECMD if (inGame && (localPlayer != nullptr)) localPlayer->updateStatus(); #endif // DYECMD } #ifndef DYECMD if (inGame && (localPlayer != nullptr)) localPlayer->updateName(); #endif // DYECMD if ((event.active.state & SDL_APPINPUTFOCUS) != 0) { settings.inputFocused = (event.active.gain != 0U) ? KeyboardFocus::Focused : KeyboardFocus::Unfocused; } if ((event.active.state & SDL_APPMOUSEFOCUS) != 0) settings.mouseFocused = (event.active.gain != 0U); #ifndef DYECMD if (inGame) Game::instance()->updateFrameRate(fpsLimit); #endif // DYECMD } #endif // USE_SDL2