- Actually displaying installed title now

- Try to clean up the async stuff when the vector gets bigger
This commit is contained in:
Maschell 2020-02-20 02:03:43 +01:00
parent 4c4fbcfb2e
commit d97d5ed325
13 changed files with 589 additions and 27 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
*.rpx
build/
src/resources/filelist.h
*.save-failed

View File

@ -21,6 +21,7 @@ TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := src \
src/fs \
src/game \
src/gui \
src/menu \
src/resources \

BIN
data/images/noGameIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -6,6 +6,7 @@ extern "C" {
#endif
#define LAUNCHIINE_VERSION "v0.1"
#define META_PATH "/meta"
#ifdef __cplusplus
}

202
src/game/GameList.cpp Normal file
View File

@ -0,0 +1,202 @@
#include <algorithm>
#include <string>
#include <string.h>
#include <coreinit/mcp.h>
#include <nn/acp/nn_acp_types.h>
#include <coreinit/cache.h>
#include "utils/AsyncExecutor.h"
#include "GameList.h"
#include "common/common.h"
#include "fs/DirList.h"
#include "fs/FSUtils.h"
#include "utils/logger.h"
#include "utils/StringTools.h"
void GameList::clear() {
for (auto const& x : fullGameList) {
if(x != NULL) {
if(x->imageData != NULL) {
DEBUG_FUNCTION_LINE("Delete the image data\n");
delete x->imageData;
x->imageData = NULL;
}
delete x;
}
}
gameFilter.clear();
fullGameList.clear();
filteredList.clear();
//! Clear memory of the vector completely
std::vector<gameInfo *>().swap(filteredList);
std::vector<gameInfo*>().swap(fullGameList);
titleListChanged(this);
}
gameInfo * GameList::getGameInfo(uint64_t titleId) const {
for (uint32_t i = 0; i < filteredList.size(); ++i) {
if(titleId == filteredList[i]->titleId)
return filteredList[i];
}
return NULL;
}
extern "C" int ACPGetTitleMetaXml(uint64_t titleid, ACPMetaXml*);
int32_t GameList::readGameList() {
// Clear list
for (auto const& x : fullGameList) {
delete x;
}
fullGameList.clear();
//! Clear memory of the vector completely
std::vector<gameInfo*>().swap(fullGameList);
int32_t cnt = 0;
MCPError mcp = MCP_Open();
if (mcp < 0) {
return 0;
}
MCPError titleCount = MCP_TitleCount(mcp);
if (titleCount < 0) {
MCP_Close(mcp);
return 0;
}
std::vector<struct MCPTitleListType> titles(titleCount);
uint32_t realTitleCount;
MCPError err = MCP_TitleList(mcp, &realTitleCount, titles.data(), titles.size() * sizeof(decltype(titles)::value_type));
if (err < 0) {
MCP_Close(mcp);
return 0;
}
if (realTitleCount != titles.size()) {
titles.resize(realTitleCount);
}
for (auto title_candidate : titles) {
if(true || (title_candidate.titleId & 0xFFFFFFFF00000000L) == 0x0005000000000000L) {
gameInfo* newGameInfo = new gameInfo;
newGameInfo->titleId = title_candidate.titleId;
newGameInfo->gamePath = title_candidate.path;
newGameInfo->name = "<unknown>";
newGameInfo->imageData = NULL;
DCFlushRange(newGameInfo, sizeof(gameInfo));
fullGameList.push_back(newGameInfo);
titleAdded(newGameInfo);
cnt++;
}
}
return cnt;
}
void GameList::loadIcons() {
for (int i = 0; i < this->size(); i++) {
gameInfo * newHeader = this->at(i);
ACPMetaXml* meta = (ACPMetaXml*)calloc(1, 0x4000); //TODO fix wut
if(meta) {
auto acp = ACPGetTitleMetaXml(newHeader->titleId, meta);
if(acp >= 0) {
newHeader->name = meta->shortname_en;
}
free(meta);
}
if(newHeader->imageData == NULL) {
std::string filepath = "fs:" + newHeader->gamePath + META_PATH + "/iconTex.tga";
uint8_t *buffer = NULL;
uint32_t bufferSize = 0;
int iResult = FSUtils::LoadFileToMem(filepath.c_str(), &buffer, &bufferSize);
if(iResult > 0) {
GuiImageData * imageData = new GuiImageData(buffer, bufferSize, GX2_TEX_CLAMP_MODE_MIRROR);
if(imageData) {
newHeader->imageData = imageData;
}
//! free original image buffer which is converted to texture now and not needed anymore
free(buffer);
}
}
DCFlushRange(newHeader, sizeof(gameInfo));
titleUpdated(newHeader);
}
}
void GameList::internalFilterList(std::vector<gameInfo*> &fullList) {
for (uint32_t i = 0; i < fullList.size(); ++i) {
gameInfo *header = fullList[i];
//! TODO: do filtering as needed
filteredList.push_back(header);
}
}
int32_t GameList::filterList(const char * filter) {
if(filter) {
gameFilter = filter;
}
if(fullGameList.size() == 0) {
readGameList();
}
filteredList.clear();
// Filter current game list if selected
internalFilterList(fullGameList);
sortList();
titleListChanged(this);
AsyncExecutor::execute([&] { loadIcons();});
return filteredList.size();
}
void GameList::internalLoadUnfiltered(std::vector<gameInfo*> & fullList) {
for (uint32_t i = 0; i < fullList.size(); ++i) {
gameInfo *header = fullList[i];
filteredList.push_back(header);
}
}
int32_t GameList::loadUnfiltered() {
if(fullGameList.size() == 0) {
readGameList();
}
gameFilter.clear();
filteredList.clear();
// Filter current game list if selected
internalLoadUnfiltered(fullGameList);
sortList();
AsyncExecutor::execute([&] { loadIcons();});
titleListChanged(this);
return filteredList.size();
}
void GameList::sortList() {
std::sort(filteredList.begin(), filteredList.end(), nameSortCallback);
}
bool GameList::nameSortCallback(const gameInfo *a, const gameInfo *b) {
return (strcasecmp(((gameInfo *) a)->name.c_str(), ((gameInfo *) b)->name.c_str()) < 0);
}

102
src/game/GameList.h Normal file
View File

@ -0,0 +1,102 @@
#ifndef GAME_LIST_H_
#define GAME_LIST_H_
#include <vector>
#include <stdint.h>
#include <coreinit/mcp.h>
#include <gui/sigslot.h>
#include <gui/GuiImageData.h>
typedef struct _gameInfo {
uint64_t titleId;
std::string name;
std::string gamePath;
GuiImageData * imageData;
} gameInfo;
class GameList {
public:
GameList() : selectedGame(0) { };
~GameList() { clear(); };
int32_t size() const {
return filteredList.size();
}
int32_t gameCount() const {
return fullGameList.size();
}
int32_t filterList(const char * gameFilter = NULL);
int32_t loadUnfiltered();
gameInfo * at(int32_t i) const {
return operator[](i);
}
gameInfo * operator[](int32_t i) const {
if (i < 0 || i >= (int32_t) filteredList.size())
return NULL;
return filteredList[i];
}
gameInfo * getGameInfo(uint64_t titleId) const;
const char * getCurrentFilter() const {
return gameFilter.c_str();
}
void sortList();
void clear();
bool operator!() const {
return (fullGameList.size() == 0);
}
//! Gamelist scrolling operators
int32_t operator+=(int32_t i) {
return (selectedGame = (selectedGame+i) % filteredList.size());
}
int32_t operator-=(int32_t i) {
return (selectedGame = (selectedGame-i+filteredList.size()) % filteredList.size());
}
int32_t operator++() {
return (selectedGame = (selectedGame+1) % filteredList.size());
}
int32_t operator--() {
return (selectedGame = (selectedGame-1+filteredList.size()) % filteredList.size());
}
int32_t operator++(int32_t i) {
return operator++();
}
int32_t operator--(int32_t i) {
return operator--();
}
gameInfo * GetCurrentSelected() const {
return operator[](selectedGame);
}
std::vector<gameInfo *> & getfilteredList(void) {
return filteredList;
}
std::vector<gameInfo *> & getFullGameList(void) {
return fullGameList;
}
sigslot::signal1<GameList *> titleListChanged;
sigslot::signal1<gameInfo *> titleUpdated;
sigslot::signal1<gameInfo *> titleAdded;
protected:
int32_t readGameList();
void internalFilterList(std::vector<gameInfo*> & fullList);
void internalLoadUnfiltered(std::vector<gameInfo*> & fullList);
void loadIcons();
static bool nameSortCallback(const gameInfo *a, const gameInfo *b);
static GameList *gameListInstance;
std::string gameFilter;
int32_t selectedGame;
std::vector<gameInfo *> filteredList;
std::vector<gameInfo *> fullGameList;
};
#endif

View File

@ -16,31 +16,155 @@
****************************************************************************/
#include <gui/GuiIconGrid.h>
#include <gui/GuiController.h>
#include <coreinit/cache.h>
#include "common/common.h"
#include "Application.h"
#include <gui/video/CVideo.h>
#include "utils/logger.h"
GuiIconGrid::GuiIconGrid(int32_t w, int32_t h, int32_t GameIndex)
GuiIconGrid::GuiIconGrid(int32_t w, int32_t h, uint64_t GameIndex)
: GuiTitleBrowser(w, h, GameIndex),
particleBgImage(w, h, 50, 60.0f, 90.0f, 0.6f, 1.0f) {
particleBgImage(w, h, 50, 60.0f, 90.0f, 0.6f, 1.0f)
, touchTrigger(GuiTrigger::CHANNEL_1, GuiTrigger::VPAD_TOUCH)
, wpadTouchTrigger(GuiTrigger::CHANNEL_2 | GuiTrigger::CHANNEL_3 | GuiTrigger::CHANNEL_4 | GuiTrigger::CHANNEL_5, GuiTrigger::BUTTON_A)
, noIcon(Resources::GetFile("noGameIcon.png"), Resources::GetFileSize("noGameIcon.png"), GX2_TEX_CLAMP_MODE_MIRROR) {
append(&particleBgImage);
selectedGame = GameIndex;
listOffset = selectedGame / (MAX_COLS * MAX_ROWS);
targetLeftPosition = -listOffset * getWidth();
currentLeftPosition = targetLeftPosition;
}
GuiIconGrid::~GuiIconGrid() {
containerMutex.lock();
for (auto const& x : gameInfoContainers) {
remove(x.second->button);
delete x.second;
}
gameInfoContainers.clear();
containerMutex.unlock();
}
void GuiIconGrid::setSelectedGame(int32_t idx) {
void GuiIconGrid::setSelectedGame(uint64_t idx) {
this->selectedGame = idx;
}
int32_t GuiIconGrid::getSelectedGame(void) {
uint64_t GuiIconGrid::getSelectedGame(void) {
return selectedGame;
}
void GuiIconGrid::OnGameTitleListUpdated(GameList * gameList) {
containerMutex.lock();
for(int32_t i = 0; i < gameList->size(); i++) {
gameInfo * info = gameList->at(i);
GameInfoContainer * container = NULL;
for (auto const& x : gameInfoContainers) {
if(info->titleId == x.first) {
container = x.second;
break;
}
}
if(container == NULL) {
OnGameTitleAdded(info);
}
}
containerMutex.unlock();
bUpdatePositions = true;
}
void GuiIconGrid::OnLaunchClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) {
//! do not auto launch when wiimote is pointing to screen and presses A
//if((trigger == &buttonATrigger) && (controller->chan & (GuiTrigger::CHANNEL_2 | GuiTrigger::CHANNEL_3 | GuiTrigger::CHANNEL_4 | GuiTrigger::CHANNEL_5)) && controller->data.validPointer) {
// return;
//}
DEBUG_FUNCTION_LINE("Tried to launch %s (%016llX)\n", gameInfoContainers[getSelectedGame()]->info->name.c_str(),getSelectedGame());
gameLaunchClicked(this, getSelectedGame());
}
void GuiIconGrid::OnGameButtonClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) {
containerMutex.lock();
for (auto const& x : gameInfoContainers) {
if(x.second->button == button) {
if(selectedGame == (x.second->info->titleId)) {
if(gameLaunchTimer < 30)
OnLaunchClick(button, controller, trigger);
} else {
setSelectedGame(x.second->info->titleId);
gameSelectionChanged(this, selectedGame);
}
gameLaunchTimer = 0;
break;
}
}
containerMutex.unlock();
}
void GuiIconGrid::OnGameTitleAdded(gameInfo * info) {
DEBUG_FUNCTION_LINE("Adding %016llX\n", info->titleId);
GuiImage * image = new GuiImage(&noIcon);
GuiButton * button = new GuiButton(noIcon.getWidth(), noIcon.getHeight());
button->setImage(image);
button->setPosition(0, 0);
button->setEffectGrow();
button->setTrigger(&touchTrigger);
button->setTrigger(&wpadTouchTrigger);
button->setSoundClick(buttonClickSound);
//button->setClickable( (idx < gameList->size()) );
//button->setSelectable( (idx < gameList->size()) );
button->clicked.connect(this, &GuiIconGrid::OnGameButtonClick);
GameInfoContainer * container = new GameInfoContainer(button, image, info);
containerMutex.lock();
gameInfoContainers[info->titleId] = container;
containerMutex.unlock();
this->append(button);
bUpdatePositions = true;
}
void GuiIconGrid::OnGameTitleUpdated(gameInfo * info) {
DEBUG_FUNCTION_LINE("Updating infos of %016llX\n", info->titleId);
GameInfoContainer * container = NULL;
containerMutex.lock();
for (auto const& x : gameInfoContainers) {
if(info->titleId == x.first) {
container = x.second;
break;
}
}
if(container != NULL) {
container->updateImageData();
}
containerMutex.unlock();
bUpdatePositions = true;
}
void GuiIconGrid::process() {
if(currentLeftPosition < targetLeftPosition) {
currentLeftPosition += 35;
if(currentLeftPosition > targetLeftPosition)
currentLeftPosition = targetLeftPosition;
bUpdatePositions = true;
} else if(currentLeftPosition > targetLeftPosition) {
currentLeftPosition -= 35;
if(currentLeftPosition < targetLeftPosition)
currentLeftPosition = targetLeftPosition;
bUpdatePositions = true;
}
if(bUpdatePositions) {
bUpdatePositions = false;
updateButtonPositions();
}
gameLaunchTimer++;
GuiFrame::process();
}
@ -48,6 +172,37 @@ void GuiIconGrid::update(GuiController * c) {
GuiFrame::update(c);
}
void GuiIconGrid::draw(CVideo *pVideo) {
GuiFrame::draw(pVideo);
void GuiIconGrid::updateButtonPositions() {
int32_t col = 0, row = 0, listOff = 0;
int i = 0;
containerMutex.lock();
for (auto const& x : gameInfoContainers) {
listOff = i / (MAX_COLS * MAX_ROWS);
float posX = currentLeftPosition + listOff * width + ( col * (noIcon.getWidth() + noIcon.getWidth() * 0.5f) - (MAX_COLS * 0.5f - 0.5f) * (noIcon.getWidth() + noIcon.getWidth() * 0.5f) );
float posY = -row * (noIcon.getHeight() + noIcon.getHeight() * 0.5f) + (MAX_ROWS * 0.5f - 0.5f) * (noIcon.getHeight() + noIcon.getHeight() * 0.5f) + 30.0f;
if(x.second->button != NULL) {
x.second->button->setPosition(posX, posY);
}
col++;
if(col >= MAX_COLS) {
col = 0;
row++;
}
if(row >= MAX_ROWS)
row = 0;
i++;
}
containerMutex.unlock();
}
void GuiIconGrid::draw(CVideo *pVideo) {
containerMutex.lock();
GuiFrame::draw(pVideo);
containerMutex.unlock();
}

View File

@ -19,25 +19,77 @@
#include <map>
#include "gui/GuiTitleBrowser.h"
#include <gui/GuiParticleImage.h>
#include "utils/AsyncExecutor.h"
#include "utils/logger.h"
class GuiIconGrid : public GuiTitleBrowser, public sigslot::has_slots<> {
public:
GuiIconGrid(int32_t w, int32_t h, int32_t GameIndex);
GuiIconGrid(int32_t w, int32_t h, uint64_t selectedTitleId);
virtual ~GuiIconGrid();
void setSelectedGame(int32_t idx);
int32_t getSelectedGame(void);
void setSelectedGame(uint64_t idx);
uint64_t getSelectedGame(void);
void update(GuiController * t);
void draw(CVideo *pVideo);
void process();
void OnGameTitleListUpdated(GameList * list);
void OnAddGameTitle(gameInfo * info);
void OnGameTitleUpdated(gameInfo * info);
void OnGameTitleAdded(gameInfo * info);
private:
static const int32_t MAX_ROWS = 3;
static const int32_t MAX_COLS = 5;
GuiSound *buttonClickSound;
int32_t selectedGame = 0;
GuiParticleImage particleBgImage;
GuiTrigger touchTrigger;
GuiTrigger wpadTouchTrigger;
GuiImageData noIcon;
void OnLaunchClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger);
void OnGameButtonClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger);
void updateButtonPositions();
int32_t listOffset;
uint64_t selectedGame;
int32_t currentLeftPosition;
int32_t targetLeftPosition;
uint32_t gameLaunchTimer;
bool bUpdatePositions = false;
class GameInfoContainer {
public:
GameInfoContainer(GuiButton* button, GuiImage * image, gameInfo * info) {
this->image = image;
this->info = info;
this->button = button;
}
~GameInfoContainer() {
if(button != NULL) {
AsyncExecutor::pushForDelete(button);
}
if(image != NULL) {
AsyncExecutor::pushForDelete(image);
}
}
void updateImageData() {
if(image != NULL && info != NULL && info->imageData != NULL) {
image->setImageData(info->imageData);
}
}
GuiImage * image;
gameInfo * info;
GuiButton * button;
};
CMutex containerMutex;
std::map<uint64_t, GameInfoContainer *> gameInfoContainers;
};

View File

@ -2,15 +2,20 @@
#include <gui/Gui.h>
#include <gui/sigslot.h>
#include "game/GameList.h"
class GuiTitleBrowser : public GuiFrame {
public:
GuiTitleBrowser(int32_t w, int32_t h, int32_t GameIndex) : GuiFrame(w, h) {}
GuiTitleBrowser(int32_t w, int32_t h, uint64_t GameIndex) : GuiFrame(w, h) {}
virtual ~GuiTitleBrowser() {}
virtual void setSelectedGame(int32_t idx) = 0;
virtual int32_t getSelectedGame(void) = 0;
virtual void setSelectedGame(uint64_t idx) = 0;
virtual uint64_t getSelectedGame(void) = 0;
sigslot::signal2<GuiTitleBrowser *, int32_t> gameLaunchClicked;
sigslot::signal2<GuiTitleBrowser *, int32_t> gameSelectionChanged;
virtual void OnGameTitleListUpdated(GameList * list) = 0;
virtual void OnGameTitleUpdated(gameInfo * info) = 0;
virtual void OnGameTitleAdded(gameInfo * info) = 0;
sigslot::signal2<GuiTitleBrowser *, uint64_t> gameLaunchClicked;
sigslot::signal2<GuiTitleBrowser *, uint64_t> gameSelectionChanged;
};

View File

@ -40,9 +40,15 @@ MainWindow::MainWindow(int32_t w, int32_t h)
pointerValid[i] = false;
}
SetupMainView();
gameList.titleListChanged.connect(this, &MainWindow::OnGameTitleListChanged);
gameList.titleUpdated.connect(this, &MainWindow::OnGameTitleUpdated);
gameList.titleAdded.connect(this, &MainWindow::OnGameTitleAdded);
AsyncExecutor::execute([&] {gameList.loadUnfiltered();});
}
MainWindow::~MainWindow() {
gameList.titleListChanged.disconnect(this);
while(!tvElements.empty()) {
delete tvElements[0];
remove(tvElements[0]);
@ -103,6 +109,31 @@ void MainWindow::process() {
}
}
void MainWindow::OnGameTitleListChanged(GameList * list) {
currentTvFrame->OnGameTitleListUpdated(list);
if(currentTvFrame != currentDrcFrame) {
currentDrcFrame->OnGameTitleListUpdated(list);
}
}
void MainWindow::OnGameTitleUpdated(gameInfo * info) {
currentTvFrame->OnGameTitleUpdated(info);
if(currentTvFrame != currentDrcFrame) {
currentDrcFrame->OnGameTitleUpdated(info);
}
}
void MainWindow::OnGameTitleAdded(gameInfo * info) {
DEBUG_FUNCTION_LINE("%08X\n", info);
if(info == NULL) {
return;
}
currentTvFrame->OnGameTitleAdded(info);
if(currentTvFrame != currentDrcFrame) {
currentDrcFrame->OnGameTitleAdded(info);
}
}
void MainWindow::update(GuiController *controller) {
//! dont read behind the initial elements in case one was added
//uint32_t tvSize = tvElements.size();
@ -179,6 +210,7 @@ void MainWindow::SetupMainView() {
currentTvFrame->setEffect(EFFECT_FADE, 10, 255);
currentTvFrame->setState(GuiElement::STATE_DISABLED);
currentTvFrame->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish);
appendTv(currentTvFrame);
currentDrcFrame = new GuiIconGrid(width, height,0);
@ -186,7 +218,6 @@ void MainWindow::SetupMainView() {
currentDrcFrame->setState(GuiElement::STATE_DISABLED);
currentDrcFrame->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish);
if(currentTvFrame != currentDrcFrame) {
currentDrcFrame->setEffect(EFFECT_FADE, 10, 255);
currentDrcFrame->setState(GuiElement::STATE_DISABLED);
@ -292,7 +323,7 @@ void MainWindow::OnSettingsButtonClicked(GuiElement *element){
}
void MainWindow::OnGameSelectionChange(GuiTitleBrowser *element, int32_t selectedIdx) {
void MainWindow::OnGameSelectionChange(GuiTitleBrowser *element, uint64_t selectedIdx) {
if(!currentDrcFrame || !currentTvFrame)
return;
@ -303,6 +334,6 @@ void MainWindow::OnGameSelectionChange(GuiTitleBrowser *element, int32_t selecte
}
}
void MainWindow::OnGameLaunch(GuiTitleBrowser *element, int32_t selectedIdx) {
void MainWindow::OnGameLaunch(GuiTitleBrowser *element, uint64_t selectedIdx) {
}

View File

@ -20,6 +20,7 @@
#include <vector>
#include <queue>
#include <gui/Gui.h>
#include "game/GameList.h"
#include "system/CMutex.h"
#include "gui/GuiTitleBrowser.h"
#include "MainDrcButtonsFrame.h"
@ -116,13 +117,17 @@ private:
void OnOpenEffectFinish(GuiElement *element);
void OnCloseEffectFinish(GuiElement *element);
void OnGameLaunch(GuiTitleBrowser *element, int32_t gameIdx);
void OnGameSelectionChange(GuiTitleBrowser *element, int32_t selectedIdx);
void OnGameLaunch(GuiTitleBrowser *element, uint64_t gameIdx);
void OnGameSelectionChange(GuiTitleBrowser *element, uint64_t selectedIdx);
void OnSettingsButtonClicked(GuiElement *element);
void OnLayoutSwitchClicked(GuiElement *element);
void OnLayoutSwitchEffectFinish(GuiElement *element);
void OnGameTitleListChanged(GameList * list);
void OnGameTitleUpdated(gameInfo * info);
void OnGameTitleAdded(gameInfo * info);
int32_t width, height;
std::vector<GuiElement *> drcElements;
std::vector<GuiElement *> tvElements;
@ -138,6 +143,8 @@ private:
GuiImage *pointerImg[4];
bool pointerValid[4];
GameList gameList;
CMutex guiMutex;
};

View File

@ -11,5 +11,10 @@ void AsyncExecutor::execute(std::function<void()> func) {
if(!instance) {
instance = new AsyncExecutor();
}
instance->elements.push_back(std::async(std::launch::async,func));
instance->elements.push(std::async(std::launch::async,func));
if(instance->elements.size() >= 25){
//DEBUG_FUNCTION_LINE("Wait on queue %d\n",instance->elements.size());
instance->elements.front().get();
instance->elements.pop();
}
}

View File

@ -1,6 +1,6 @@
#pragma once
#include <vector>
#include <queue>
#include <future>
#include <gui/GuiElement.h>
@ -22,5 +22,5 @@ private:
AsyncExecutor() {}
~AsyncExecutor() {}
std::vector<std::future<void>> elements;
std::queue<std::future<void>> elements;
};