summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorbjørn Lindeijer <bjorn@lindeijer.nl>2025-07-04 17:06:32 +0000
committerThorbjørn Lindeijer <bjorn@lindeijer.nl>2025-07-04 17:06:32 +0000
commit3fb53be63095dad2ebcfd52d44f13d20f630e478 (patch)
tree645f45caa1ca94c53f90713a1bc3d1b97bc59ebf
parent49d4981d5e7902489db2d4be8d4ad388fbd616bd (diff)
downloadmana-3fb53be63095dad2ebcfd52d44f13d20f630e478.tar.gz
mana-3fb53be63095dad2ebcfd52d44f13d20f630e478.tar.bz2
mana-3fb53be63095dad2ebcfd52d44f13d20f630e478.tar.xz
mana-3fb53be63095dad2ebcfd52d44f13d20f630e478.zip
Allow the window to keep updating during move/resize on Windows and macOS
On Windows, the SDL event loop would get blocked during move/resize, which made the game stop updating. Since version 2.30, SDL will send its SDL_WINDOWEVENT_EXPOSED event on an internal timer during window move/resize so the application can keep redrawing. Since no window size changed events can be received either, the size of the window and the renderer is now updated at the start of each frame. On macOS the SDL event loop was similarly blocked during resize. The event watcher is only used on Windows and macOS because on X11 the processing of all the queued up expose events can cause a delayed response to resizing. Besides, the issues fixed by this watcher don't exist on X11 and Wayland.
-rw-r--r--NEWS1
-rw-r--r--src/client.cpp744
-rw-r--r--src/client.h25
-rw-r--r--src/video.cpp10
-rw-r--r--src/video.h4
5 files changed, 407 insertions, 377 deletions
diff --git a/NEWS b/NEWS
index 681eeab9..13bcbd17 100644
--- a/NEWS
+++ b/NEWS
@@ -63,6 +63,7 @@
- Fixed handling of custom port in update URL
- Fixed stutter when new music starts playing
- Fixed keyboard setup to allow assigning keys before starting the game
+- Fixed window move/resize blocking logic and rendering on Windows and macOS
- Updated to tmwAthena protocol changes
- Updated to Manaserv protocol changes (specials, guilds, debug mode, skills, text particles)
- CMake: Use GNUInstallDirs and made PKG_DATADIR / PKG_BINDIR paths modifiable
diff --git a/src/client.cpp b/src/client.cpp
index b86b44b9..03fbc184 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -115,7 +115,7 @@ volatile int frame_count = 0; /**< Counts the frames during one second */
* Updates fps.
* Called every seconds by SDL_AddTimer()
*/
-Uint32 nextSecond(Uint32 interval, void *param)
+static Uint32 nextSecond(Uint32 interval, void *param)
{
fps = frame_count;
frame_count = 0;
@@ -443,8 +443,6 @@ int Client::exec()
while (mState != STATE_EXIT)
{
- Time::beginFrame();
-
// Handle SDL events
SDL_Event event;
while (SDL_PollEvent(&event))
@@ -505,463 +503,473 @@ int Client::exec()
}
}
- // Let the game handle continuous input while it is active
- if (mGame)
- mGame->handleInput();
+ update();
+ }
- if (Net::getGeneralHandler())
- Net::getGeneralHandler()->flushNetwork();
+ Net::unload();
- gui->logic();
- if (mGame)
- mGame->logic();
+ return 0;
+}
- sound.logic();
+void Client::update()
+{
+ Time::beginFrame();
- // Update the screen when application is active, delay otherwise.
- if (isActive())
- {
- frame_count++;
- gui->draw();
- graphics->updateScreen();
- mFpsManager.limitFps(config.fpsLimit);
- }
- else
- {
- mFpsManager.limitFps(10);
- }
+ mVideo.updateWindowSize();
+ checkGraphicsSize();
- // TODO: Add connect timeouts
- if (mState == STATE_CONNECT_GAME &&
- Net::getGameHandler()->isConnected())
- {
- Net::getLoginHandler()->disconnect();
- }
- else if (mState == STATE_CONNECT_SERVER &&
- mOldState == STATE_CONNECT_SERVER &&
- Net::getLoginHandler()->isConnected())
+ // Let the game handle continuous input while it is active
+ if (mGame)
+ mGame->handleInput();
+
+ if (Net::getGeneralHandler())
+ Net::getGeneralHandler()->flushNetwork();
+
+ gui->logic();
+ if (mGame)
+ mGame->logic();
+
+ sound.logic();
+
+ // Update the screen when application is active, delay otherwise.
+ if (isActive())
+ {
+ frame_count++;
+ gui->draw();
+ graphics->updateScreen();
+ mFpsManager.limitFps(config.fpsLimit);
+ }
+ else
+ {
+ mFpsManager.limitFps(10);
+ }
+
+ // TODO: Add connect timeouts
+ if (mState == STATE_CONNECT_GAME &&
+ Net::getGameHandler()->isConnected())
+ {
+ Net::getLoginHandler()->disconnect();
+ }
+ else if (mState == STATE_CONNECT_SERVER &&
+ mOldState == STATE_CONNECT_SERVER &&
+ Net::getLoginHandler()->isConnected())
+ {
+ mState = STATE_LOGIN;
+ }
+ else if (mState == STATE_WORLD_SELECT && mOldState == STATE_UPDATE)
+ {
+ if (Net::getLoginHandler()->getWorlds().size() < 2)
{
mState = STATE_LOGIN;
}
- else if (mState == STATE_WORLD_SELECT && mOldState == STATE_UPDATE)
+ }
+ else if (mOldState == STATE_START ||
+ (mOldState == STATE_GAME && mState != STATE_GAME))
+ {
+ auto *top = static_cast<gcn::Container*>(gui->getTop());
+
+ mDesktop = new Desktop;
+ top->add(mDesktop);
+ mSetupButton = new Button("", "Setup", this);
+ mSetupButton->setButtonPopupText(_("Setup"));
+ mSetupButton->setButtonIcon("button-icon-setup.png");
+ mSetupButton->setPosition(top->getWidth() - mSetupButton->getWidth()
+ - 3, 3);
+ top->add(mSetupButton);
+
+ mDesktop->setSize(graphics->getWidth(), graphics->getHeight());
+ }
+
+ if (mState == STATE_SWITCH_LOGIN && mOldState == STATE_GAME)
+ {
+ Net::getGameHandler()->disconnect();
+ }
+
+ if (mState != mOldState)
+ {
{
- if (Net::getLoginHandler()->getWorlds().size() < 2)
- {
- mState = STATE_LOGIN;
- }
+ Event event(Event::StateChange);
+ event.setInt("oldState", mOldState);
+ event.setInt("newState", mState);
+ event.trigger(Event::ClientChannel);
}
- else if (mOldState == STATE_START ||
- (mOldState == STATE_GAME && mState != STATE_GAME))
+
+ if (mOldState == STATE_GAME)
{
- auto *top = static_cast<gcn::Container*>(gui->getTop());
-
- mDesktop = new Desktop;
- top->add(mDesktop);
- mSetupButton = new Button("", "Setup", this);
- mSetupButton->setButtonPopupText(_("Setup"));
- mSetupButton->setButtonIcon("button-icon-setup.png");
- mSetupButton->setPosition(top->getWidth() - mSetupButton->getWidth()
- - 3, 3);
- top->add(mSetupButton);
-
- mDesktop->setSize(graphics->getWidth(), graphics->getHeight());
+ delete mGame;
+ mGame = nullptr;
}
- if (mState == STATE_SWITCH_LOGIN && mOldState == STATE_GAME)
+ mOldState = mState;
+
+ // Get rid of the dialog of the previous state
+ if (mCurrentDialog)
{
- Net::getGameHandler()->disconnect();
+ delete mCurrentDialog;
+ mCurrentDialog = nullptr;
}
+ // State has changed, while the quitDialog was active, it might
+ // not be correct anymore
+ if (mQuitDialog)
+ mQuitDialog->scheduleDelete();
- if (mState != mOldState)
+ switch (mState)
{
- {
- Event event(Event::StateChange);
- event.setInt("oldState", mOldState);
- event.setInt("newState", mState);
- event.trigger(Event::ClientChannel);
- }
-
- if (mOldState == STATE_GAME)
- {
- delete mGame;
- mGame = nullptr;
- }
+ case STATE_CHOOSE_SERVER:
+ logger->log("State: CHOOSE SERVER");
- mOldState = mState;
+ // Don't allow an alpha opacity
+ // lower than the default value
+ gui->getTheme()->setMinimumOpacity(0.8f);
- // Get rid of the dialog of the previous state
- if (mCurrentDialog)
- {
- delete mCurrentDialog;
- mCurrentDialog = nullptr;
- }
- // State has changed, while the quitDialog was active, it might
- // not be correct anymore
- if (mQuitDialog)
- mQuitDialog->scheduleDelete();
+ mCurrentDialog = new ServerDialog(&mCurrentServer,
+ mConfigDir);
+ break;
- switch (mState)
- {
- case STATE_CHOOSE_SERVER:
- logger->log("State: CHOOSE SERVER");
+ case STATE_CONNECT_SERVER:
+ logger->log("State: CONNECT SERVER");
- // Don't allow an alpha opacity
- // lower than the default value
- gui->getTheme()->setMinimumOpacity(0.8f);
+ Net::connectToServer(mCurrentServer);
- mCurrentDialog = new ServerDialog(&mCurrentServer,
- mConfigDir);
- break;
+ mCurrentDialog = new ConnectionDialog(
+ _("Connecting to server"), STATE_SWITCH_SERVER);
+ break;
- case STATE_CONNECT_SERVER:
- logger->log("State: CONNECT SERVER");
+ case STATE_LOGIN:
+ logger->log("State: LOGIN");
+ // Don't allow an alpha opacity
+ // lower than the default value
+ gui->getTheme()->setMinimumOpacity(0.8f);
- Net::connectToServer(mCurrentServer);
+ if (mOptions.username.empty() || mOptions.password.empty())
+ {
+ mCurrentDialog = new LoginDialog(&loginData);
+ }
+ else
+ {
+ mState = STATE_LOGIN_ATTEMPT;
+ // Clear the password so that when login fails, the
+ // dialog will show up next time.
+ mOptions.password.clear();
+ }
+ break;
- mCurrentDialog = new ConnectionDialog(
- _("Connecting to server"), STATE_SWITCH_SERVER);
- break;
+ case STATE_LOGIN_ATTEMPT:
+ logger->log("State: LOGIN ATTEMPT");
+ accountLogin(&loginData);
+ mCurrentDialog = new ConnectionDialog(
+ _("Logging in"), STATE_SWITCH_SERVER);
+ break;
- case STATE_LOGIN:
- logger->log("State: LOGIN");
- // Don't allow an alpha opacity
- // lower than the default value
- gui->getTheme()->setMinimumOpacity(0.8f);
+ case STATE_WORLD_SELECT:
+ logger->log("State: WORLD SELECT");
+ {
+ Worlds worlds = Net::getLoginHandler()->getWorlds();
- if (mOptions.username.empty() || mOptions.password.empty())
+ if (worlds.empty())
{
- mCurrentDialog = new LoginDialog(&loginData);
+ // Trust that the netcode knows what it's doing
+ mState = STATE_UPDATE;
}
- else
+ else if (worlds.size() == 1 || mOptions.chooseDefault)
{
- mState = STATE_LOGIN_ATTEMPT;
- // Clear the password so that when login fails, the
- // dialog will show up next time.
- mOptions.password.clear();
+ Net::getLoginHandler()->chooseServer(0);
+ mState = STATE_UPDATE;
}
- break;
-
- case STATE_LOGIN_ATTEMPT:
- logger->log("State: LOGIN ATTEMPT");
- accountLogin(&loginData);
- mCurrentDialog = new ConnectionDialog(
- _("Logging in"), STATE_SWITCH_SERVER);
- break;
-
- case STATE_WORLD_SELECT:
- logger->log("State: WORLD SELECT");
+ else
{
- Worlds worlds = Net::getLoginHandler()->getWorlds();
-
- if (worlds.empty())
- {
- // Trust that the netcode knows what it's doing
- mState = STATE_UPDATE;
- }
- else if (worlds.size() == 1 || mOptions.chooseDefault)
- {
- Net::getLoginHandler()->chooseServer(0);
- mState = STATE_UPDATE;
- }
- else
- {
- mCurrentDialog = new WorldSelectDialog(std::move(worlds));
- }
+ mCurrentDialog = new WorldSelectDialog(std::move(worlds));
}
- break;
+ }
+ break;
- case STATE_WORLD_SELECT_ATTEMPT:
- logger->log("State: WORLD SELECT ATTEMPT");
- mCurrentDialog = new ConnectionDialog(
- _("Entering game world"), STATE_WORLD_SELECT);
- break;
+ case STATE_WORLD_SELECT_ATTEMPT:
+ logger->log("State: WORLD SELECT ATTEMPT");
+ mCurrentDialog = new ConnectionDialog(
+ _("Entering game world"), STATE_WORLD_SELECT);
+ break;
- case STATE_UPDATE:
- logger->log("State: UPDATE");
+ case STATE_UPDATE:
+ logger->log("State: UPDATE");
- if (mOptions.skipUpdate)
- {
- mState = STATE_LOAD_DATA;
- }
- else if (initUpdatesDir())
- {
- mCurrentDialog = new UpdaterWindow(mUpdateHost,
- mLocalDataDir + "/" + mUpdatesDir,
- mOptions.dataPath.empty());
- }
- break;
+ if (mOptions.skipUpdate)
+ {
+ mState = STATE_LOAD_DATA;
+ }
+ else if (initUpdatesDir())
+ {
+ mCurrentDialog = new UpdaterWindow(mUpdateHost,
+ mLocalDataDir + "/" + mUpdatesDir,
+ mOptions.dataPath.empty());
+ }
+ break;
- case STATE_LOAD_DATA:
- logger->log("State: LOAD DATA");
+ case STATE_LOAD_DATA:
+ logger->log("State: LOAD DATA");
- // If another data path has been set,
- // we don't load any other files...
- if (mOptions.dataPath.empty())
- {
- // Add customdata directory
- ResourceManager::searchAndAddArchives(
- "customdata/",
- "zip",
- false);
- }
+ // If another data path has been set,
+ // we don't load any other files...
+ if (mOptions.dataPath.empty())
+ {
+ // Add customdata directory
+ ResourceManager::searchAndAddArchives(
+ "customdata/",
+ "zip",
+ false);
+ }
- // TODO remove this as soon as inventoryhandler stops using this event
- Event::trigger(Event::ClientChannel, Event::LoadingDatabases);
+ // TODO remove this as soon as inventoryhandler stops using this event
+ Event::trigger(Event::ClientChannel, Event::LoadingDatabases);
- // Load XML databases
- CharDB::load();
+ // Load XML databases
+ CharDB::load();
- delete itemDb;
+ delete itemDb;
- switch (Net::getNetworkType())
- {
- case ServerType::TmwAthena:
- itemDb = new TmwAthena::TaItemDB;
- break;
- case ServerType::ManaServ:
- itemDb = new ManaServ::ManaServItemDB;
- break;
- default:
- // Nothing
- itemDb = nullptr;
- break;
- }
- assert(itemDb);
+ switch (Net::getNetworkType())
+ {
+ case ServerType::TmwAthena:
+ itemDb = new TmwAthena::TaItemDB;
+ break;
+ case ServerType::ManaServ:
+ itemDb = new ManaServ::ManaServItemDB;
+ break;
+ default:
+ // Nothing
+ itemDb = nullptr;
+ break;
+ }
+ assert(itemDb);
- // load settings.xml
- SettingsManager::load();
+ // load settings.xml
+ SettingsManager::load();
- ActorSprite::load();
+ ActorSprite::load();
- mDesktop->reloadWallpaper();
+ mDesktop->reloadWallpaper();
- mState = STATE_GET_CHARACTERS;
- break;
+ mState = STATE_GET_CHARACTERS;
+ break;
- case STATE_GET_CHARACTERS:
- logger->log("State: GET CHARACTERS");
- Net::getCharHandler()->requestCharacters();
- mCurrentDialog = new ConnectionDialog(
- _("Requesting characters"),
- STATE_SWITCH_SERVER);
- break;
+ case STATE_GET_CHARACTERS:
+ logger->log("State: GET CHARACTERS");
+ Net::getCharHandler()->requestCharacters();
+ mCurrentDialog = new ConnectionDialog(
+ _("Requesting characters"),
+ STATE_SWITCH_SERVER);
+ break;
- case STATE_CHAR_SELECT:
- logger->log("State: CHAR SELECT");
- // Don't allow an alpha opacity
- // lower than the default value
- gui->getTheme()->setMinimumOpacity(0.8f);
+ case STATE_CHAR_SELECT:
+ logger->log("State: CHAR SELECT");
+ // Don't allow an alpha opacity
+ // lower than the default value
+ gui->getTheme()->setMinimumOpacity(0.8f);
- mCurrentDialog = new CharSelectDialog(&loginData);
+ mCurrentDialog = new CharSelectDialog(&loginData);
- if (!((CharSelectDialog*) mCurrentDialog)->selectByName(
- mOptions.character, CharSelectDialog::Choose))
- {
- ((CharSelectDialog*) mCurrentDialog)->selectByName(
- config.lastCharacter,
- mOptions.chooseDefault ?
- CharSelectDialog::Choose :
- CharSelectDialog::Focus);
- }
+ if (!((CharSelectDialog*) mCurrentDialog)->selectByName(
+ mOptions.character, CharSelectDialog::Choose))
+ {
+ ((CharSelectDialog*) mCurrentDialog)->selectByName(
+ config.lastCharacter,
+ mOptions.chooseDefault ?
+ CharSelectDialog::Choose :
+ CharSelectDialog::Focus);
+ }
- // Choosing character on the command line should work only
- // once, clear it so that 'switch character' works.
- mOptions.character.clear();
- mOptions.chooseDefault = false;
+ // Choosing character on the command line should work only
+ // once, clear it so that 'switch character' works.
+ mOptions.character.clear();
+ mOptions.chooseDefault = false;
- break;
+ break;
- case STATE_CONNECT_GAME:
- logger->log("State: CONNECT GAME");
+ case STATE_CONNECT_GAME:
+ logger->log("State: CONNECT GAME");
- Net::getGameHandler()->connect();
- mCurrentDialog = new ConnectionDialog(
- _("Connecting to the game server"),
- Net::getNetworkType() == ServerType::TmwAthena ?
- STATE_CHOOSE_SERVER : STATE_SWITCH_CHARACTER);
- break;
+ Net::getGameHandler()->connect();
+ mCurrentDialog = new ConnectionDialog(
+ _("Connecting to the game server"),
+ Net::getNetworkType() == ServerType::TmwAthena ?
+ STATE_CHOOSE_SERVER : STATE_SWITCH_CHARACTER);
+ break;
- case STATE_CHANGE_MAP:
- logger->log("State: CHANGE_MAP");
+ case STATE_CHANGE_MAP:
+ logger->log("State: CHANGE_MAP");
- Net::getGameHandler()->connect();
- mCurrentDialog = new ConnectionDialog(
- _("Changing game servers"),
- STATE_SWITCH_CHARACTER);
- break;
+ Net::getGameHandler()->connect();
+ mCurrentDialog = new ConnectionDialog(
+ _("Changing game servers"),
+ STATE_SWITCH_CHARACTER);
+ break;
- case STATE_GAME:
- logger->log("Memorizing selected character %s",
- local_player->getName().c_str());
- config.lastCharacter = local_player->getName();
+ case STATE_GAME:
+ logger->log("Memorizing selected character %s",
+ local_player->getName().c_str());
+ config.lastCharacter = local_player->getName();
- // Fade out logon-music here too to give the desired effect
- // of "flowing" into the game.
- sound.fadeOutMusic(1000);
+ // Fade out logon-music here too to give the desired effect
+ // of "flowing" into the game.
+ sound.fadeOutMusic(1000);
- // Allow any alpha opacity
- gui->getTheme()->setMinimumOpacity(0.0f);
+ // Allow any alpha opacity
+ gui->getTheme()->setMinimumOpacity(0.0f);
- delete mSetupButton;
- delete mDesktop;
- mSetupButton = nullptr;
- mDesktop = nullptr;
+ delete mSetupButton;
+ delete mDesktop;
+ mSetupButton = nullptr;
+ mDesktop = nullptr;
- mCurrentDialog = nullptr;
+ mCurrentDialog = nullptr;
- logger->log("State: GAME");
- mGame = new Game;
- break;
+ logger->log("State: GAME");
+ mGame = new Game;
+ break;
- case STATE_LOGIN_ERROR:
- logger->log("State: LOGIN ERROR");
- showErrorDialog(errorMessage, STATE_LOGIN);
- break;
+ case STATE_LOGIN_ERROR:
+ logger->log("State: LOGIN ERROR");
+ showErrorDialog(errorMessage, STATE_LOGIN);
+ break;
- case STATE_ACCOUNTCHANGE_ERROR:
- logger->log("State: ACCOUNT CHANGE ERROR");
- showErrorDialog(errorMessage, STATE_CHAR_SELECT);
- break;
+ case STATE_ACCOUNTCHANGE_ERROR:
+ logger->log("State: ACCOUNT CHANGE ERROR");
+ showErrorDialog(errorMessage, STATE_CHAR_SELECT);
+ break;
- case STATE_REGISTER_PREP:
- logger->log("State: REGISTER_PREP");
- Net::getLoginHandler()->getRegistrationDetails();
- mCurrentDialog = new ConnectionDialog(
- _("Requesting registration details"), STATE_LOGIN);
- break;
+ case STATE_REGISTER_PREP:
+ logger->log("State: REGISTER_PREP");
+ Net::getLoginHandler()->getRegistrationDetails();
+ mCurrentDialog = new ConnectionDialog(
+ _("Requesting registration details"), STATE_LOGIN);
+ break;
- case STATE_REGISTER:
- logger->log("State: REGISTER");
- mCurrentDialog = new RegisterDialog(&loginData);
- break;
+ case STATE_REGISTER:
+ logger->log("State: REGISTER");
+ mCurrentDialog = new RegisterDialog(&loginData);
+ break;
- case STATE_REGISTER_ATTEMPT:
- logger->log("Username is %s", loginData.username.c_str());
- Net::getLoginHandler()->registerAccount(&loginData);
- loginData.password.clear();
- break;
+ case STATE_REGISTER_ATTEMPT:
+ logger->log("Username is %s", loginData.username.c_str());
+ Net::getLoginHandler()->registerAccount(&loginData);
+ loginData.password.clear();
+ break;
- case STATE_CHANGEPASSWORD:
- logger->log("State: CHANGE PASSWORD");
- mCurrentDialog = new ChangePasswordDialog(&loginData);
- break;
+ case STATE_CHANGEPASSWORD:
+ logger->log("State: CHANGE PASSWORD");
+ mCurrentDialog = new ChangePasswordDialog(&loginData);
+ break;
- case STATE_CHANGEPASSWORD_ATTEMPT:
- logger->log("State: CHANGE PASSWORD ATTEMPT");
- Net::getLoginHandler()->changePassword(loginData.username,
- loginData.password,
- loginData.newPassword);
- break;
+ case STATE_CHANGEPASSWORD_ATTEMPT:
+ logger->log("State: CHANGE PASSWORD ATTEMPT");
+ Net::getLoginHandler()->changePassword(loginData.username,
+ loginData.password,
+ loginData.newPassword);
+ break;
- case STATE_CHANGEPASSWORD_SUCCESS:
- logger->log("State: CHANGE PASSWORD SUCCESS");
- showOkDialog(_("Password Change"),
- _("Password changed successfully!"),
- STATE_CHAR_SELECT);
- loginData.password.clear();
- loginData.newPassword.clear();
- break;
+ case STATE_CHANGEPASSWORD_SUCCESS:
+ logger->log("State: CHANGE PASSWORD SUCCESS");
+ showOkDialog(_("Password Change"),
+ _("Password changed successfully!"),
+ STATE_CHAR_SELECT);
+ loginData.password.clear();
+ loginData.newPassword.clear();
+ break;
- case STATE_CHANGEEMAIL:
- logger->log("State: CHANGE EMAIL");
- mCurrentDialog = new ChangeEmailDialog(&loginData);
- break;
+ case STATE_CHANGEEMAIL:
+ logger->log("State: CHANGE EMAIL");
+ mCurrentDialog = new ChangeEmailDialog(&loginData);
+ break;
- case STATE_CHANGEEMAIL_ATTEMPT:
- logger->log("State: CHANGE EMAIL ATTEMPT");
- Net::getLoginHandler()->changeEmail(loginData.email);
- break;
+ case STATE_CHANGEEMAIL_ATTEMPT:
+ logger->log("State: CHANGE EMAIL ATTEMPT");
+ Net::getLoginHandler()->changeEmail(loginData.email);
+ break;
- case STATE_CHANGEEMAIL_SUCCESS:
- logger->log("State: CHANGE EMAIL SUCCESS");
- showOkDialog(_("Email Change"),
- _("Email changed successfully!"),
- STATE_CHAR_SELECT);
- break;
+ case STATE_CHANGEEMAIL_SUCCESS:
+ logger->log("State: CHANGE EMAIL SUCCESS");
+ showOkDialog(_("Email Change"),
+ _("Email changed successfully!"),
+ STATE_CHAR_SELECT);
+ break;
- case STATE_UNREGISTER:
- logger->log("State: UNREGISTER");
- mCurrentDialog = new UnRegisterDialog(&loginData);
- break;
+ case STATE_UNREGISTER:
+ logger->log("State: UNREGISTER");
+ mCurrentDialog = new UnRegisterDialog(&loginData);
+ break;
- case STATE_UNREGISTER_ATTEMPT:
- logger->log("State: UNREGISTER ATTEMPT");
- Net::getLoginHandler()->unregisterAccount(
- loginData.username, loginData.password);
- break;
+ case STATE_UNREGISTER_ATTEMPT:
+ logger->log("State: UNREGISTER ATTEMPT");
+ Net::getLoginHandler()->unregisterAccount(
+ loginData.username, loginData.password);
+ break;
- case STATE_UNREGISTER_SUCCESS:
- logger->log("State: UNREGISTER SUCCESS");
- Net::getLoginHandler()->disconnect();
+ case STATE_UNREGISTER_SUCCESS:
+ logger->log("State: UNREGISTER SUCCESS");
+ Net::getLoginHandler()->disconnect();
- showOkDialog(_("Unregister Successful"),
- _("Farewell, come back any time..."),
- STATE_CHOOSE_SERVER);
- loginData.clear();
- break;
+ showOkDialog(_("Unregister Successful"),
+ _("Farewell, come back any time..."),
+ STATE_CHOOSE_SERVER);
+ loginData.clear();
+ break;
- case STATE_SWITCH_SERVER:
- logger->log("State: SWITCH SERVER");
+ case STATE_SWITCH_SERVER:
+ logger->log("State: SWITCH SERVER");
- Net::getLoginHandler()->disconnect();
- Net::getGameHandler()->disconnect();
+ Net::getLoginHandler()->disconnect();
+ Net::getGameHandler()->disconnect();
- mCurrentServer.hostname.clear();
- mState = STATE_CHOOSE_SERVER;
- break;
+ mCurrentServer.hostname.clear();
+ mState = STATE_CHOOSE_SERVER;
+ break;
- case STATE_SWITCH_LOGIN:
- logger->log("State: SWITCH LOGIN");
+ case STATE_SWITCH_LOGIN:
+ logger->log("State: SWITCH LOGIN");
- Net::getLoginHandler()->disconnect();
+ Net::getLoginHandler()->disconnect();
- mState = STATE_CONNECT_SERVER;
- break;
+ mState = STATE_CONNECT_SERVER;
+ break;
- case STATE_SWITCH_CHARACTER:
- logger->log("State: SWITCH CHARACTER");
+ case STATE_SWITCH_CHARACTER:
+ logger->log("State: SWITCH CHARACTER");
- // Done with game
- Net::getGameHandler()->disconnect();
+ // Done with game
+ Net::getGameHandler()->disconnect();
- mState = STATE_GET_CHARACTERS;
- break;
+ mState = STATE_GET_CHARACTERS;
+ break;
- case STATE_LOGOUT_ATTEMPT:
- logger->log("State: LOGOUT ATTEMPT");
- // TODO
- break;
+ case STATE_LOGOUT_ATTEMPT:
+ logger->log("State: LOGOUT ATTEMPT");
+ // TODO
+ break;
- case STATE_WAIT:
- logger->log("State: WAIT");
- break;
+ case STATE_WAIT:
+ logger->log("State: WAIT");
+ break;
- case STATE_EXIT:
- logger->log("State: EXIT");
- break;
+ case STATE_EXIT:
+ logger->log("State: EXIT");
+ break;
- case STATE_FORCE_QUIT:
- logger->log("State: FORCE QUIT");
- mState = STATE_EXIT;
- break;
+ case STATE_FORCE_QUIT:
+ logger->log("State: FORCE QUIT");
+ mState = STATE_EXIT;
+ break;
- case STATE_ERROR:
- logger->log("State: ERROR");
- logger->log("Error: %s", errorMessage.c_str());
- showErrorDialog(errorMessage, STATE_CHOOSE_SERVER);
- Net::getGameHandler()->disconnect();
- break;
+ case STATE_ERROR:
+ logger->log("State: ERROR");
+ logger->log("Error: %s", errorMessage.c_str());
+ showErrorDialog(errorMessage, STATE_CHOOSE_SERVER);
+ Net::getGameHandler()->disconnect();
+ break;
- default:
- mState = STATE_FORCE_QUIT;
- break;
- }
+ default:
+ mState = STATE_FORCE_QUIT;
+ break;
}
}
-
- Net::unload();
-
- return 0;
}
void Client::showOkDialog(const std::string &title,
@@ -1245,10 +1253,6 @@ void Client::handleWindowSizeChanged(int width, int height)
// Store the new size in the configuration.
config.screenWidth = width;
config.screenHeight = height;
-
- mVideo.windowSizeChanged(width, height);
-
- checkGraphicsSize();
}
void Client::checkGraphicsSize()
diff --git a/src/client.h b/src/client.h
index a3e9c572..b9e16af4 100644
--- a/src/client.h
+++ b/src/client.h
@@ -150,6 +150,8 @@ public:
int exec();
+ void update();
+
/**
* Pops up an OkDialog with the given \a title and \a message, and
* switches to the given \a state when Ok is pressed.
@@ -236,4 +238,27 @@ private:
SDL_TimerID mSecondsCounterId = 0;
FpsManager mFpsManager;
+
+#if defined(_WIN32) || defined(__APPLE__)
+ /**
+ * This class triggers an update on the window expose event, which allows
+ * the application to draw while Windows is in a modal move/resize loop
+ * as well as while resizing on macOS.
+ */
+ class ExposeEventWatcher
+ {
+ public:
+ ExposeEventWatcher() { SDL_AddEventWatch(&watch, nullptr); }
+ ~ExposeEventWatcher() { SDL_DelEventWatch(&watch, nullptr); }
+
+ static int watch(void *, SDL_Event *event)
+ {
+ if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_EXPOSED)
+ Client::instance()->update();
+ return 0;
+ }
+ };
+
+ ExposeEventWatcher mExposeEventWatcher;
+#endif
};
diff --git a/src/video.cpp b/src/video.cpp
index 7bfe9747..71eff1f9 100644
--- a/src/video.cpp
+++ b/src/video.cpp
@@ -243,12 +243,12 @@ bool Video::apply(const VideoSettings &settings)
return true;
}
-void Video::windowSizeChanged(int width, int height)
+void Video::updateWindowSize()
{
- mSettings.width = width;
- mSettings.height = height;
-
- mGraphics->updateSize(width, height, mSettings.scale());
+ SDL_GetWindowSize(mWindow, &mSettings.width, &mSettings.height);
+ mGraphics->updateSize(mSettings.width,
+ mSettings.height,
+ mSettings.scale());
}
bool Video::initDisplayModes()
diff --git a/src/video.h b/src/video.h
index 47aed627..03e0dc4f 100644
--- a/src/video.h
+++ b/src/video.h
@@ -89,9 +89,9 @@ public:
bool apply(const VideoSettings &settings);
/**
- * Handle a change in window size, possibly adjusting the scale.
+ * Handles a change in window size, possibly adjusting the scale.
*/
- void windowSizeChanged(int width, int height);
+ void updateWindowSize();
const DisplayMode &desktopDisplayMode() const
{