From b8094ab6187cfe8802d002693183e3538a869590 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Fri, 26 Feb 2021 16:48:59 -0800 Subject: [PATCH 01/34] Try to handle resize event. Resizing on the title screen works fine except for some flickering. --- libgag/include/SDLGraphicContext.h | 1 + libgag/src/GUIBase.cpp | 13 ++++++------ libgag/src/GraphicContext.cpp | 34 ++++++++++++++++++++++-------- src/GameGUI.cpp | 28 ++++++++++++------------ 4 files changed, 47 insertions(+), 29 deletions(-) diff --git a/libgag/include/SDLGraphicContext.h b/libgag/include/SDLGraphicContext.h index 24ee4f06..0d4c6087 100644 --- a/libgag/include/SDLGraphicContext.h +++ b/libgag/include/SDLGraphicContext.h @@ -319,6 +319,7 @@ namespace GAGCore //! the minimum acceptable resolution int minW, minH; SDL_Window *window = nullptr; + SDL_GLContext context = nullptr; friend class DrawableSurface; //! option flags Uint32 optionFlags; diff --git a/libgag/src/GUIBase.cpp b/libgag/src/GUIBase.cpp index eee50771..3fd53b7a 100644 --- a/libgag/src/GUIBase.cpp +++ b/libgag/src/GUIBase.cpp @@ -470,12 +470,13 @@ namespace GAGGUI { windowEvent=event; wasWindowEvent=true; - } - break; - case SDL_WINDOWEVENT_RESIZED: - { - // FIXME: window resize is broken - // gfx->setRes(event.window.data1, event.window.data2); + if (event.window.event == SDL_WINDOWEVENT_RESIZED) + { + + // FIXME: window resize is broken + gfx->setRes(event.window.data1, event.window.data2); + onAction(NULL, SCREEN_RESIZED, gfx->getW(), gfx->getH()); + } } break; case SDL_WINDOWEVENT_SIZE_CHANGED: diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index 1033c073..3450f6d1 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -2028,8 +2028,8 @@ namespace GAGCore if (flags & FULLSCREEN) sdlFlags |= SDL_WINDOW_FULLSCREEN; // FIXME: window resize is broken - // if (flags & RESIZABLE) - // sdlFlags |= SDL_WINDOW_RESIZABLE; + if (flags & RESIZABLE) + sdlFlags |= SDL_WINDOW_RESIZABLE; #ifdef HAVE_OPENGL if (flags & USEGPU) { @@ -2041,13 +2041,29 @@ namespace GAGCore optionFlags &= ~USEGPU; #endif - // if window exists, delete it + // if window exists, resize it if (window) { - SDL_DestroyWindow(window); - window = nullptr; + SDL_SetWindowSize(window, w, h); + sdlsurface = SDL_GetWindowSurface(window); +#ifdef HAVE_OPENGL + if (flags & USEGPU) + { + // https://gamedev.stackexchange.com/questions/62691/opengl-resize-problem + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, w, 0, h, -1.0, -1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } +#endif + setClipRect(0, 0, w, h); + nextFrame(); + } + else { + // create the new window and the surface + window = SDL_CreateWindow(windowTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, sdlFlags); } - // create the new window and the surface - window = SDL_CreateWindow(windowTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, sdlFlags); sdlsurface = window != nullptr ? SDL_GetWindowSurface(window) : nullptr; // check surface @@ -2061,9 +2077,9 @@ namespace GAGCore { _gc = this; // enable GL context - if (flags & USEGPU) + if (flags & USEGPU && !context) { - SDL_GLContext context = SDL_GL_CreateContext(window); + context = SDL_GL_CreateContext(window); SDL_GL_MakeCurrent(window, context); } // set _glFormat diff --git a/src/GameGUI.cpp b/src/GameGUI.cpp index 55dfd7e5..5ce6f9af 100644 --- a/src/GameGUI.cpp +++ b/src/GameGUI.cpp @@ -1117,6 +1117,20 @@ void GameGUI::processEvent(SDL_Event *event) else if (event->type==SDL_WINDOWEVENT) { handleActivation(event->window.data1, event->window.data2); + if (event->window.event == SDL_WINDOWEVENT_RESIZED || event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + // FIXME: window resize is broken + int newW=event->window.data1; + int newH=event->window.data2; + newW&=(~(0x1F)); + newH&=(~(0x1F)); + if (newW<640) + newW=640; + if (newH<480) + newH=480; + printf("New size : %dx%d\n", newW, newH); + globalContainer->gfx->setRes(newW, newH); + } } else if (event->type==SDL_QUIT) { @@ -1124,20 +1138,6 @@ void GameGUI::processEvent(SDL_Event *event) orderQueue.push_back(shared_ptr(new PlayerQuitsGameOrder(localPlayer))); flushOutgoingAndExit=true; } - else if (event->type==SDL_WINDOWEVENT_RESIZED) - { - // FIXME: window resize is broken - /*int newW=event->window.data1; - int newH=event->window.data2; - newW&=(~(0x1F)); - newH&=(~(0x1F)); - if (newW<640) - newW=640; - if (newH<480) - newH=480; - printf("New size : %dx%d\n", newW, newH); - globalContainer->gfx->setRes(newW, newH);*/ - } } void GameGUI::handleActivation(Uint8 state, Uint8 gain) From b94f466c93d5626396c97027beec9e8f2a8931ff Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Sat, 12 Nov 2022 15:19:53 -0800 Subject: [PATCH 02/34] Write class to listen for events in main thread. --- libgag/include/EventListener.h | 15 +++++++++++++++ libgag/src/EventListener.cpp | 29 +++++++++++++++++++++++++++++ libgag/src/SConscript | 3 ++- src/GlobalContainer.cpp | 6 +++++- 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 libgag/include/EventListener.h create mode 100644 libgag/src/EventListener.cpp diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h new file mode 100644 index 00000000..c14df376 --- /dev/null +++ b/libgag/include/EventListener.h @@ -0,0 +1,15 @@ +#include "GraphicContext.h" +#include +#include +namespace GAGCore { +extern std::deque events; +class EventListener { +public: + EventListener(GraphicContext* gfx); + void run(); + ~EventListener(); +private: + GraphicContext* gfx; + bool quit, done; +}; +} \ No newline at end of file diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp new file mode 100644 index 00000000..2c5db089 --- /dev/null +++ b/libgag/src/EventListener.cpp @@ -0,0 +1,29 @@ +#include "EventListener.h" +namespace GAGCore { +std::deque events = std::deque(); +EventListener::EventListener(GraphicContext* gfx) +{ + this->gfx = gfx; + done = false; + quit = false; +} +EventListener::~EventListener() +{ + quit = true; + while (!done) { + SDL_Delay(100); + } +} +void EventListener::run() +{ + while (!quit) + { + SDL_Event event; + while (SDL_PollEvent(&event)) + { + events.push_back(event); + } + } + done = true; +} +} \ No newline at end of file diff --git a/libgag/src/SConscript b/libgag/src/SConscript index 7e227754..4d6de955 100644 --- a/libgag/src/SConscript +++ b/libgag/src/SConscript @@ -7,7 +7,8 @@ GUITextArea.cpp GUIText.cpp GUITextInput.cpp GUIImage.cpp GUIProgressBar.cpp KeyPress.cpp Sprite.cpp StreamBackend.cpp Stream.cpp StreamFilter.cpp StringTable.cpp SupportFunctions.cpp TextStream.cpp Toolkit.cpp TrueTypeFont.cpp win32_dirent.cpp -GUITabScreen.cpp GUITabScreenWindow.cpp TextSort.cpp GUICheckList.cpp +GUITabScreen.cpp GUITabScreenWindow.cpp TextSort.cpp GUICheckList.cpp +EventListener.cpp """) libgag_just_server = Split(""" diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index 315483ff..e84f8d4d 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -16,11 +16,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - +#include #include #include #include +#include "EventListener.h" #include "FileManager.h" #include "GameGUIKeyActions.h" #include "Glob2Screen.h" @@ -514,6 +515,9 @@ void GlobalContainer::loadClient(void) gfx->setMinRes(640, 480); //gfx->setQuality((settings.optionFlags & OPTION_LOW_SPEED_GFX) != 0 ? GraphicContext::LOW_QUALITY : GraphicContext::HIGH_QUALITY); + EventListener el(gfx); + el.run(); + // load data required for drawing progress screen title = new DrawableSurface("data/gfx/title.png"); terrain = Toolkit::getSprite("data/gfx/terrain"); From aff7fe625fb2cdfbaf540cb238173678b8b0250b Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Sat, 12 Nov 2022 19:03:39 -0800 Subject: [PATCH 03/34] Change globalContainer to handle both event and logic threads. --- libgag/include/EventListener.h | 6 +++- libgag/src/EventListener.cpp | 4 +++ src/Glob2.cpp | 26 +++++++++++++---- src/GlobalContainer.cpp | 53 +++++++++++++++++++--------------- src/GlobalContainer.h | 6 ++-- 5 files changed, 62 insertions(+), 33 deletions(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index c14df376..47a18753 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -1,3 +1,5 @@ +#ifndef __EVENTLISTENER_H +#define __EVENTLISTENER_H #include "GraphicContext.h" #include #include @@ -7,9 +9,11 @@ class EventListener { public: EventListener(GraphicContext* gfx); void run(); + bool isRunning(); ~EventListener(); private: GraphicContext* gfx; bool quit, done; }; -} \ No newline at end of file +} +#endif //__EVENTLISTENER_H \ No newline at end of file diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 2c5db089..4a51e43b 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -26,4 +26,8 @@ void EventListener::run() } done = true; } +bool EventListener::isRunning() +{ + return !quit; +} } \ No newline at end of file diff --git a/src/Glob2.cpp b/src/Glob2.cpp index 3d982214..258bcdc3 100644 --- a/src/Glob2.cpp +++ b/src/Glob2.cpp @@ -20,9 +20,10 @@ #include "Glob2.h" #include "GlobalContainer.h" #include "YOGServer.h" +#include +#include #ifndef YOG_SERVER_ONLY - #include "CampaignEditor.h" #include "CampaignMenuScreen.h" #include "CampaignMainMenu.h" @@ -211,14 +212,27 @@ int Glob2::runTestMapGeneration() } #endif // !YOG_SERVER_ONLY - +std::thread* otherthread = nullptr; +std::thread::id mainthr; +std::atomic mainthrSet = false; int Glob2::run(int argc, char *argv[]) { srand(time(NULL)); - - globalContainer=new GlobalContainer(); - globalContainer->parseArgs(argc, argv); - globalContainer->load(); + if (!globalContainer) { + globalContainer=new GlobalContainer(); + globalContainer->parseArgs(argc, argv); + } + if (!mainthrSet) { + mainthr = std::this_thread::get_id(); + mainthrSet = true; + otherthread = new std::thread(&Glob2::run, this, argc, argv); + } + if (std::this_thread::get_id() == mainthr) { + globalContainer->load(true); + } + else { + globalContainer->load(false); + } if ( SDLNet_Init() < 0 ) { diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index e84f8d4d..5b5a78ae 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -505,8 +505,9 @@ void GlobalContainer::updateLoadProgressScreen(int value) gfx->nextFrame(); } +EventListener* el = NULL; // glob2-client specific actions here. -void GlobalContainer::loadClient(void) +void GlobalContainer::loadClient(bool runEventListener) { if (!runNoX) { @@ -515,8 +516,10 @@ void GlobalContainer::loadClient(void) gfx->setMinRes(640, 480); //gfx->setQuality((settings.optionFlags & OPTION_LOW_SPEED_GFX) != 0 ? GraphicContext::LOW_QUALITY : GraphicContext::HIGH_QUALITY); - EventListener el(gfx); - el.run(); + if (runEventListener) { + el = new EventListener(gfx); + el->run(); + } // load data required for drawing progress screen title = new DrawableSurface("data/gfx/title.png"); @@ -624,31 +627,33 @@ void GlobalContainer::loadClient(void) } #endif // !YOG_SERVER_ONLY -void GlobalContainer::load(void) +void GlobalContainer::load(bool runEventListener) { - // load texts - if (!Toolkit::getStringTable()->load("data/texts.list.txt")) - { - std::cerr << "Fatal error : while loading \"data/texts.list.txt\"" << std::endl; - assert(false); - exit(-1); - } - // load texts - if (!Toolkit::getStringTable()->loadIncompleteList("data/texts.incomplete.txt")) - { - std::cerr << "Fatal error : while loading \"data/texts.incomplete.txt\"" << std::endl; - assert(false); - exit(-1); + if (runEventListener) { + // load texts + if (!Toolkit::getStringTable()->load("data/texts.list.txt")) + { + std::cerr << "Fatal error : while loading \"data/texts.list.txt\"" << std::endl; + assert(false); + exit(-1); + } + // load texts + if (!Toolkit::getStringTable()->loadIncompleteList("data/texts.incomplete.txt")) + { + std::cerr << "Fatal error : while loading \"data/texts.incomplete.txt\"" << std::endl; + assert(false); + exit(-1); + } + + Toolkit::getStringTable()->setLang(Toolkit::getStringTable()->getLangCode(settings.language)); + // load default unit types + Race::loadDefault(); + // load resources types + ressourcesTypes.load("data/ressources.txt"); ///TODO: coding in english or french? english is resources, french is ressources } - - Toolkit::getStringTable()->setLang(Toolkit::getStringTable()->getLangCode(settings.language)); - // load default unit types - Race::loadDefault(); - // load resources types - ressourcesTypes.load("data/ressources.txt"); ///TODO: coding in english or french? english is resources, french is ressources #ifndef YOG_SERVER_ONLY - loadClient(); + loadClient(runEventListener); #endif // !YOG_SERVER_ONLY } diff --git a/src/GlobalContainer.h b/src/GlobalContainer.h index cd0ed9de..c1daac02 100644 --- a/src/GlobalContainer.h +++ b/src/GlobalContainer.h @@ -23,6 +23,7 @@ #include "BuildingsTypes.h" #include "RessourcesTypes.h" #include "Settings.h" +#include "EventListener.h" namespace GAGCore { @@ -41,6 +42,7 @@ class UnitsSkins; class ReplayReader; class ReplayWriter; +extern EventListener* el; class GlobalContainer { public: @@ -59,9 +61,9 @@ class GlobalContainer void parseArgs(int argc, char *argv[]); #ifndef YOG_SERVER_ONLY - void loadClient(void); + void loadClient(bool runEventListener); #endif // !YOG_SERVER_ONLY - void load(void); + void load(bool runEventListener); //void setUsername(const std::string &name); //const std::string &getUsername(void) { return settings.getUsername(); } From 0e786bd53402293d5de6b8af7a2fbce5ae6ba8a4 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Sat, 12 Nov 2022 20:30:20 -0800 Subject: [PATCH 04/34] Replace SDL_PollEvent with EventListener::poll. --- libgag/include/EventListener.h | 3 +++ libgag/src/EventListener.cpp | 15 +++++++++++++++ libgag/src/GUIBase.cpp | 4 +++- libgag/src/GUIMessageBox.cpp | 4 +++- src/EndGameScreen.cpp | 4 +++- src/FertilityCalculatorDialog.cpp | 4 +++- src/GameGUI.cpp | 4 +++- src/GlobalContainer.cpp | 13 ++++++++----- src/MapEdit.cpp | 4 +++- src/ScriptEditorScreen.cpp | 4 +++- src/YOGClientGameConnectionDialog.cpp | 4 +++- 11 files changed, 50 insertions(+), 13 deletions(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index 47a18753..b7f495f1 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -10,9 +10,12 @@ class EventListener { EventListener(GraphicContext* gfx); void run(); bool isRunning(); + int poll(SDL_Event* e); + static EventListener *instance(); ~EventListener(); private: GraphicContext* gfx; + static EventListener* el; bool quit, done; }; } diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 4a51e43b..40f3011d 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -1,9 +1,11 @@ #include "EventListener.h" namespace GAGCore { std::deque events = std::deque(); +EventListener* EventListener::el = nullptr; EventListener::EventListener(GraphicContext* gfx) { this->gfx = gfx; + el = this; done = false; quit = false; } @@ -26,6 +28,19 @@ void EventListener::run() } done = true; } +int EventListener::poll(SDL_Event* e) +{ + if (events.size()) { + *e = events.front(); + events.pop_front(); + return 1; + } + return 0; +} +EventListener *EventListener::instance() +{ + return el; +} bool EventListener::isRunning() { return !quit; diff --git a/libgag/src/GUIBase.cpp b/libgag/src/GUIBase.cpp index 3fd53b7a..8e50b0ba 100644 --- a/libgag/src/GUIBase.cpp +++ b/libgag/src/GUIBase.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -449,7 +450,8 @@ namespace GAGGUI SDL_Event lastMouseMotion, windowEvent, event; bool hadLastMouseMotion=false; bool wasWindowEvent=false; - while (SDL_PollEvent(&event)) + EventListener* el = EventListener::instance(); + while (el->poll(&event)) { switch (event.type) { diff --git a/libgag/src/GUIMessageBox.cpp b/libgag/src/GUIMessageBox.cpp index 611e3769..745e204d 100644 --- a/libgag/src/GUIMessageBox.cpp +++ b/libgag/src/GUIMessageBox.cpp @@ -24,6 +24,7 @@ #include #include #include +#include using namespace GAGCore; @@ -110,7 +111,8 @@ namespace GAGGUI while(mbs->endValue<0) { Sint32 time = SDL_GetTicks(); - while (SDL_PollEvent(&event)) + EventListener* el = EventListener::instance(); + while (el->poll(&event)) { if (event.type==SDL_QUIT) break; diff --git a/src/EndGameScreen.cpp b/src/EndGameScreen.cpp index cbde4b3a..2b1aaff0 100644 --- a/src/EndGameScreen.cpp +++ b/src/EndGameScreen.cpp @@ -35,6 +35,7 @@ #include "GameGUILoadSave.h" #include "StreamBackend.h" #include "ReplayWriter.h" +#include "EventListener.h" EndGameStat::EndGameStat(int x, int y, int w, int h, Uint32 hAlign, Uint32 vAlign, Game *game) { @@ -567,7 +568,8 @@ void EndGameScreen::saveReplay(const char *dir, const char *ext) while(loadSaveScreen->endValue<0) { int time = SDL_GetTicks(); - while (SDL_PollEvent(&event)) + EventListener *el = EventListener::instance(); + while (el->poll(&event)) { loadSaveScreen->translateAndProcessEvent(&event); } diff --git a/src/FertilityCalculatorDialog.cpp b/src/FertilityCalculatorDialog.cpp index 085e4c15..934f705e 100644 --- a/src/FertilityCalculatorDialog.cpp +++ b/src/FertilityCalculatorDialog.cpp @@ -26,6 +26,7 @@ #include #include "StringTable.h" #include "Toolkit.h" +#include "EventListener.h" using namespace GAGCore; using namespace GAGGUI; @@ -66,7 +67,8 @@ void FertilityCalculatorDialog::execute() while(endValue<0) { Sint32 time = SDL_GetTicks(); - while (SDL_PollEvent(&event)) + EventListener *el = EventListener::instance(); + while (el->poll(&event)) { if (event.type==SDL_QUIT) break; diff --git a/src/GameGUI.cpp b/src/GameGUI.cpp index 5ce6f9af..2834ef4f 100644 --- a/src/GameGUI.cpp +++ b/src/GameGUI.cpp @@ -55,6 +55,7 @@ #include "ReplayWriter.h" #include "config.h" #include "Order.h" +#include "EventListener.h" #include @@ -388,7 +389,8 @@ void GameGUI::step(void) bool wasWindowEvent=false; int oldMouseMapX = -1, oldMouseMapY = -1; // hopefully the values here will never matter // we get all pending events but for mousemotion we only keep the last one - while (SDL_PollEvent(&event)) + EventListener *el = EventListener::instance(); + while (el->poll(&event)) { if (event.type==SDL_MOUSEMOTION) { diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index 5b5a78ae..c72938b6 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -511,16 +511,19 @@ void GlobalContainer::loadClient(bool runEventListener) { if (!runNoX) { - // create graphic context - gfx = Toolkit::initGraphic(settings.screenWidth, settings.screenHeight, settings.screenFlags, "Globulation 2", "glob 2"); - gfx->setMinRes(640, 480); - //gfx->setQuality((settings.optionFlags & OPTION_LOW_SPEED_GFX) != 0 ? GraphicContext::LOW_QUALITY : GraphicContext::HIGH_QUALITY); - if (runEventListener) { + // create graphic context + gfx = Toolkit::initGraphic(settings.screenWidth, settings.screenHeight, settings.screenFlags, "Globulation 2", "glob 2"); + gfx->setMinRes(640, 480); + //gfx->setQuality((settings.optionFlags & OPTION_LOW_SPEED_GFX) != 0 ? GraphicContext::LOW_QUALITY : GraphicContext::HIGH_QUALITY); + el = new EventListener(gfx); el->run(); } + while (!el || !el->isRunning()) { + SDL_Delay(100); + } // load data required for drawing progress screen title = new DrawableSurface("data/gfx/title.png"); terrain = Toolkit::getSprite("data/gfx/terrain"); diff --git a/src/MapEdit.cpp b/src/MapEdit.cpp index 172bf1e8..cd71450c 100644 --- a/src/MapEdit.cpp +++ b/src/MapEdit.cpp @@ -37,6 +37,7 @@ #include "Utilities.h" #include "FertilityCalculatorDialog.h" #include "GUIMessageBox.h" +#include "EventListener.h" #define RIGHT_MENU_WIDTH 160 @@ -1236,7 +1237,8 @@ int MapEdit::run(void) // we get all pending events but for mousemotion we only keep the last one SDL_Event event; - while (SDL_PollEvent(&event)) + EventListener *el = EventListener::instance(); + while (el->poll(&event)) { processEvent(event); } diff --git a/src/ScriptEditorScreen.cpp b/src/ScriptEditorScreen.cpp index 002cc9c3..23b9b6f4 100644 --- a/src/ScriptEditorScreen.cpp +++ b/src/ScriptEditorScreen.cpp @@ -28,6 +28,7 @@ #include #include #include +#include using namespace GAGCore; #include #include @@ -569,7 +570,8 @@ void ScriptEditorScreen::loadSave(bool isLoad, const char *dir, const char *ext) while(loadSaveScreen->endValue<0) { int time = SDL_GetTicks(); - while (SDL_PollEvent(&event)) + EventListener *el = EventListener::instance(); + while (el->poll(&event)) { loadSaveScreen->translateAndProcessEvent(&event); } diff --git a/src/YOGClientGameConnectionDialog.cpp b/src/YOGClientGameConnectionDialog.cpp index 8d29c96f..93009ba9 100644 --- a/src/YOGClientGameConnectionDialog.cpp +++ b/src/YOGClientGameConnectionDialog.cpp @@ -24,6 +24,7 @@ #include #include "StringTable.h" #include "Toolkit.h" +#include "EventListener.h" using namespace GAGCore; using namespace GAGGUI; @@ -62,7 +63,8 @@ void YOGClientGameConnectionDialog::execute() while(endValue<0) { Sint32 time = SDL_GetTicks(); - while (SDL_PollEvent(&event)) + EventListener *el = EventListener::instance(); + while (el->poll(&event)) { if (event.type==SDL_QUIT) break; From 1730c9e66f70965b23e4571b5f6c16be9d230db2 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Wed, 16 Nov 2022 17:28:50 -0800 Subject: [PATCH 05/34] Handle resize on title screen. --- libgag/include/SDLGraphicContext.h | 4 ++++ libgag/src/GUIBase.cpp | 6 ++++++ libgag/src/GraphicContext.cpp | 29 +++++++++++++++++++++++++++++ src/GlobalContainer.cpp | 2 ++ 4 files changed, 41 insertions(+) diff --git a/libgag/include/SDLGraphicContext.h b/libgag/include/SDLGraphicContext.h index 0d4c6087..fc98244b 100644 --- a/libgag/include/SDLGraphicContext.h +++ b/libgag/include/SDLGraphicContext.h @@ -318,6 +318,7 @@ namespace GAGCore protected: //! the minimum acceptable resolution int minW, minH; + int prevW, prevH; SDL_Window *window = nullptr; SDL_GLContext context = nullptr; friend class DrawableSurface; @@ -335,6 +336,9 @@ namespace GAGCore // modifiers virtual bool setRes(int w, int h, Uint32 flags); virtual void setRes(int w, int h) { setRes(w, h, optionFlags); } + virtual SDL_Rect getRes(); + virtual bool resChanged(); + virtual void createGLContext(); virtual void setClipRect(int x, int y, int w, int h); virtual void setClipRect(void); virtual void nextFrame(void); diff --git a/libgag/src/GUIBase.cpp b/libgag/src/GUIBase.cpp index 8e50b0ba..1392258e 100644 --- a/libgag/src/GUIBase.cpp +++ b/libgag/src/GUIBase.cpp @@ -516,6 +516,12 @@ namespace GAGGUI break; } } + GraphicContext* gfxCasted = dynamic_cast(gfx); + if (gfxCasted && gfxCasted->resChanged()) { + SDL_Rect r = gfxCasted->getRes(); + gfx->setRes(r.w, r.h); + onAction(NULL, SCREEN_RESIZED, gfx->getW(), gfx->getH()); + } if (hadLastMouseMotion) dispatchEvents(&lastMouseMotion); if (wasWindowEvent) diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index 3450f6d1..db1399dc 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -2006,6 +2006,32 @@ namespace GAGCore fprintf(stderr, "Toolkit : Graphic Context destroyed\n"); } + void GraphicContext::createGLContext() + { + // enable GL context + if (optionFlags & USEGPU) + { + if (context) + SDL_GL_DeleteContext(context); + context = SDL_GL_CreateContext(window); + SDL_GL_MakeCurrent(window, context); + } + } + bool GraphicContext::resChanged() + { + int w, h; + SDL_GetWindowSize(window, &w, &h); + return prevW != w || prevH != h; + } + SDL_Rect GraphicContext::getRes() + { + int w, h; + SDL_Rect r; + SDL_GetWindowSize(window, &w, &h); + r = {0, 0, w, h}; + return r; + } + bool GraphicContext::setRes(int w, int h, Uint32 flags) { // check dimension @@ -2022,6 +2048,9 @@ namespace GAGCore h = minH; } + prevW = w; + prevH = h; + // set flags optionFlags = flags; Uint32 sdlFlags = 0; diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index c72938b6..365a0044 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -519,11 +519,13 @@ void GlobalContainer::loadClient(bool runEventListener) el = new EventListener(gfx); el->run(); + exit(0); } while (!el || !el->isRunning()) { SDL_Delay(100); } + gfx->createGLContext(); // load data required for drawing progress screen title = new DrawableSurface("data/gfx/title.png"); terrain = Toolkit::getSprite("data/gfx/terrain"); From 9d4e8fe806830bf600c8b77afa90465ea87a793b Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Tue, 22 Nov 2022 16:15:24 -0800 Subject: [PATCH 06/34] Change EventListener booleans to be atomic. This fixes a bug where one of the booleans was 238. --- libgag/include/EventListener.h | 4 +++- libgag/src/EventListener.cpp | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index b7f495f1..d6d00fd9 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -3,12 +3,14 @@ #include "GraphicContext.h" #include #include +#include namespace GAGCore { extern std::deque events; class EventListener { public: EventListener(GraphicContext* gfx); void run(); + void stop(); bool isRunning(); int poll(SDL_Event* e); static EventListener *instance(); @@ -16,7 +18,7 @@ class EventListener { private: GraphicContext* gfx; static EventListener* el; - bool quit, done; + std::atomic quit, done; }; } #endif //__EVENTLISTENER_H \ No newline at end of file diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 40f3011d..72c30774 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -9,13 +9,16 @@ EventListener::EventListener(GraphicContext* gfx) done = false; quit = false; } -EventListener::~EventListener() +void EventListener::stop() { quit = true; while (!done) { SDL_Delay(100); } } +EventListener::~EventListener() +{ +} void EventListener::run() { while (!quit) From 7ceb58e0925d608a0f22a88e2cccf7fff2349378 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Tue, 22 Nov 2022 17:48:09 -0800 Subject: [PATCH 07/34] Fix graphics glitches in OpenGL mode. On the title screen, the background was white instead of the green texture background and the buttons had white rectangles where the text should be. --- libgag/include/SDLGraphicContext.h | 2 ++ libgag/src/GraphicContext.cpp | 47 +++++++++++++++++++++--------- libgag/src/Toolkit.cpp | 8 +++++ src/GlobalContainer.cpp | 1 + 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/libgag/include/SDLGraphicContext.h b/libgag/include/SDLGraphicContext.h index fc98244b..1d2e7bf7 100644 --- a/libgag/include/SDLGraphicContext.h +++ b/libgag/include/SDLGraphicContext.h @@ -339,6 +339,8 @@ namespace GAGCore virtual SDL_Rect getRes(); virtual bool resChanged(); virtual void createGLContext(); + virtual void unsetContext(); + void resetMatrices(); virtual void setClipRect(int x, int y, int w, int h); virtual void setClipRect(void); virtual void nextFrame(void); diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index db1399dc..e3189fe3 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #ifdef HAVE_CONFIG_H #include @@ -2006,17 +2007,28 @@ namespace GAGCore fprintf(stderr, "Toolkit : Graphic Context destroyed\n"); } + std::mutex m; + void GraphicContext::createGLContext() { // enable GL context if (optionFlags & USEGPU) { - if (context) - SDL_GL_DeleteContext(context); - context = SDL_GL_CreateContext(window); + std::lock_guard l(m); + if (!context) + context = SDL_GL_CreateContext(window); + if (!context) + throw "no context"; SDL_GL_MakeCurrent(window, context); } } + void GraphicContext::unsetContext() + { + if (optionFlags & USEGPU) + { + SDL_GL_MakeCurrent(window, nullptr); + } + } bool GraphicContext::resChanged() { int w, h; @@ -2031,6 +2043,17 @@ namespace GAGCore r = {0, 0, w, h}; return r; } + void GraphicContext::resetMatrices() + { + // https://gamedev.stackexchange.com/questions/62691/opengl-resize-problem + int w = getW(), h = getH(); + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, w, 0, h, -1.0, -1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } bool GraphicContext::setRes(int w, int h, Uint32 flags) { @@ -2077,13 +2100,7 @@ namespace GAGCore #ifdef HAVE_OPENGL if (flags & USEGPU) { - // https://gamedev.stackexchange.com/questions/62691/opengl-resize-problem - glViewport(0, 0, w, h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, w, 0, h, -1.0, -1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + resetMatrices(); } #endif setClipRect(0, 0, w, h); @@ -2094,7 +2111,12 @@ namespace GAGCore window = SDL_CreateWindow(windowTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, sdlFlags); } sdlsurface = window != nullptr ? SDL_GetWindowSurface(window) : nullptr; - +#ifdef HAVE_OPENGL + if (flags & USEGPU) + { + resetMatrices(); + } +#endif // check surface if (!sdlsurface) { @@ -2108,8 +2130,7 @@ namespace GAGCore // enable GL context if (flags & USEGPU && !context) { - context = SDL_GL_CreateContext(window); - SDL_GL_MakeCurrent(window, context); + createGLContext(); } // set _glFormat if ((optionFlags & USEGPU) && (_gc->sdlsurface->format->BitsPerPixel != 32)) diff --git a/libgag/src/Toolkit.cpp b/libgag/src/Toolkit.cpp index 6de1c2c7..fd42977d 100644 --- a/libgag/src/Toolkit.cpp +++ b/libgag/src/Toolkit.cpp @@ -23,6 +23,7 @@ #include #include #include "TrueTypeFont.h" +#include "EventListener.h" #ifndef YOG_SERVER_ONLY #include @@ -77,6 +78,13 @@ namespace GAGCore } #ifndef YOG_SERVER_ONLY + EventListener *el = EventListener::instance(); + if (el) + { + el->stop(); + delete el; + el = NULL; + } if (gc) { delete gc; diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index 365a0044..efc30d10 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -517,6 +517,7 @@ void GlobalContainer::loadClient(bool runEventListener) gfx->setMinRes(640, 480); //gfx->setQuality((settings.optionFlags & OPTION_LOW_SPEED_GFX) != 0 ? GraphicContext::LOW_QUALITY : GraphicContext::HIGH_QUALITY); + gfx->unsetContext(); el = new EventListener(gfx); el->run(); exit(0); From 6c4c7ad9bf0d75359ff52b1ce19e3dd1c058ecfe Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Tue, 22 Nov 2022 20:21:24 -0800 Subject: [PATCH 08/34] Handle resizing during a game. --- libgag/include/SDLGraphicContext.h | 1 + libgag/src/GUIBase.cpp | 6 +++--- libgag/src/GraphicContext.cpp | 6 +++++- src/GameGUI.cpp | 6 ++++++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/libgag/include/SDLGraphicContext.h b/libgag/include/SDLGraphicContext.h index 1d2e7bf7..04fa7da5 100644 --- a/libgag/include/SDLGraphicContext.h +++ b/libgag/include/SDLGraphicContext.h @@ -340,6 +340,7 @@ namespace GAGCore virtual bool resChanged(); virtual void createGLContext(); virtual void unsetContext(); + static GraphicContext* instance(); void resetMatrices(); virtual void setClipRect(int x, int y, int w, int h); virtual void setClipRect(void); diff --git a/libgag/src/GUIBase.cpp b/libgag/src/GUIBase.cpp index 1392258e..3e8fa0ff 100644 --- a/libgag/src/GUIBase.cpp +++ b/libgag/src/GUIBase.cpp @@ -516,9 +516,9 @@ namespace GAGGUI break; } } - GraphicContext* gfxCasted = dynamic_cast(gfx); - if (gfxCasted && gfxCasted->resChanged()) { - SDL_Rect r = gfxCasted->getRes(); + GraphicContext* gfx = GraphicContext::instance(); + if (gfx->resChanged()) { + SDL_Rect r = gfx->getRes(); gfx->setRes(r.w, r.h); onAction(NULL, SCREEN_RESIZED, gfx->getW(), gfx->getH()); } diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index e3189fe3..fdd9d316 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -2007,8 +2007,12 @@ namespace GAGCore fprintf(stderr, "Toolkit : Graphic Context destroyed\n"); } - std::mutex m; + GraphicContext* GraphicContext::instance() + { + return _gc; + } + std::mutex m; void GraphicContext::createGLContext() { // enable GL context diff --git a/src/GameGUI.cpp b/src/GameGUI.cpp index 2834ef4f..1f755f4d 100644 --- a/src/GameGUI.cpp +++ b/src/GameGUI.cpp @@ -56,6 +56,7 @@ #include "config.h" #include "Order.h" #include "EventListener.h" +#include "SDLGraphicContext.h" #include @@ -477,6 +478,11 @@ void GameGUI::step(void) processEvent(&event); } } + GraphicContext* gfx = GraphicContext::instance(); + if (gfx->resChanged()) { + SDL_Rect r = gfx->getRes(); + gfx->setRes(r.w, r.h); + } if (wasMouseMotion) processEvent(&mouseMotionEvent); if (wasWindowEvent) From 5dacb46bd35003955cd1c78b362a99e34d7cea85 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Tue, 22 Nov 2022 20:21:54 -0800 Subject: [PATCH 09/34] Fix position of minimap when resizing game window. --- src/Minimap.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Minimap.cpp b/src/Minimap.cpp index e6416fe7..c379efee 100644 --- a/src/Minimap.cpp +++ b/src/Minimap.cpp @@ -71,6 +71,9 @@ void Minimap::draw(int localteam, int viewportX, int viewportY, int viewportW, i { if (noX) return; + // Keep the minimap in the right place even if window is resized. + gameWidth = globalContainer->gfx->getW(); + // Compute the position of the minimap if it needs to be scaled & centered computeMinimapPositioning(); From 6837dcf35d6cc9132dae2a9d86739462b24dc0d0 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Tue, 22 Nov 2022 22:36:35 -0800 Subject: [PATCH 10/34] Keep map editor widgets in the right panel. --- src/MapEdit.cpp | 127 +++++++++++++++++++++++++++--------------------- src/MapEdit.h | 11 +++-- 2 files changed, 79 insertions(+), 59 deletions(-) diff --git a/src/MapEdit.cpp b/src/MapEdit.cpp index cd71450c..63cfaddc 100644 --- a/src/MapEdit.cpp +++ b/src/MapEdit.cpp @@ -38,6 +38,7 @@ #include "FertilityCalculatorDialog.h" #include "GUIMessageBox.h" #include "EventListener.h" +#include "SDLGraphicContext.h" #define RIGHT_MENU_WIDTH 160 @@ -59,6 +60,13 @@ void MapEditorWidget::drawSelf() } +// If the window gets wider, we need to move the widgets further to the right. +// Likewize, if the window becomes smaller, the widgets should move to the left. +int MapEditorWidget::adjustX() +{ + return globalContainer->gfx->getW()-area.initialWindowWidth; +} + void MapEditorWidget::disable() { @@ -100,7 +108,7 @@ void BuildingSelectorWidget::draw() int imgid = bt->miniSpriteImage; int x, y; - x=area.x; + x=area.x+adjustX(); y=area.y; Sprite *buildingSprite; @@ -141,7 +149,7 @@ void TeamColorSelector::draw() { for(int n=0; n<16; ++n) { - const int xpos = area.x + (n%6)*16; + const int xpos = area.x + adjustX() + (n%6)*16; const int ypos = area.y + (n/6)*16; if(me.game.teams[n]) { @@ -166,7 +174,7 @@ SingleLevelSelector::SingleLevelSelector(MapEdit& me, const widgetRectangle& are void SingleLevelSelector::draw() { - globalContainer->gfx->drawSprite(area.x, area.y, me.menu, 30+level-1, (level-1)==levelNum ? 128 : 255); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, me.menu, 30+level-1, (level-1)==levelNum ? 128 : 255); } @@ -183,9 +191,9 @@ void PanelIcon::draw() { // draw buttons if (me.panelMode==panelModeHilight) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, iconNumber+1); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, iconNumber+1); else - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, iconNumber); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, iconNumber); } @@ -203,9 +211,9 @@ void MenuIcon::draw() { // draw buttons if (me.showingMenuScreen) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 7); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 7); else - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 6); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 6); } @@ -224,25 +232,25 @@ void ZoneSelector::draw() bool isSelected=false; if(zoneType==ForbiddenZone) { - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 13); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 13); if(me.brushType==MapEdit::ForbiddenBrush) isSelected=true; } else if(zoneType==GuardingZone) { - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 14); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 14); if(me.brushType==MapEdit::GuardAreaBrush) isSelected=true; } else if(zoneType==ClearingZone) { - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 25); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 25); if(me.brushType==MapEdit::ClearAreaBrush) isSelected=true; } if(me.selectionMode==MapEdit::PlaceZone && isSelected) { - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 22); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 22); } } @@ -258,7 +266,7 @@ BrushSelector::BrushSelector(MapEdit& me, const widgetRectangle& area, const std void BrushSelector::draw() { - brushTool.draw(area.x, area.y); + brushTool.draw(area.x+adjustX(), area.y); } @@ -281,23 +289,23 @@ void UnitSelector::draw() { if(me.selectionMode==MapEdit::PlaceUnit && me.placingUnit==MapEdit::Worker) drawSelection=true; - globalContainer->gfx->drawSprite(area.x, area.y, unitSprite, 64); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, unitSprite, 64); } else if(unitType==EXPLORER) { if(me.selectionMode==MapEdit::PlaceUnit && me.placingUnit==MapEdit::Explorer) drawSelection=true; - globalContainer->gfx->drawSprite(area.x, area.y, unitSprite, 0); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, unitSprite, 0); } else if(unitType==WARRIOR) { if(me.selectionMode==MapEdit::PlaceUnit && me.placingUnit==MapEdit::Warrior) drawSelection=true; - globalContainer->gfx->drawSprite(area.x, area.y, unitSprite, 256); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, unitSprite, 256); } if(drawSelection) { - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 23); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 23); } } @@ -314,29 +322,29 @@ TerrainSelector::TerrainSelector(MapEdit& me, const widgetRectangle& area, const void TerrainSelector::draw() { if(terrainType==Grass) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->terrain, 0); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->terrain, 0); if(terrainType==Sand) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->terrain, 128); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->terrain, 128); if(terrainType==Water) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->terrain, 259); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->terrain, 259); if(terrainType==Wheat) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->ressources, 19); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->ressources, 19); if(terrainType==Trees) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->ressources, 2); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->ressources, 2); if(terrainType==Stone) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->ressources, 34); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->ressources, 34); if(terrainType==Algae) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->ressources, 44); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->ressources, 44); if(terrainType==Papyrus) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->ressources, 24); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->ressources, 24); if(terrainType==CherryTree) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->ressources, 54); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->ressources, 54); if(terrainType==OrangeTree) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->ressources, 59); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->ressources, 59); if(terrainType==PruneTree) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->ressources, 64); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->ressources, 64); if(me.terrainType==terrainType) - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 22); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 22); } @@ -351,15 +359,15 @@ BlueButton::BlueButton(MapEdit& me, const widgetRectangle& area, const std::stri void BlueButton::draw() { - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 12); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 12); if(selected) - globalContainer->gfx->drawFilledRect(area.x+9, area.y+3, 94, 10, 128, 128, 192); + globalContainer->gfx->drawFilledRect(area.x+9+adjustX(), area.y+3, 94, 10, 128, 128, 192); std::string translatedText; translatedText=Toolkit::getStringTable()->getString(text.c_str()); int len=globalContainer->littleFont->getStringWidth(translatedText.c_str()); int h=globalContainer->littleFont->getStringHeight(translatedText.c_str()); - globalContainer->gfx->drawString(area.x+9+((94-len)/2), area.y+((16-h)/2), globalContainer->littleFont, translatedText); + globalContainer->gfx->drawString(area.x+adjustX()+9+((94-len)/2), area.y+((16-h)/2), globalContainer->littleFont, translatedText); } @@ -387,10 +395,10 @@ PlusIcon::PlusIcon(MapEdit& me, const widgetRectangle& area, const std::string& void PlusIcon::draw() { - globalContainer->gfx->drawFilledRect(area.x, area.y, 32, 32, Color(75,0,200)); - globalContainer->gfx->drawRect(area.x, area.y, 32, 32, Color::white); - globalContainer->gfx->drawFilledRect(area.x + 15, area.y + 6, 2, 20, Color::white); - globalContainer->gfx->drawFilledRect(area.x + 6, area.y + 15, 20, 2, Color::white); + globalContainer->gfx->drawFilledRect(area.x+adjustX(), area.y, 32, 32, Color(75,0,200)); + globalContainer->gfx->drawRect(area.x+adjustX(), area.y, 32, 32, Color::white); + globalContainer->gfx->drawFilledRect(area.x+adjustX() + 15, area.y + 6, 2, 20, Color::white); + globalContainer->gfx->drawFilledRect(area.x+adjustX() + 6, area.y + 15, 20, 2, Color::white); } @@ -405,9 +413,9 @@ MinusIcon::MinusIcon(MapEdit& me, const widgetRectangle& area, const std::string void MinusIcon::draw() { - globalContainer->gfx->drawFilledRect(area.x, area.y, 32, 32, Color(75,0,200)); - globalContainer->gfx->drawRect(area.x, area.y, 32, 32, Color::white); - globalContainer->gfx->drawFilledRect(area.x + 6, area.y + 15, 20, 2, Color::white); + globalContainer->gfx->drawFilledRect(area.x+adjustX(), area.y, 32, 32, Color(75,0,200)); + globalContainer->gfx->drawRect(area.x+adjustX(), area.y, 32, 32, Color::white); + globalContainer->gfx->drawFilledRect(area.x+adjustX() + 6, area.y + 15, 20, 2, Color::white); } @@ -422,7 +430,7 @@ UnitInfoTitle::UnitInfoTitle(MapEdit& me, const widgetRectangle& area, const std void UnitInfoTitle::draw() { - const int xpos=area.x; + const int xpos=area.x+adjustX(); const int ypos=area.y; Unit* u=unit; @@ -468,7 +476,7 @@ UnitPicture::UnitPicture(MapEdit& me, const widgetRectangle& area, const std::st void UnitPicture::draw() { - const int xpos=area.x; + const int xpos=area.x+adjustX(); const int ypos=area.y; // draw unit's image @@ -537,7 +545,7 @@ FractionValueText::~FractionValueText() void FractionValueText::draw() { - globalContainer->gfx->drawString(area.x, area.y, globalContainer->littleFont, FormatableString("%0: %1/%2").arg(Toolkit::getStringTable()->getString(label.c_str())).arg(*numerator).arg(*denominator).c_str()); + globalContainer->gfx->drawString(area.x+adjustX(), area.y, globalContainer->littleFont, FormatableString("%0: %1/%2").arg(Toolkit::getStringTable()->getString(label.c_str())).arg(*numerator).arg(*denominator).c_str()); } @@ -586,11 +594,11 @@ void ValueScrollBox::draw() //Sometimes a scrollbox gets initiated with max-value 0. A turret construction site has 0/0 stone and 0/0 shots. To not run into arithmetic exceptions those cases are treated here. if((*max) != 0) { - globalContainer->gfx->setClipRect(area.x, area.y, 112, 16); - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 9); + globalContainer->gfx->setClipRect(area.x+adjustX(), area.y, 112, 16); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 9); int size=((*value)*92)/(*max); - globalContainer->gfx->setClipRect(area.x+10, area.y, size, 16); - globalContainer->gfx->drawSprite(area.x+10, area.y+3, globalContainer->gamegui, 10); + globalContainer->gfx->setClipRect(area.x+10+adjustX(), area.y, size, 16); + globalContainer->gfx->drawSprite(area.x+10+adjustX(), area.y+3, globalContainer->gamegui, 10); globalContainer->gfx->setClipRect(); } } @@ -658,7 +666,7 @@ void BuildingInfoTitle::draw() globalContainer->littleFont->pushStyle(Font::Style(Font::STYLE_NORMAL, r, g, b)); int titleLen = globalContainer->littleFont->getStringWidth(title.c_str()); - int titlePos = area.x+((area.width-titleLen)/2); + int titlePos = area.x+adjustX()+((area.width-titleLen)/2); globalContainer->gfx->drawString(titlePos, area.y, globalContainer->littleFont, title.c_str()); globalContainer->littleFont->popStyle(); } @@ -701,8 +709,8 @@ void BuildingPicture::draw() int dx = (56-miniSprite->getW(imgid))/2; int dy = (46-miniSprite->getH(imgid))/2; miniSprite->setBaseColor(selBuild->owner->color); - globalContainer->gfx->drawSprite(area.x+dx, area.y+dy, miniSprite, imgid); - globalContainer->gfx->drawSprite(area.x, area.y, globalContainer->gamegui, 18); + globalContainer->gfx->drawSprite(area.x+dx+adjustX(), area.y+dy, miniSprite, imgid); + globalContainer->gfx->drawSprite(area.x+adjustX(), area.y, globalContainer->gamegui, 18); } @@ -730,9 +738,9 @@ void TextLabel::draw() int titleWidth = globalContainer->littleFont->getStringWidth(label.c_str()); int titleHeight = globalContainer->littleFont->getStringHeight(label.c_str()); if(centered) - globalContainer->gfx->drawString(area.x+(area.width-titleWidth)/2, area.y+(area.height-titleHeight)/2, globalContainer->littleFont, label.c_str()); + globalContainer->gfx->drawString(area.x+adjustX()+(area.width-titleWidth)/2, area.y+(area.height-titleHeight)/2, globalContainer->littleFont, label.c_str()); else - globalContainer->gfx->drawString(area.x, area.y, globalContainer->littleFont, label.c_str()); + globalContainer->gfx->drawString(area.x+adjustX(), area.y, globalContainer->littleFont, label.c_str()); } @@ -756,7 +764,7 @@ void NumberCycler::draw() { std::stringstream s; s<gfx->drawString(area.x, area.y, globalContainer->standardFont, s.str().c_str()); + globalContainer->gfx->drawString(area.x+adjustX(), area.y, globalContainer->standardFont, s.str().c_str()); } @@ -789,17 +797,17 @@ Checkbox::Checkbox(MapEdit& me, const widgetRectangle& area, const std::string& void Checkbox::draw() { - globalContainer->gfx->drawRect(area.x, area.y, 16, 16, Color::white); + globalContainer->gfx->drawRect(area.x+adjustX(), area.y, 16, 16, Color::white); if(isActivated) { - globalContainer->gfx->drawLine(area.x+4, area.y+4, area.x+12, area.y+12, Color::white); - globalContainer->gfx->drawLine(area.x+12, area.y+4, area.x+4, area.y+12, Color::white); + globalContainer->gfx->drawLine(area.x+4+adjustX(), area.y+4, area.x+adjustX()+12, area.y+12, Color::white); + globalContainer->gfx->drawLine(area.x+12+adjustX(), area.y+4, area.x+adjustX()+4, area.y+12, Color::white); } std::string translatedText; translatedText=Toolkit::getStringTable()->getString(text.c_str()); - globalContainer->gfx->drawString(area.x+20, area.y, globalContainer->littleFont, translatedText); + globalContainer->gfx->drawString(area.x+20+adjustX(), area.y, globalContainer->littleFont, translatedText); } @@ -1243,6 +1251,12 @@ int MapEdit::run(void) processEvent(event); } + GraphicContext *gfx = GraphicContext::instance(); + if (gfx->resChanged()) { + SDL_Rect r = gfx->getRes(); + gfx->setRes(r.w, r.h); + } + // While processing events the user could've tried to load a map that failed. // Then we can't go through drawing everything because that would segfault. if(doQuitAfterLoadSave && !showingSave) @@ -3197,7 +3211,8 @@ bool MapEdit::findAction(int x, int y) MapEditorWidget* mi=*i; if(mi->is_in(x, y) && mi->enabled) { - mi->handleClick(mouseX-mi->area.x, mouseY-mi->area.y); + int adjustX = globalContainer->gfx->getW() - mi->area.initialWindowWidth; + mi->handleClick(mouseX-(mi->area.x+adjustX), mouseY-mi->area.y); return true; } } diff --git a/src/MapEdit.h b/src/MapEdit.h index de444303..1896797a 100644 --- a/src/MapEdit.h +++ b/src/MapEdit.h @@ -38,14 +38,18 @@ ///A generic rectangle structure used for a variety of purposes, but mainly for the convience of the widget system struct widgetRectangle { - widgetRectangle(int x, int y, int width, int height) : x(x), y(y), width(width), height(height) {} - widgetRectangle() : x(0), y(0), width(0), height(0) {} - bool is_in(int posx, int posy) { return posx>x && posx<(x+width) && posy>y && posy<(y+height); } + widgetRectangle(int x, int y, int width, int height) : x(x), y(y), width(width), height(height), initialWindowWidth(globalContainer->gfx->getW()) {} + widgetRectangle() : x(0), y(0), width(0), height(0), initialWindowWidth(0) {} + bool is_in(int posx, int posy) { return posx>(x+globalContainer->gfx->getW()-initialWindowWidth) && + posx<(x+globalContainer->gfx->getW()-initialWindowWidth+width) && + posy>y && + posy<(y+height); } int x; int y; int width; int height; + int initialWindowWidth; }; class MapEdit; @@ -78,6 +82,7 @@ class MapEditorWidget ///This function must be implemented by all derived classes. This is where the widget draws itself. It should use area.x ///and area.y to get the cordinates. virtual void draw()=0; + int adjustX(); friend class MapEdit; protected: MapEdit& me; From 0ca173d13ec7d8588fec79aaa8ff34439ff91ec7 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Wed, 23 Nov 2022 16:35:48 -0800 Subject: [PATCH 11/34] Join thread, fix server, and move globals into GlobalContainer. --- src/Glob2.cpp | 16 ++++++++-------- src/GlobalContainer.cpp | 6 ++++++ src/GlobalContainer.h | 8 ++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Glob2.cpp b/src/Glob2.cpp index 258bcdc3..f708881f 100644 --- a/src/Glob2.cpp +++ b/src/Glob2.cpp @@ -212,9 +212,6 @@ int Glob2::runTestMapGeneration() } #endif // !YOG_SERVER_ONLY -std::thread* otherthread = nullptr; -std::thread::id mainthr; -std::atomic mainthrSet = false; int Glob2::run(int argc, char *argv[]) { srand(time(NULL)); @@ -222,12 +219,15 @@ int Glob2::run(int argc, char *argv[]) globalContainer=new GlobalContainer(); globalContainer->parseArgs(argc, argv); } - if (!mainthrSet) { - mainthr = std::this_thread::get_id(); - mainthrSet = true; - otherthread = new std::thread(&Glob2::run, this, argc, argv); + if (!globalContainer->mainthrSet) { + globalContainer->mainthr = std::this_thread::get_id(); + globalContainer->mainthrSet = true; + if (!globalContainer->hostServer) { + globalContainer->otherthread = new std::thread(&Glob2::run, this, argc, argv); + } } - if (std::this_thread::get_id() == mainthr) { + if (!globalContainer->hostServer && + std::this_thread::get_id() == globalContainer->mainthr) { globalContainer->load(true); } else { diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index efc30d10..97627e2a 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -26,6 +26,7 @@ #include "GameGUIKeyActions.h" #include "Glob2Screen.h" #include "Glob2Style.h" +#include "Glob2.h" #include "GlobalContainer.h" #include "Header.h" #include "IntBuildingType.h" @@ -128,6 +129,9 @@ GlobalContainer::GlobalContainer(void) replayShowAreas = false; replayShowFlags = true; + mainthrSet = false; + otherthread = nullptr; + #ifndef YOG_SERVER_ONLY replayReader = NULL; replayWriter = NULL; @@ -520,6 +524,8 @@ void GlobalContainer::loadClient(bool runEventListener) gfx->unsetContext(); el = new EventListener(gfx); el->run(); + otherthread->join(); + delete otherthread; exit(0); } diff --git a/src/GlobalContainer.h b/src/GlobalContainer.h index c1daac02..64c67eef 100644 --- a/src/GlobalContainer.h +++ b/src/GlobalContainer.h @@ -24,6 +24,8 @@ #include "RessourcesTypes.h" #include "Settings.h" #include "EventListener.h" +#include +#include namespace GAGCore { @@ -140,6 +142,12 @@ class GlobalContainer bool replayShowAreas; //!< Show areas of gui.localPlayer or not. Can be edited real-time. bool replayShowFlags; //!< Show all flags or show none. Can be edited real-time. + // other thread handles events in the queue, game logic, rendering, etc. + std::thread* otherthread; + // main thread listens for SDL events and adds them to a queue + std::thread::id mainthr; + std::atomic mainthrSet; + #ifndef YOG_SERVER_ONLY ReplayReader *replayReader; //!< Reads and processes replay files, and outputs orders ReplayWriter *replayWriter; //!< Writes orders into replay files From 4cb6f6cd8f55c08aeb109373d9fc6806696e75c6 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Wed, 23 Nov 2022 17:58:14 -0800 Subject: [PATCH 12/34] Use condition variables rather than busy looping. --- libgag/include/EventListener.h | 6 ++++++ libgag/src/EventListener.cpp | 23 ++++++++++++++++++++--- src/GlobalContainer.cpp | 7 +++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index d6d00fd9..fc3bcced 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace GAGCore { extern std::deque events; class EventListener { @@ -15,6 +17,10 @@ class EventListener { int poll(SDL_Event* e); static EventListener *instance(); ~EventListener(); + static std::mutex startMutex; + static std::condition_variable startedCond; + static std::mutex doneMutex; + static std::condition_variable doneCond; private: GraphicContext* gfx; static EventListener* el; diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 72c30774..c6db8ce3 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -2,18 +2,23 @@ namespace GAGCore { std::deque events = std::deque(); EventListener* EventListener::el = nullptr; +std::mutex EventListener::startMutex; +std::condition_variable EventListener::startedCond; +std::mutex EventListener::doneMutex; +std::condition_variable EventListener::doneCond; EventListener::EventListener(GraphicContext* gfx) { this->gfx = gfx; el = this; done = false; - quit = false; + quit = true; } void EventListener::stop() { quit = true; + std::unique_lock lock(doneMutex); while (!done) { - SDL_Delay(100); + doneCond.wait(lock); } } EventListener::~EventListener() @@ -21,15 +26,27 @@ EventListener::~EventListener() } void EventListener::run() { + { + std::unique_lock lock(startMutex); + quit = false; + startedCond.notify_one(); + } while (!quit) { SDL_Event event; while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + quit = true; + } events.push_back(event); } } - done = true; + { + std::unique_lock lock(doneMutex); + done = true; + doneCond.notify_one(); + } } int EventListener::poll(SDL_Event* e) { diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index 97627e2a..cd1aec46 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -529,8 +529,11 @@ void GlobalContainer::loadClient(bool runEventListener) exit(0); } - while (!el || !el->isRunning()) { - SDL_Delay(100); + { + std::unique_lock lock(EventListener::startMutex); + while (!el || !el->isRunning()) { + EventListener::startedCond.wait(lock); + } } gfx->createGLContext(); // load data required for drawing progress screen From dbba9bc54c67bb75349c73d84b8e471b06719bbe Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Fri, 25 Nov 2022 15:36:06 -0800 Subject: [PATCH 13/34] Fix deadlock when exiting the game. Also fix game when running without a window (nox mode). --- src/Glob2.cpp | 28 ++++++++++++++++++++++++---- src/Glob2.h | 1 + src/GlobalContainer.cpp | 2 +- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Glob2.cpp b/src/Glob2.cpp index f708881f..15c69ea7 100644 --- a/src/Glob2.cpp +++ b/src/Glob2.cpp @@ -212,6 +212,12 @@ int Glob2::runTestMapGeneration() } #endif // !YOG_SERVER_ONLY +void Glob2::finish() +{ + // This is for the textshot code + GAGCore::DrawableSurface::printFinishingText(); + delete globalContainer; +} int Glob2::run(int argc, char *argv[]) { srand(time(NULL)); @@ -222,13 +228,16 @@ int Glob2::run(int argc, char *argv[]) if (!globalContainer->mainthrSet) { globalContainer->mainthr = std::this_thread::get_id(); globalContainer->mainthrSet = true; - if (!globalContainer->hostServer) { + if (!globalContainer->hostServer && !globalContainer->runNoX) { globalContainer->otherthread = new std::thread(&Glob2::run, this, argc, argv); } } if (!globalContainer->hostServer && + !globalContainer->runNoX && std::this_thread::get_id() == globalContainer->mainthr) { globalContainer->load(true); + finish(); + return 0; } else { globalContainer->load(false); @@ -421,9 +430,20 @@ int Glob2::run(int argc, char *argv[]) } } - // This is for the textshot code - GAGCore::DrawableSurface::printFinishingText(); - delete globalContainer; + // quit event loop so otherthread can be joined by main thread + if (globalContainer->otherthread) + { + EventListener *el = EventListener::instance(); + el->stop(); + } + + // Deleting globalContainer indirectly deletes GraphicContext whose + // destructor calls SDL_Quit(). I think SDL_Quit needs to be called from + // same thread that called SDL_Init. + if (std::this_thread::get_id() == globalContainer->mainthr) + { + finish(); + } #endif // !YOG_SERVER_ONLY diff --git a/src/Glob2.h b/src/Glob2.h index 44feec14..8d7c1760 100644 --- a/src/Glob2.h +++ b/src/Glob2.h @@ -37,6 +37,7 @@ class Glob2 ///Generates random maps non stop until the game crashes int runTestMapGeneration(); int run(int argc, char *argv[]); + void finish(); }; #endif diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index cd1aec46..dab5ee46 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -526,7 +526,7 @@ void GlobalContainer::loadClient(bool runEventListener) el->run(); otherthread->join(); delete otherthread; - exit(0); + return; } { From bcb6fd369627928017cdcc8ed765c7f7b3aad4dd Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Fri, 25 Nov 2022 17:58:31 -0800 Subject: [PATCH 14/34] Add copyright header. --- libgag/include/EventListener.h | 18 ++++++++++++++++++ libgag/src/EventListener.cpp | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index fc3bcced..6452ea8e 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -1,3 +1,21 @@ +/* + Copyright (C) 2022 Nathan Mills + for any question or comment contact us at + + 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 3 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ #ifndef __EVENTLISTENER_H #define __EVENTLISTENER_H #include "GraphicContext.h" diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index c6db8ce3..5b3d6ada 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -1,3 +1,21 @@ +/* + Copyright (C) 2022 Nathan Mills + for any question or comment contact us at + + 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 3 of the License, or + (at your option) 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ #include "EventListener.h" namespace GAGCore { std::deque events = std::deque(); From abd01a0e084b9e5a17f257089c45dc365d9199f6 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Sat, 26 Nov 2022 20:05:11 -0800 Subject: [PATCH 15/34] Disallow resizing to smaller than minimum size. --- libgag/src/GraphicContext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index fdd9d316..078f7f26 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -1920,6 +1920,7 @@ namespace GAGCore { minW = w; minH = h; + SDL_SetWindowMinimumSize(window, minW, minH); } VideoModes GraphicContext::listVideoModes() const From c0e4a4d4071ef6d2145bb49ab84798f598f0f0d3 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Fri, 23 Dec 2022 17:49:05 -0800 Subject: [PATCH 16/34] Fix missing template argument with GCC 7.5.0. --- libgag/src/EventListener.cpp | 6 +++--- src/GlobalContainer.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 5b3d6ada..8ced5ceb 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -34,7 +34,7 @@ EventListener::EventListener(GraphicContext* gfx) void EventListener::stop() { quit = true; - std::unique_lock lock(doneMutex); + std::unique_lock lock(doneMutex); while (!done) { doneCond.wait(lock); } @@ -45,7 +45,7 @@ EventListener::~EventListener() void EventListener::run() { { - std::unique_lock lock(startMutex); + std::unique_lock lock(startMutex); quit = false; startedCond.notify_one(); } @@ -61,7 +61,7 @@ void EventListener::run() } } { - std::unique_lock lock(doneMutex); + std::unique_lock lock(doneMutex); done = true; doneCond.notify_one(); } diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index dab5ee46..597df8fb 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -530,7 +530,7 @@ void GlobalContainer::loadClient(bool runEventListener) } { - std::unique_lock lock(EventListener::startMutex); + std::unique_lock lock(EventListener::startMutex); while (!el || !el->isRunning()) { EventListener::startedCond.wait(lock); } From 69c7619e63212a6a1f3aef72cf00489c64352c85 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Sat, 24 Dec 2022 17:01:27 -0800 Subject: [PATCH 17/34] Fix black window upon resizing. I don't know why, but removing the glOrtho call makes the window stop going black when resizing. --- libgag/include/SDLGraphicContext.h | 1 + libgag/src/GraphicContext.cpp | 41 ++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/libgag/include/SDLGraphicContext.h b/libgag/include/SDLGraphicContext.h index 04fa7da5..e07e1388 100644 --- a/libgag/include/SDLGraphicContext.h +++ b/libgag/include/SDLGraphicContext.h @@ -336,6 +336,7 @@ namespace GAGCore // modifiers virtual bool setRes(int w, int h, Uint32 flags); virtual void setRes(int w, int h) { setRes(w, h, optionFlags); } + virtual SDL_Surface *getOrCreateSurface(int w, int h, Uint32 flags); virtual SDL_Rect getRes(); virtual bool resChanged(); virtual void createGLContext(); diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index 078f7f26..a6db8b74 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -2055,11 +2055,25 @@ namespace GAGCore glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho(0, w, 0, h, -1.0, -1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } + SDL_Surface* GraphicContext::getOrCreateSurface(int w, int h, Uint32 flags) { + if (flags & USEGPU) + { + if (sdlsurface) + SDL_FreeSurface(sdlsurface); + // Can't use SDL_GetWindowSurface with OpenGL; the documentation forbids it. + sdlsurface = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); + } + else + { + sdlsurface = SDL_GetWindowSurface(window); + } + return sdlsurface; + } + bool GraphicContext::setRes(int w, int h, Uint32 flags) { // check dimension @@ -2101,7 +2115,7 @@ namespace GAGCore // if window exists, resize it if (window) { SDL_SetWindowSize(window, w, h); - sdlsurface = SDL_GetWindowSurface(window); + getOrCreateSurface(w, h, flags); #ifdef HAVE_OPENGL if (flags & USEGPU) { @@ -2109,19 +2123,23 @@ namespace GAGCore } #endif setClipRect(0, 0, w, h); - nextFrame(); + //nextFrame(); } else { // create the new window and the surface window = SDL_CreateWindow(windowTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, sdlFlags); - } - sdlsurface = window != nullptr ? SDL_GetWindowSurface(window) : nullptr; + sdlsurface = window != nullptr ? getOrCreateSurface(w, h, flags) : nullptr; #ifdef HAVE_OPENGL - if (flags & USEGPU) - { - resetMatrices(); - } + // enable GL context + if (flags & USEGPU) + { + if (!context) + createGLContext(); + resetMatrices(); + } #endif + } + // check surface if (!sdlsurface) { @@ -2132,11 +2150,6 @@ namespace GAGCore else { _gc = this; - // enable GL context - if (flags & USEGPU && !context) - { - createGLContext(); - } // set _glFormat if ((optionFlags & USEGPU) && (_gc->sdlsurface->format->BitsPerPixel != 32)) { From 05be3cf7353ff3b6988c69f73552c23247357d2f Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Tue, 27 Dec 2022 21:14:43 -0800 Subject: [PATCH 18/34] Fix white screen in software renderered mode. --- src/GlobalContainer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index 597df8fb..5ab2ec4d 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -536,6 +536,8 @@ void GlobalContainer::loadClient(bool runEventListener) } } gfx->createGLContext(); + // Next line fixes white screen during loading screen in software rendered mode. + gfx->getOrCreateSurface(gfx->getW(), gfx->getH(), gfx->getOptionFlags()); // load data required for drawing progress screen title = new DrawableSurface("data/gfx/title.png"); terrain = Toolkit::getSprite("data/gfx/terrain"); From 34885f073ccef4f50038a9f3ba875ef8854ac03a Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Tue, 27 Dec 2022 21:20:17 -0800 Subject: [PATCH 19/34] Disable resize during loading screen. --- libgag/src/GraphicContext.cpp | 10 +++++++++- src/GlobalContainer.cpp | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index a6db8b74..38b63b45 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -2076,6 +2076,7 @@ namespace GAGCore bool GraphicContext::setRes(int w, int h, Uint32 flags) { + static bool isLoading = true; // check dimension if (minW && (w < minW)) { @@ -2099,8 +2100,14 @@ namespace GAGCore if (flags & FULLSCREEN) sdlFlags |= SDL_WINDOW_FULLSCREEN; // FIXME: window resize is broken - if (flags & RESIZABLE) + if (flags & RESIZABLE && !isLoading) + { sdlFlags |= SDL_WINDOW_RESIZABLE; + } + else + { + isLoading = false; + } #ifdef HAVE_OPENGL if (flags & USEGPU) { @@ -2115,6 +2122,7 @@ namespace GAGCore // if window exists, resize it if (window) { SDL_SetWindowSize(window, w, h); + SDL_SetWindowResizable(window, SDL_TRUE); getOrCreateSurface(w, h, flags); #ifdef HAVE_OPENGL if (flags & USEGPU) diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index 5ab2ec4d..2c05332d 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -640,6 +640,7 @@ void GlobalContainer::loadClient(bool runEventListener) Style::style = new Glob2Style; updateLoadProgressScreen(100); + gfx->setRes(gfx->getW(), gfx->getH()); } } #endif // !YOG_SERVER_ONLY From 8d8d3ac425592219596672bc8fdcc6877b93271a Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Wed, 8 Mar 2023 12:05:30 -0800 Subject: [PATCH 20/34] Hook resize event on Windows. --- libgag/include/EventListener.h | 4 ++ libgag/src/EventListener.cpp | 72 ++++++++++++++++++++++++++++++++++ src/GameGUI.cpp | 2 + 3 files changed, 78 insertions(+) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index 6452ea8e..0ca466d5 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace GAGCore { extern std::deque events; class EventListener { @@ -39,7 +40,10 @@ class EventListener { static std::condition_variable startedCond; static std::mutex doneMutex; static std::condition_variable doneCond; + void setPainter(std::function f); + void paint(); private: + std::function painter; GraphicContext* gfx; static EventListener* el; std::atomic quit, done; diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 8ced5ceb..3facdc95 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -17,6 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "EventListener.h" +#include namespace GAGCore { std::deque events = std::deque(); EventListener* EventListener::el = nullptr; @@ -24,7 +25,14 @@ std::mutex EventListener::startMutex; std::condition_variable EventListener::startedCond; std::mutex EventListener::doneMutex; std::condition_variable EventListener::doneCond; + +#define SIZE_MOVE_TIMER_ID 1 +#if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) +#define WINDOWS_OR_MINGW 1 +#endif + EventListener::EventListener(GraphicContext* gfx) +: painter(nullptr) { this->gfx = gfx; el = this; @@ -42,6 +50,45 @@ void EventListener::stop() EventListener::~EventListener() { } +void EventListener::setPainter(std::function f) +{ + painter = f; +} +void EventListener::paint() +{ + if (painter) + { + painter(); + gfx->nextFrame(); + } +} +//https://stackoverflow.com/a/51597338/8890345 +#ifdef WINDOWS_OR_MINGW +bool sizeMoveTimerRunning = false; +int eventWatch(void* self, SDL_Event* event) { + if (event->type == SDL_SYSWMEVENT) + { + const auto &winMessage = event->syswm.msg->msg.win; + if (winMessage.msg == WM_ENTERSIZEMOVE) + { + // the user started dragging, so create the timer (with the minimum timeout) + // if you have vsync enabled, then this shouldn't render unnecessarily + sizeMoveTimerRunning = SetTimer(GetActiveWindow(), SIZE_MOVE_TIMER_ID, USER_TIMER_MINIMUM, nullptr); + } + else if (winMessage.msg == WM_TIMER) + { + if (winMessage.wParam == SIZE_MOVE_TIMER_ID) + { + // call your render function + EventListener *el = reinterpret_cast(self); + if (el) + el->paint(); + } + } + } + return 0; +} +#endif void EventListener::run() { { @@ -49,6 +96,10 @@ void EventListener::run() quit = false; startedCond.notify_one(); } +#ifdef WINDOWS_OR_MINGW + SDL_AddEventWatch(eventWatch, this); // register the event watch function + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); // we need the native Windows events, so we can listen to WM_ENTERSIZEMOVE and WM_TIMER +#endif while (!quit) { SDL_Event event; @@ -57,6 +108,27 @@ void EventListener::run() if (event.type == SDL_QUIT) { quit = true; } + if (event.type == SDL_WINDOWEVENT && + (event.window.event == SDL_WINDOWEVENT_RESIZED || + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)) + { + /*glClearColor (0.0f, 0.0f, 0.0f, 1.0f ); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + SDL_GL_SwapWindow(_gc->window);*/ + /*if (painter) + { + painter(); + gfx->nextFrame(); + }*/ + } +#ifdef WINDOWS_OR_MINGW + if (sizeMoveTimerRunning) + { + // modal drag/size loop ended, so kill the timer + KillTimer(GetActiveWindow(), SIZE_MOVE_TIMER_ID); + sizeMoveTimerRunning = false; + } +#endif events.push_back(event); } } diff --git a/src/GameGUI.cpp b/src/GameGUI.cpp index 1f755f4d..2fffdf95 100644 --- a/src/GameGUI.cpp +++ b/src/GameGUI.cpp @@ -191,6 +191,7 @@ GameGUI::GameGUI() GameGUI::~GameGUI() { + EventListener::instance()->setPainter(nullptr); for (ParticleSet::iterator it = particles.begin(); it != particles.end(); ++it) delete *it; } @@ -4384,6 +4385,7 @@ void GameGUI::drawInGameScrollableText(void) void GameGUI::drawAll(int team) { + EventListener::instance()->setPainter(std::bind(&GameGUI::drawAll, this, team)); // draw the map Uint32 drawOptions = (drawHealthFoodBar ? Game::DRAW_HEALTH_FOOD_BAR : 0) | (drawPathLines ? Game::DRAW_PATH_LINE : 0) | From 3c95f3bf5301676d05cb31ca9922fd313d57ebf2 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Fri, 24 Mar 2023 09:22:34 -0700 Subject: [PATCH 21/34] Use a mutex when painting or changing gfx surface. --- libgag/include/EventListener.h | 1 + libgag/src/EventListener.cpp | 4 ++++ libgag/src/GraphicContext.cpp | 2 ++ src/Engine.cpp | 8 ++++++++ src/GameGUI.cpp | 1 - 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index 0ca466d5..aacdd095 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -40,6 +40,7 @@ class EventListener { static std::condition_variable startedCond; static std::mutex doneMutex; static std::condition_variable doneCond; + static std::mutex renderMutex; void setPainter(std::function f); void paint(); private: diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 3facdc95..e77d823f 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -25,6 +25,7 @@ std::mutex EventListener::startMutex; std::condition_variable EventListener::startedCond; std::mutex EventListener::doneMutex; std::condition_variable EventListener::doneCond; +std::mutex EventListener::renderMutex; #define SIZE_MOVE_TIMER_ID 1 #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) @@ -52,12 +53,15 @@ EventListener::~EventListener() } void EventListener::setPainter(std::function f) { + std::unique_lock lock(renderMutex); painter = f; } void EventListener::paint() { if (painter) { + std::unique_lock lock(renderMutex); + gfx->createGLContext(); painter(); gfx->nextFrame(); } diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index 38b63b45..379fe826 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -21,6 +21,7 @@ #include #include #include +#include "EventListener.h" #include #include #include @@ -2060,6 +2061,7 @@ namespace GAGCore } SDL_Surface* GraphicContext::getOrCreateSurface(int w, int h, Uint32 flags) { + std::unique_lock lock(EventListener::instance()->renderMutex); if (flags & USEGPU) { if (sdlsurface) diff --git a/src/Engine.cpp b/src/Engine.cpp index 6b83259b..48aec3c5 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -507,6 +507,14 @@ int Engine::run(void) if (nextGuiStep == 0) { // we draw + static bool isPainterSet = false; + if (!isPainterSet) + { + EventListener::instance()->setPainter(std::bind(GameGUI::drawAll, &gui, gui.localTeamNo)); + isPainterSet = true; + } + std::unique_lock lock(EventListener::instance()->renderMutex); + globalContainer->gfx->createGLContext(); gui.drawAll(gui.localTeamNo); globalContainer->gfx->nextFrame(); } diff --git a/src/GameGUI.cpp b/src/GameGUI.cpp index 2fffdf95..87763e85 100644 --- a/src/GameGUI.cpp +++ b/src/GameGUI.cpp @@ -4385,7 +4385,6 @@ void GameGUI::drawInGameScrollableText(void) void GameGUI::drawAll(int team) { - EventListener::instance()->setPainter(std::bind(&GameGUI::drawAll, this, team)); // draw the map Uint32 drawOptions = (drawHealthFoodBar ? Game::DRAW_HEALTH_FOOD_BAR : 0) | (drawPathLines ? Game::DRAW_PATH_LINE : 0) | From c8073e763926a13979aed6c8180543d39b2ee270 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Sun, 16 Apr 2023 20:43:20 -0700 Subject: [PATCH 22/34] Move draw code in MapEdit::run into MapEdit::draw. --- src/MapEdit.cpp | 90 +++++++++++++++++++++++++------------------------ src/MapEdit.h | 3 ++ 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/MapEdit.cpp b/src/MapEdit.cpp index 63cfaddc..29e21b1d 100644 --- a/src/MapEdit.cpp +++ b/src/MapEdit.cpp @@ -1207,6 +1207,50 @@ bool MapEdit::save(const std::string filename, const std::string name) } } +void MapEdit::draw(void) +{ + drawMap(0, 0, globalContainer->gfx->getW() - 0, globalContainer->gfx->getH(), true, true); + + drawMenu(); + drawMiniMap(); + wasMinimapRendered = false; + drawWidgets(); + if (showingMenuScreen) + { + globalContainer->gfx->setClipRect(); + menuScreen->dispatchTimer(startTick); + menuScreen->dispatchPaint(); + globalContainer->gfx->drawSurface((int)menuScreen->decX, (int)menuScreen->decY, menuScreen->getSurface()); + } + if (showingLoad || showingSave) + { + globalContainer->gfx->setClipRect(); + loadSaveScreen->dispatchTimer(startTick); + loadSaveScreen->dispatchPaint(); + globalContainer->gfx->drawSurface((int)loadSaveScreen->decX, (int)loadSaveScreen->decY, loadSaveScreen->getSurface()); + } + if (showingScriptEditor) + { + globalContainer->gfx->setClipRect(); + scriptEditor->dispatchTimer(startTick); + scriptEditor->dispatchPaint(); + globalContainer->gfx->drawSurface((int)scriptEditor->decX, (int)scriptEditor->decY, scriptEditor->getSurface()); + } + if (showingTeamsEditor) + { + globalContainer->gfx->setClipRect(); + teamsEditor->dispatchTimer(startTick); + teamsEditor->dispatchPaint(); + globalContainer->gfx->drawSurface((int)teamsEditor->decX, (int)teamsEditor->decY, teamsEditor->getSurface()); + } + if (isShowingAreaName) + { + globalContainer->gfx->setClipRect(); + areaName->dispatchTimer(startTick); + areaName->dispatchPaint(); + globalContainer->gfx->drawSurface((int)areaName->decX, (int)areaName->decY, areaName->getSurface()); + } +} int MapEdit::run(int sizeX, int sizeY, TerrainType terrainType) @@ -1237,7 +1281,6 @@ int MapEdit::run(void) bool isRunning=true; int returnCode=0; - Uint32 startTick, endTick, deltaTick; while (isRunning) { //SDL_Event event; @@ -1289,49 +1332,8 @@ int MapEdit::run(void) performAction("no ressource growth area drag motion"); } - drawMap(0, 0, globalContainer->gfx->getW()-0, globalContainer->gfx->getH(), true, true); - - drawMenu(); - drawMiniMap(); - wasMinimapRendered=false; - drawWidgets(); - if(showingMenuScreen) - { - globalContainer->gfx->setClipRect(); - menuScreen->dispatchTimer(startTick); - menuScreen->dispatchPaint(); - globalContainer->gfx->drawSurface((int)menuScreen->decX, (int)menuScreen->decY, menuScreen->getSurface()); - } - if(showingLoad || showingSave) - { - globalContainer->gfx->setClipRect(); - loadSaveScreen->dispatchTimer(startTick); - loadSaveScreen->dispatchPaint(); - globalContainer->gfx->drawSurface((int)loadSaveScreen->decX, (int)loadSaveScreen->decY, loadSaveScreen->getSurface()); - } - if(showingScriptEditor) - { - globalContainer->gfx->setClipRect(); - scriptEditor->dispatchTimer(startTick); - scriptEditor->dispatchPaint(); - globalContainer->gfx->drawSurface((int)scriptEditor->decX, (int)scriptEditor->decY, scriptEditor->getSurface()); - } - if(showingTeamsEditor) - { - globalContainer->gfx->setClipRect(); - teamsEditor->dispatchTimer(startTick); - teamsEditor->dispatchPaint(); - globalContainer->gfx->drawSurface((int)teamsEditor->decX, (int)teamsEditor->decY, teamsEditor->getSurface()); - } - if(isShowingAreaName) - { - globalContainer->gfx->setClipRect(); - areaName->dispatchTimer(startTick); - areaName->dispatchPaint(); - globalContainer->gfx->drawSurface((int)areaName->decX, (int)areaName->decY, areaName->getSurface()); - } - - + draw(); + globalContainer->gfx->nextFrame(); diff --git a/src/MapEdit.h b/src/MapEdit.h index 1896797a..1b9c892b 100644 --- a/src/MapEdit.h +++ b/src/MapEdit.h @@ -401,6 +401,8 @@ class MapEdit ///Saves the game to a particular file name bool save(const std::string filename, const std::string name); + void draw(void); + ///Updates the editor after map generation void update(); @@ -778,6 +780,7 @@ class MapEdit ///Handles a click or drag of the no ressource growth area placement tool void handleNoRessourceGrowthClick(int mx, int my); + Uint32 startTick, endTick, deltaTick; }; From 5b530e5a3153fe801f1e1c27ab80bd4da3565de4 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Wed, 19 Apr 2023 18:43:40 -0700 Subject: [PATCH 23/34] WIP: Implement painting MapEdit and Screen objects. --- glob2.vcxproj | 4 +++- libgag/include/EventListener.h | 4 ++++ libgag/src/EventListener.cpp | 30 ++++++++++++++++++++++++++++-- libgag/src/GUIBase.cpp | 2 ++ src/Engine.cpp | 2 +- src/GameGUI.cpp | 2 +- src/MapEdit.cpp | 2 ++ 7 files changed, 41 insertions(+), 5 deletions(-) diff --git a/glob2.vcxproj b/glob2.vcxproj index 44286e05..f8911fa3 100644 --- a/glob2.vcxproj +++ b/glob2.vcxproj @@ -262,6 +262,7 @@ + ZLIB_WINAPI;%(PreprocessorDefinitions) ZLIB_WINAPI;%(PreprocessorDefinitions) @@ -507,6 +508,7 @@ + @@ -622,4 +624,4 @@ - + \ No newline at end of file diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index aacdd095..e4aa0f49 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace GAGCore { extern std::deque events; class EventListener { @@ -42,9 +43,12 @@ class EventListener { static std::condition_variable doneCond; static std::mutex renderMutex; void setPainter(std::function f); + void addPainter(const std::string& name, std::function f); + void removePainter(const std::string& name); void paint(); private: std::function painter; + std::multimap > painters; GraphicContext* gfx; static EventListener* el; std::atomic quit, done; diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index e77d823f..d60d04f0 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -18,6 +18,7 @@ */ #include "EventListener.h" #include +#include namespace GAGCore { std::deque events = std::deque(); EventListener* EventListener::el = nullptr; @@ -56,13 +57,38 @@ void EventListener::setPainter(std::function f) std::unique_lock lock(renderMutex); painter = f; } + +void EventListener::addPainter(const std::string& name, std::function f) +{ + std::unique_lock lock(renderMutex); + painters.insert(std::pair >(name, f)); +} +void EventListener::removePainter(const std::string& name) +{ + if (painters.empty()) + assert("Tried to remove a painter when painters map is empty."); + std::unique_lock lock(renderMutex); + for (auto it = painters.rbegin(); it != painters.rend();++it) + { + if (it->first == name) + { + // There might be multiple Screens active, so we remove the one added last. + // For example, a Screen with an OverlayScreen above it. + painters.erase(it.base()); + break; + } + } +} void EventListener::paint() { - if (painter) + if (painters.size()) { std::unique_lock lock(renderMutex); gfx->createGLContext(); - painter(); + for (std::multimap >::iterator it = painters.begin(); it != painters.end(); ++it) + { + it->second(); + } gfx->nextFrame(); } } diff --git a/libgag/src/GUIBase.cpp b/libgag/src/GUIBase.cpp index 3e8fa0ff..d6140b50 100644 --- a/libgag/src/GUIBase.cpp +++ b/libgag/src/GUIBase.cpp @@ -415,6 +415,7 @@ namespace GAGGUI Screen::~Screen() { + EventListener::instance()->removePainter("Screen"); for (std::set::iterator it=widgets.begin(); it!=widgets.end(); ++it) { delete (*it); @@ -427,6 +428,7 @@ namespace GAGGUI Sint32 frameWaitTime; this->gfx = gfx; + EventListener::instance()->addPainter("Screen", std::bind(&Screen::dispatchPaint, this)); // init widgets dispatchInit(); diff --git a/src/Engine.cpp b/src/Engine.cpp index 48aec3c5..fa486e7b 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -510,7 +510,7 @@ int Engine::run(void) static bool isPainterSet = false; if (!isPainterSet) { - EventListener::instance()->setPainter(std::bind(GameGUI::drawAll, &gui, gui.localTeamNo)); + EventListener::instance()->addPainter("GameGUI", std::bind(&GameGUI::drawAll, &gui, gui.localTeamNo)); isPainterSet = true; } std::unique_lock lock(EventListener::instance()->renderMutex); diff --git a/src/GameGUI.cpp b/src/GameGUI.cpp index 87763e85..caac66b3 100644 --- a/src/GameGUI.cpp +++ b/src/GameGUI.cpp @@ -191,7 +191,7 @@ GameGUI::GameGUI() GameGUI::~GameGUI() { - EventListener::instance()->setPainter(nullptr); + EventListener::instance()->removePainter("GameGUI"); for (ParticleSet::iterator it = particles.begin(); it != particles.end(); ++it) delete *it; } diff --git a/src/MapEdit.cpp b/src/MapEdit.cpp index 29e21b1d..d5ceab67 100644 --- a/src/MapEdit.cpp +++ b/src/MapEdit.cpp @@ -1116,6 +1116,7 @@ MapEdit::MapEdit() MapEdit::~MapEdit() { + EventListener::instance()->removePainter("MapEdit"); Toolkit::releaseSprite("data/gui/editor"); for(std::vector::iterator i=mew.begin(); i!=mew.end(); ++i) { @@ -1257,6 +1258,7 @@ int MapEdit::run(int sizeX, int sizeY, TerrainType terrainType) { game.map.setSize(sizeX, sizeY, terrainType); game.map.setGame(&game); + EventListener::instance()->addPainter("MapEdit", std::bind(&MapEdit::draw, this)); return run(); } From 8c15bc27c002fc5f62e02faa1d13d7b7bfcd8c8f Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Sun, 23 Apr 2023 20:07:30 -0700 Subject: [PATCH 24/34] WIP: Fix crash when resizing. EventListener::paint can indirectly recurse by way of WindowProc. --- libgag/include/EventListener.h | 1 + libgag/src/EventListener.cpp | 14 +++++++++----- libgag/src/GUIBase.cpp | 5 ++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index e4aa0f49..f7909e74 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -52,6 +52,7 @@ class EventListener { GraphicContext* gfx; static EventListener* el; std::atomic quit, done; + std::atomic depth; }; } #endif //__EVENTLISTENER_H \ No newline at end of file diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index d60d04f0..505a5cdb 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -34,7 +34,7 @@ std::mutex EventListener::renderMutex; #endif EventListener::EventListener(GraphicContext* gfx) -: painter(nullptr) +: painter(nullptr), depth(0) { this->gfx = gfx; el = this; @@ -68,19 +68,22 @@ void EventListener::removePainter(const std::string& name) if (painters.empty()) assert("Tried to remove a painter when painters map is empty."); std::unique_lock lock(renderMutex); - for (auto it = painters.rbegin(); it != painters.rend();++it) + for (std::multimap >::reverse_iterator it = painters.rbegin(); it != painters.rend(); ++it) { if (it->first == name) { // There might be multiple Screens active, so we remove the one added last. // For example, a Screen with an OverlayScreen above it. - painters.erase(it.base()); + painters.erase(--(it.base())); break; } } } void EventListener::paint() { + depth++; + if (depth > 1) + return; if (painters.size()) { std::unique_lock lock(renderMutex); @@ -91,6 +94,7 @@ void EventListener::paint() } gfx->nextFrame(); } + depth--; } //https://stackoverflow.com/a/51597338/8890345 #ifdef WINDOWS_OR_MINGW @@ -124,8 +128,8 @@ void EventListener::run() { std::unique_lock lock(startMutex); quit = false; - startedCond.notify_one(); } + startedCond.notify_one(); #ifdef WINDOWS_OR_MINGW SDL_AddEventWatch(eventWatch, this); // register the event watch function SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); // we need the native Windows events, so we can listen to WM_ENTERSIZEMOVE and WM_TIMER @@ -165,8 +169,8 @@ void EventListener::run() { std::unique_lock lock(doneMutex); done = true; - doneCond.notify_one(); } + doneCond.notify_one(); } int EventListener::poll(SDL_Event* e) { diff --git a/libgag/src/GUIBase.cpp b/libgag/src/GUIBase.cpp index d6140b50..44057fc6 100644 --- a/libgag/src/GUIBase.cpp +++ b/libgag/src/GUIBase.cpp @@ -530,7 +530,10 @@ namespace GAGGUI dispatchEvents(&windowEvent); // draw - dispatchPaint(); + { + std::unique_lock lock(EventListener::instance()->renderMutex); + dispatchPaint(); + } // wait timer frameWaitTime=SDL_GetTicks()-frameStartTime; From 63b9bf091ffd27a701ed2de4cca7970bd49fef9b Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Mon, 15 May 2023 14:17:06 -0700 Subject: [PATCH 25/34] Fix globules' sprites flickering to white and back. --- libgag/include/EventListener.h | 3 ++- libgag/src/EventListener.cpp | 27 ++++++++++++++++++++++----- libgag/src/GUIBase.cpp | 7 +++---- libgag/src/GraphicContext.cpp | 4 +++- src/Engine.cpp | 8 ++++---- src/GameGUI.cpp | 4 +++- src/GameGUI.h | 4 ++++ src/MapEdit.cpp | 2 ++ 8 files changed, 43 insertions(+), 16 deletions(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index f7909e74..84fe39d8 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -41,10 +41,11 @@ class EventListener { static std::condition_variable startedCond; static std::mutex doneMutex; static std::condition_variable doneCond; - static std::mutex renderMutex; + static std::recursive_mutex renderMutex; void setPainter(std::function f); void addPainter(const std::string& name, std::function f); void removePainter(const std::string& name); + static void ensureContext(); void paint(); private: std::function painter; diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 505a5cdb..63c07592 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -26,21 +26,24 @@ std::mutex EventListener::startMutex; std::condition_variable EventListener::startedCond; std::mutex EventListener::doneMutex; std::condition_variable EventListener::doneCond; -std::mutex EventListener::renderMutex; +std::recursive_mutex EventListener::renderMutex; #define SIZE_MOVE_TIMER_ID 1 #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) #define WINDOWS_OR_MINGW 1 #endif +// The depth variable is used to return early when indirect recursion happens EventListener::EventListener(GraphicContext* gfx) : painter(nullptr), depth(0) { + assert(gfx); this->gfx = gfx; el = this; done = false; quit = true; } + void EventListener::stop() { quit = true; @@ -49,25 +52,38 @@ void EventListener::stop() doneCond.wait(lock); } } + EventListener::~EventListener() { } + +// Create an OpenGL context or set existing context as current on this thread. +void EventListener::ensureContext() +{ + instance()->gfx->createGLContext(); +} + +// deprecated; use addPainter/removePainter instead. void EventListener::setPainter(std::function f) { - std::unique_lock lock(renderMutex); + std::unique_lock lock(renderMutex); painter = f; } +/* name should be the class name that the function is called on + * f is the function to call to draw the screen. + */ void EventListener::addPainter(const std::string& name, std::function f) { - std::unique_lock lock(renderMutex); + std::unique_lock lock(renderMutex); painters.insert(std::pair >(name, f)); } +// Erase the latest painter added with the name `name` from the multimap void EventListener::removePainter(const std::string& name) { if (painters.empty()) assert("Tried to remove a painter when painters map is empty."); - std::unique_lock lock(renderMutex); + std::unique_lock lock(renderMutex); for (std::multimap >::reverse_iterator it = painters.rbegin(); it != painters.rend(); ++it) { if (it->first == name) @@ -79,6 +95,7 @@ void EventListener::removePainter(const std::string& name) } } } +// Draw all the registered painters in order void EventListener::paint() { depth++; @@ -86,7 +103,7 @@ void EventListener::paint() return; if (painters.size()) { - std::unique_lock lock(renderMutex); + std::unique_lock lock(renderMutex); gfx->createGLContext(); for (std::multimap >::iterator it = painters.begin(); it != painters.end(); ++it) { diff --git a/libgag/src/GUIBase.cpp b/libgag/src/GUIBase.cpp index 44057fc6..6443d64f 100644 --- a/libgag/src/GUIBase.cpp +++ b/libgag/src/GUIBase.cpp @@ -530,10 +530,7 @@ namespace GAGGUI dispatchEvents(&windowEvent); // draw - { - std::unique_lock lock(EventListener::instance()->renderMutex); - dispatchPaint(); - } + dispatchPaint(); // wait timer frameWaitTime=SDL_GetTicks()-frameStartTime; @@ -680,6 +677,8 @@ namespace GAGGUI void Screen::dispatchPaint(void) { assert(gfx); + std::unique_lock lock(EventListener::renderMutex); + EventListener::ensureContext(); gfx->setClipRect(); paint(); for (std::set::iterator it=widgets.begin(); it!=widgets.end(); ++it) diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index 379fe826..efb23c1e 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -349,6 +349,8 @@ namespace GAGCore #ifdef HAVE_OPENGL if (_gc->optionFlags & GraphicContext::USEGPU) { + std::unique_lock lock(EventListener::renderMutex); + EventListener::ensureContext(); glState.setTexture(texture); void *pixelsPtr; @@ -2061,7 +2063,7 @@ namespace GAGCore } SDL_Surface* GraphicContext::getOrCreateSurface(int w, int h, Uint32 flags) { - std::unique_lock lock(EventListener::instance()->renderMutex); + std::unique_lock lock(EventListener::renderMutex); if (flags & USEGPU) { if (sdlsurface) diff --git a/src/Engine.cpp b/src/Engine.cpp index fa486e7b..896366f2 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -507,13 +507,13 @@ int Engine::run(void) if (nextGuiStep == 0) { // we draw - static bool isPainterSet = false; - if (!isPainterSet) + if (!gui.isRegistered) { + std::unique_lock lock(EventListener::renderMutex); EventListener::instance()->addPainter("GameGUI", std::bind(&GameGUI::drawAll, &gui, gui.localTeamNo)); - isPainterSet = true; + gui.isRegistered = true; } - std::unique_lock lock(EventListener::instance()->renderMutex); + std::unique_lock lock(EventListener::renderMutex); globalContainer->gfx->createGLContext(); gui.drawAll(gui.localTeamNo); globalContainer->gfx->nextFrame(); diff --git a/src/GameGUI.cpp b/src/GameGUI.cpp index caac66b3..0048855d 100644 --- a/src/GameGUI.cpp +++ b/src/GameGUI.cpp @@ -184,7 +184,7 @@ GameGUI::GameGUI() 128, // width 128, //height Minimap::ShowFOW), // minimap mode - + isRegistered(false), ghostManager(game) { } @@ -4385,6 +4385,8 @@ void GameGUI::drawInGameScrollableText(void) void GameGUI::drawAll(int team) { + std::unique_lock lock(EventListener::renderMutex); + EventListener::ensureContext(); // draw the map Uint32 drawOptions = (drawHealthFoodBar ? Game::DRAW_HEALTH_FOOD_BAR : 0) | (drawPathLines ? Game::DRAW_PATH_LINE : 0) | diff --git a/src/GameGUI.h b/src/GameGUI.h index b43db896..167d83e9 100644 --- a/src/GameGUI.h +++ b/src/GameGUI.h @@ -201,6 +201,10 @@ class GameGUI bool hardPause; bool isRunning; bool notmenu; + + // isRegistered tracks whether this instance of GameGUI has been registered with EventListener. + bool isRegistered; + //! true if user close the glob2 window. bool exitGlobCompletely; //! true if the game needs to flush all outgoing orders and exit diff --git a/src/MapEdit.cpp b/src/MapEdit.cpp index d5ceab67..3566c237 100644 --- a/src/MapEdit.cpp +++ b/src/MapEdit.cpp @@ -1210,6 +1210,8 @@ bool MapEdit::save(const std::string filename, const std::string name) void MapEdit::draw(void) { + std::unique_lock lock(EventListener::renderMutex); + EventListener::ensureContext(); drawMap(0, 0, globalContainer->gfx->getW() - 0, globalContainer->gfx->getH(), true, true); drawMenu(); From 38f3ea197dae2752d4067cba9a5d5ba389c909f2 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:27:49 -0700 Subject: [PATCH 26/34] Fix intermittent crash during window resizing. --- libgag/include/EventListener.h | 2 ++ libgag/src/EventListener.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index 84fe39d8..cc64018a 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -54,6 +54,8 @@ class EventListener { static EventListener* el; std::atomic quit, done; std::atomic depth; + + static std::mutex queueMutex; // used when pushing/popping queue. }; } #endif //__EVENTLISTENER_H \ No newline at end of file diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 63c07592..17ab33dc 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -21,6 +21,7 @@ #include namespace GAGCore { std::deque events = std::deque(); +std::mutex EventListener::queueMutex; EventListener* EventListener::el = nullptr; std::mutex EventListener::startMutex; std::condition_variable EventListener::startedCond; @@ -180,7 +181,10 @@ void EventListener::run() sizeMoveTimerRunning = false; } #endif - events.push_back(event); + { + std::lock_guard lock(queueMutex); + events.push_back(event); + } } } { @@ -191,6 +195,7 @@ void EventListener::run() } int EventListener::poll(SDL_Event* e) { + std::lock_guard lock(queueMutex); if (events.size()) { *e = events.front(); events.pop_front(); From 2cfc7196b7bd2c43150b742c1141f0be777f68b2 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Thu, 15 Jun 2023 19:02:07 -0700 Subject: [PATCH 27/34] Name logic thread so I can tell which thread it is. --- libgag/include/EventListener.h | 5 +++++ libgag/src/EventListener.cpp | 3 --- src/Glob2.cpp | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index cc64018a..874b82aa 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -26,6 +26,11 @@ #include #include #include + +#if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) +#define WINDOWS_OR_MINGW 1 +#endif + namespace GAGCore { extern std::deque events; class EventListener { diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 17ab33dc..611f4c4e 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -30,9 +30,6 @@ std::condition_variable EventListener::doneCond; std::recursive_mutex EventListener::renderMutex; #define SIZE_MOVE_TIMER_ID 1 -#if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) -#define WINDOWS_OR_MINGW 1 -#endif // The depth variable is used to return early when indirect recursion happens EventListener::EventListener(GraphicContext* gfx) diff --git a/src/Glob2.cpp b/src/Glob2.cpp index 15c69ea7..77cfe966 100644 --- a/src/Glob2.cpp +++ b/src/Glob2.cpp @@ -32,6 +32,7 @@ #include "CreditScreen.h" #include "EditorMainMenu.h" #include "Engine.h" +#include "EventListener.h" #include "Game.h" #include "GUIMessageBox.h" #include "Header.h" @@ -66,6 +67,11 @@ # include #endif +#if defined(__linux__) || defined(__APPLE__) +// for setting thread name +#include +#endif + #ifdef __APPLE__ # include # include @@ -235,11 +241,22 @@ int Glob2::run(int argc, char *argv[]) if (!globalContainer->hostServer && !globalContainer->runNoX && std::this_thread::get_id() == globalContainer->mainthr) { + // Handle events in main thread globalContainer->load(true); finish(); return 0; } else { + // set thread name + const char* name = "Game logic"; +#ifdef WINDOWS_OR_MINGW + std::vector wideName(4096); + MultiByteToWideChar(CP_ACP, 0, name, -1, wideName.data(), 4096); + SetThreadDescription(GetCurrentThread(), wideName.data()); +#elif defined(__linux__) || defined(__APPLE__) + pthread_setname_np(pthread_self(), name); +#endif + // Handle game logic in logic thread globalContainer->load(false); } From dbeefd58dc8205049c7f084c328b7a28d54e1d10 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Thu, 15 Jun 2023 19:55:47 -0700 Subject: [PATCH 28/34] Document EventListener. --- libgag/src/EventListener.cpp | 42 ++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 611f4c4e..5057f638 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -19,6 +19,14 @@ #include "EventListener.h" #include #include + +/* + The main thread opens the window and handles events; the logic thread handles + everything else. Every so often, the logic thread paints the screen. During + resize, the event watch function will call paint() on every registered painter. + + The logic thread is started in Glob2::run. +*/ namespace GAGCore { std::deque events = std::deque(); std::mutex EventListener::queueMutex; @@ -42,6 +50,7 @@ EventListener::EventListener(GraphicContext* gfx) quit = true; } +//! End the event listening loop void EventListener::stop() { quit = true; @@ -55,28 +64,32 @@ EventListener::~EventListener() { } -// Create an OpenGL context or set existing context as current on this thread. +//! Create an OpenGL context or set existing context as current on this thread. void EventListener::ensureContext() { instance()->gfx->createGLContext(); } -// deprecated; use addPainter/removePainter instead. +//! deprecated; use addPainter/removePainter instead. void EventListener::setPainter(std::function f) { std::unique_lock lock(renderMutex); painter = f; } -/* name should be the class name that the function is called on - * f is the function to call to draw the screen. +/** Add a painter to the list of painting functions to be called on window resize. + * name should be the class name that the function is called on + * f is the function to call to draw the screen. */ void EventListener::addPainter(const std::string& name, std::function f) { std::unique_lock lock(renderMutex); painters.insert(std::pair >(name, f)); } -// Erase the latest painter added with the name `name` from the multimap + +/** Erase the latest painter added with the name `name` from the multimap + * Removes the most recently added painter with that name. + */ void EventListener::removePainter(const std::string& name) { if (painters.empty()) @@ -93,7 +106,8 @@ void EventListener::removePainter(const std::string& name) } } } -// Draw all the registered painters in order + +//! Draw all the registered painters in order void EventListener::paint() { depth++; @@ -111,6 +125,9 @@ void EventListener::paint() } depth--; } + +//! Handle user resizing the game window on Microsoft Windows. +// TODO: Handle window resizing on macOS //https://stackoverflow.com/a/51597338/8890345 #ifdef WINDOWS_OR_MINGW bool sizeMoveTimerRunning = false; @@ -138,6 +155,10 @@ int eventWatch(void* self, SDL_Event* event) { return 0; } #endif + +/** Listens for events and adds them to a queue. + * Call EventListener::poll to get an event from the queue. + */ void EventListener::run() { { @@ -190,6 +211,10 @@ void EventListener::run() } doneCond.notify_one(); } + +/** Drop-in replacement for SDL_PollEvent. + * Call this to get an event from the queue. + */ int EventListener::poll(SDL_Event* e) { std::lock_guard lock(queueMutex); @@ -200,10 +225,15 @@ int EventListener::poll(SDL_Event* e) } return 0; } + +/** Gets the active EventListener instance. + * Does not initialize the EventListener if it is nullptr. + */ EventListener *EventListener::instance() { return el; } +//! Checks if the EventListener is currently in the event handling loop. bool EventListener::isRunning() { return !quit; From df0dbd3704b471c759e01527d61be335eedceb8b Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Thu, 15 Jun 2023 21:01:44 -0700 Subject: [PATCH 29/34] The queue doesn't need to be double-ended. --- libgag/include/EventListener.h | 4 ++-- libgag/src/EventListener.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index 874b82aa..02d0201b 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -20,7 +20,7 @@ #define __EVENTLISTENER_H #include "GraphicContext.h" #include -#include +#include #include #include #include @@ -32,7 +32,7 @@ #endif namespace GAGCore { -extern std::deque events; +extern std::queue events; class EventListener { public: EventListener(GraphicContext* gfx); diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 5057f638..a0ca1e3d 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -28,7 +28,7 @@ The logic thread is started in Glob2::run. */ namespace GAGCore { -std::deque events = std::deque(); +std::queue events = std::queue(); std::mutex EventListener::queueMutex; EventListener* EventListener::el = nullptr; std::mutex EventListener::startMutex; @@ -201,7 +201,7 @@ void EventListener::run() #endif { std::lock_guard lock(queueMutex); - events.push_back(event); + events.push(event); } } } @@ -220,7 +220,7 @@ int EventListener::poll(SDL_Event* e) std::lock_guard lock(queueMutex); if (events.size()) { *e = events.front(); - events.pop_front(); + events.pop(); return 1; } return 0; From f2f17d4babc7be5568e05ffbe1799669cc94db15 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Fri, 16 Jun 2023 08:53:58 -0700 Subject: [PATCH 30/34] Fix flickering during window resize. Textures still turn white when letting go of the mouse button, though. --- libgag/include/EventListener.h | 1 + libgag/src/EventListener.cpp | 6 +++++- libgag/src/GraphicContext.cpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index 02d0201b..c08cadb9 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -39,6 +39,7 @@ class EventListener { void run(); void stop(); bool isRunning(); + bool isResizing(); int poll(SDL_Event* e); static EventListener *instance(); ~EventListener(); diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index a0ca1e3d..35590dd0 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -129,8 +129,8 @@ void EventListener::paint() //! Handle user resizing the game window on Microsoft Windows. // TODO: Handle window resizing on macOS //https://stackoverflow.com/a/51597338/8890345 -#ifdef WINDOWS_OR_MINGW bool sizeMoveTimerRunning = false; +#ifdef WINDOWS_OR_MINGW int eventWatch(void* self, SDL_Event* event) { if (event->type == SDL_SYSWMEVENT) { @@ -156,6 +156,10 @@ int eventWatch(void* self, SDL_Event* event) { } #endif +bool EventListener::isResizing() +{ + return sizeMoveTimerRunning; +} /** Listens for events and adds them to a queue. * Call EventListener::poll to get an event from the queue. */ diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index efb23c1e..60d80dd8 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -1675,7 +1675,7 @@ namespace GAGCore if (_gc->optionFlags & GraphicContext::USEGPU) { // upload - if (surface->dirty) + if (surface->dirty || EventListener::instance()->isResizing()) surface->uploadToTexture(); // state change From 06e398321d37062599467d17ac76eb32214cd407 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Fri, 16 Jun 2023 09:28:03 -0700 Subject: [PATCH 31/34] Fix textures turning white after resizing window. --- libgag/include/SDLGraphicContext.h | 1 + libgag/src/GraphicContext.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libgag/include/SDLGraphicContext.h b/libgag/include/SDLGraphicContext.h index e07e1388..2bbbd2b6 100644 --- a/libgag/include/SDLGraphicContext.h +++ b/libgag/include/SDLGraphicContext.h @@ -326,6 +326,7 @@ namespace GAGCore Uint32 optionFlags; std::string windowTitle; std::string appIcon; + int resizeTimer; public: //! Constructor. Create a new window of size (w,h). If useGPU is true, use GPU for accelerated 2D (OpenGL or DX) diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index 60d80dd8..04a113fa 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -1674,8 +1674,10 @@ namespace GAGCore #ifdef HAVE_OPENGL if (_gc->optionFlags & GraphicContext::USEGPU) { + if (!resizeTimer && EventListener::instance()->isResizing()) + resizeTimer++; // upload - if (surface->dirty || EventListener::instance()->isResizing()) + if (surface->dirty || resizeTimer) surface->uploadToTexture(); // state change @@ -1967,7 +1969,8 @@ namespace GAGCore GraphicContext::GraphicContext(int w, int h, Uint32 flags, const std::string title, const std::string icon): windowTitle(title), - appIcon(icon) + appIcon(icon), + resizeTimer(0) { // some assert on the universe's structure assert(sizeof(Color) == 4); @@ -2275,6 +2278,8 @@ namespace GAGCore { SDL_UpdateWindowSurface(window); } + if (resizeTimer) + resizeTimer--; } } From 8e21b7cd3dd77b34ea836561a1b531d61abfa392 Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Fri, 16 Jun 2023 15:37:30 -0700 Subject: [PATCH 32/34] Rename poorly-named variable. --- src/Glob2.cpp | 6 +++--- src/GlobalContainer.cpp | 6 +++--- src/GlobalContainer.h | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Glob2.cpp b/src/Glob2.cpp index 77cfe966..7b7ce197 100644 --- a/src/Glob2.cpp +++ b/src/Glob2.cpp @@ -235,7 +235,7 @@ int Glob2::run(int argc, char *argv[]) globalContainer->mainthr = std::this_thread::get_id(); globalContainer->mainthrSet = true; if (!globalContainer->hostServer && !globalContainer->runNoX) { - globalContainer->otherthread = new std::thread(&Glob2::run, this, argc, argv); + globalContainer->logicThread = new std::thread(&Glob2::run, this, argc, argv); } } if (!globalContainer->hostServer && @@ -447,8 +447,8 @@ int Glob2::run(int argc, char *argv[]) } } - // quit event loop so otherthread can be joined by main thread - if (globalContainer->otherthread) + // quit event loop so logic thread can be joined by main thread + if (globalContainer->logicThread) { EventListener *el = EventListener::instance(); el->stop(); diff --git a/src/GlobalContainer.cpp b/src/GlobalContainer.cpp index 2c05332d..a1192371 100644 --- a/src/GlobalContainer.cpp +++ b/src/GlobalContainer.cpp @@ -130,7 +130,7 @@ GlobalContainer::GlobalContainer(void) replayShowFlags = true; mainthrSet = false; - otherthread = nullptr; + logicThread = nullptr; #ifndef YOG_SERVER_ONLY replayReader = NULL; @@ -524,8 +524,8 @@ void GlobalContainer::loadClient(bool runEventListener) gfx->unsetContext(); el = new EventListener(gfx); el->run(); - otherthread->join(); - delete otherthread; + logicThread->join(); + delete logicThread; return; } diff --git a/src/GlobalContainer.h b/src/GlobalContainer.h index 64c67eef..8724bdcc 100644 --- a/src/GlobalContainer.h +++ b/src/GlobalContainer.h @@ -142,8 +142,8 @@ class GlobalContainer bool replayShowAreas; //!< Show areas of gui.localPlayer or not. Can be edited real-time. bool replayShowFlags; //!< Show all flags or show none. Can be edited real-time. - // other thread handles events in the queue, game logic, rendering, etc. - std::thread* otherthread; + // logic thread handles events in the queue, game logic, rendering, etc. + std::thread* logicThread; // main thread listens for SDL events and adds them to a queue std::thread::id mainthr; std::atomic mainthrSet; From 60a544b540c24301e2fe93e062aaeea39077feec Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:33:45 -0800 Subject: [PATCH 33/34] Fix cursor flickering at screen edge when resizing. --- libgag/include/CursorManager.h | 1 + libgag/include/SDLGraphicContext.h | 3 +++ libgag/src/CursorManager.cpp | 8 ++++++++ libgag/src/GraphicContext.cpp | 4 ++++ libgag/src/Sprite.cpp | 15 +++++++++++++++ 5 files changed, 31 insertions(+) diff --git a/libgag/include/CursorManager.h b/libgag/include/CursorManager.h index 25f1cf4d..e0bd1827 100644 --- a/libgag/include/CursorManager.h +++ b/libgag/include/CursorManager.h @@ -67,6 +67,7 @@ namespace GAGCore CursorManager(); //! Load the cursor sprites void load(void); + void reinitTextures(void); //! Select the next type given the mouse position void nextTypeFromMouse(DrawableSurface *ds, int x, int y, bool button); //! Manually set the next type diff --git a/libgag/include/SDLGraphicContext.h b/libgag/include/SDLGraphicContext.h index 2bbbd2b6..f44261ad 100644 --- a/libgag/include/SDLGraphicContext.h +++ b/libgag/include/SDLGraphicContext.h @@ -156,6 +156,7 @@ namespace GAGCore protected: friend struct Color; friend class GraphicContext; + friend class Sprite; //! the underlying software SDL surface SDL_Surface *sdlsurface; //! The clipping rect, we do not draw outside it @@ -447,6 +448,8 @@ namespace GAGCore //! Load a sprite from the file, return true if any frame have been loaded bool load(const std::string filename); + + void reinit(); //! Set the (r,g,b) color to a sprite's base color virtual void setBaseColor(Uint8 r, Uint8 g, Uint8 b) { actColor = Color(r, g, b); } diff --git a/libgag/src/CursorManager.cpp b/libgag/src/CursorManager.cpp index cfe4f10b..ac670576 100644 --- a/libgag/src/CursorManager.cpp +++ b/libgag/src/CursorManager.cpp @@ -46,6 +46,14 @@ namespace GAGCore cursors.push_back(Toolkit::getSprite("data/gfx/cursor/mark")); setDefaultColor(); } + + void CursorManager::reinitTextures(void) + { + for (Sprite *cursor : cursors) + { + cursor->reinit(); + } + } void CursorManager::nextTypeFromMouse(DrawableSurface *ds, int x, int y, bool button) { diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index 04a113fa..21646561 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -2262,6 +2262,10 @@ namespace GAGCore { int mx, my; unsigned b = SDL_GetMouseState(&mx, &my); + if (resizeTimer) { + std::lock_guard lock(EventListener::renderMutex); + cursorManager.reinitTextures(); + } cursorManager.nextTypeFromMouse(this, mx, my, b != 0); setClipRect(); cursorManager.draw(this, mx, my); diff --git a/libgag/src/Sprite.cpp b/libgag/src/Sprite.cpp index 5293e3f3..f644beef 100644 --- a/libgag/src/Sprite.cpp +++ b/libgag/src/Sprite.cpp @@ -70,6 +70,21 @@ namespace GAGCore return getFrameCount() > 0; } + + void Sprite::reinit() + { + + for (DrawableSurface* image : images) + { + if (image) + image->uploadToTexture(); + } + for (RotatedImage* turned : rotated) + { + if (turned && turned->orig) + turned->orig->uploadToTexture(); + } + } DrawableSurface *Sprite::getRotatedSurface(int index) { From 7f2ff281323a39774e0ae0a5d2783fb56111356f Mon Sep 17 00:00:00 2001 From: Nathan Mills <38995150+Quipyowert2@users.noreply.github.com> Date: Sat, 11 Nov 2023 17:55:03 -0800 Subject: [PATCH 34/34] Fix textures flickering to white, try 4. --- libgag/include/SDLGraphicContext.h | 2 ++ libgag/src/GraphicContext.cpp | 42 ++++++++++++++++++++++++------ libgag/src/Sprite.cpp | 11 ++++++-- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/libgag/include/SDLGraphicContext.h b/libgag/include/SDLGraphicContext.h index f44261ad..138700eb 100644 --- a/libgag/include/SDLGraphicContext.h +++ b/libgag/include/SDLGraphicContext.h @@ -328,6 +328,7 @@ namespace GAGCore std::string windowTitle; std::string appIcon; int resizeTimer; + Sint64 framesDrawn, frameStartResize, frameStopResize; public: //! Constructor. Create a new window of size (w,h). If useGPU is true, use GPU for accelerated 2D (OpenGL or DX) @@ -345,6 +346,7 @@ namespace GAGCore virtual void unsetContext(); static GraphicContext* instance(); void resetMatrices(); + bool isResizing(); virtual void setClipRect(int x, int y, int w, int h); virtual void setClipRect(void); virtual void nextFrame(void); diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index 21646561..fc8ad794 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -1073,6 +1073,10 @@ namespace GAGCore void DrawableSurface::drawSurface(int x, int y, DrawableSurface *surface, int sx, int sy, int sw, int sh, Uint8 alpha) { + if (surface != _gc && (surface->dirty || _gc->isResizing())) + surface->uploadToTexture(); + if (this != _gc && (this->dirty || _gc->isResizing())) + this->uploadToTexture(); if (alpha == Color::ALPHA_OPAQUE) { #ifdef HAVE_OPENGL @@ -1674,10 +1678,8 @@ namespace GAGCore #ifdef HAVE_OPENGL if (_gc->optionFlags & GraphicContext::USEGPU) { - if (!resizeTimer && EventListener::instance()->isResizing()) - resizeTimer++; // upload - if (surface->dirty || resizeTimer) + if (surface->dirty || isResizing()) surface->uploadToTexture(); // state change @@ -1967,10 +1969,13 @@ namespace GAGCore return modes; } - GraphicContext::GraphicContext(int w, int h, Uint32 flags, const std::string title, const std::string icon): + GraphicContext::GraphicContext(int w, int h, Uint32 flags, const std::string title, const std::string icon) : windowTitle(title), appIcon(icon), - resizeTimer(0) + resizeTimer(0), + framesDrawn(0), + frameStartResize(-1), + frameStopResize(-1) { // some assert on the universe's structure assert(sizeof(Color) == 4); @@ -2065,6 +2070,15 @@ namespace GAGCore glLoadIdentity(); } + bool GraphicContext::isResizing() + { + static EventListener* instance = nullptr; + if (!instance) + instance = EventListener::instance(); + // Either currently resizing or is within 5 frames of stopping resize. + return instance->isResizing() || (frameStartResize <= frameStopResize && framesDrawn < (frameStopResize + 5)); + } + SDL_Surface* GraphicContext::getOrCreateSurface(int w, int h, Uint32 flags) { std::unique_lock lock(EventListener::renderMutex); if (flags & USEGPU) @@ -2262,8 +2276,8 @@ namespace GAGCore { int mx, my; unsigned b = SDL_GetMouseState(&mx, &my); - if (resizeTimer) { - std::lock_guard lock(EventListener::renderMutex); + if (isResizing()) + { cursorManager.reinitTextures(); } cursorManager.nextTypeFromMouse(this, mx, my, b != 0); @@ -2282,8 +2296,20 @@ namespace GAGCore { SDL_UpdateWindowSurface(window); } - if (resizeTimer) + bool isResizing = EventListener::instance()->isResizing(); + if (resizeTimer && !isResizing) + { + // Resize flag is set but we are not currently resizing. + frameStopResize = framesDrawn; resizeTimer--; + } + if (!resizeTimer && isResizing) + { + // The user started resizing the window. + frameStartResize = framesDrawn; + resizeTimer++; + } + framesDrawn++; } } diff --git a/libgag/src/Sprite.cpp b/libgag/src/Sprite.cpp index f644beef..d827e542 100644 --- a/libgag/src/Sprite.cpp +++ b/libgag/src/Sprite.cpp @@ -81,8 +81,15 @@ namespace GAGCore } for (RotatedImage* turned : rotated) { - if (turned && turned->orig) - turned->orig->uploadToTexture(); + if (turned) + { + if (turned->orig) + turned->orig->uploadToTexture(); + for (auto& pair : turned->rotationMap) + { + pair.second->uploadToTexture(); + } + } } }