Fix quit events being dropped if they're processed during a modal draw.

Forward quit data to the next full event processing cycle, and skip love.quit callbacks being called for a second time in that case.
This commit is contained in:
Sasha Szpakowski
2025-05-31 23:23:53 -03:00
parent 4770ca009d
commit 0111d70ead
5 changed files with 73 additions and 14 deletions
+18 -6
View File
@@ -55,10 +55,18 @@ Event::~Event()
}
void Event::push(Message *msg)
{
push(msg, false);
}
void Event::push(Message *msg, bool pushFront)
{
Lock lock(mutex);
msg->retain();
queue.push(msg);
if (pushFront)
queue.push_front(msg);
else
queue.push_back(msg);
}
bool Event::poll(Message *&msg)
@@ -67,7 +75,7 @@ bool Event::poll(Message *&msg)
if (queue.empty())
return false;
msg = queue.front();
queue.pop();
queue.pop_front();
return true;
}
@@ -76,9 +84,8 @@ void Event::clear()
Lock lock(mutex);
while (!queue.empty())
{
// std::queue::pop will remove the first (front) element.
queue.front()->release();
queue.pop();
queue.pop_front();
}
}
@@ -104,12 +111,17 @@ void Event::modalDraw()
if (!deferredExceptionMessage.empty())
return;
// Also skip the draw if a previous one generated a return value that
// needs to be passed down as a quit event.
if (deferredReturnValues[0].getType() != Variant::NIL)
return;
try
{
if (modalDrawData.draw != nullptr)
modalDrawData.draw(modalDrawData.context);
modalDrawData.draw(modalDrawData.context, &deferredReturnValues[0], &deferredReturnValues[1]);
else if (defaultModalDrawData.draw != nullptr)
defaultModalDrawData.draw(defaultModalDrawData.context);
defaultModalDrawData.draw(defaultModalDrawData.context, &deferredReturnValues[0], &deferredReturnValues[1]);
}
catch (std::exception &e)
{
+8 -4
View File
@@ -31,7 +31,7 @@
#include "thread/threads.h"
// C++
#include <queue>
#include <deque>
#include <vector>
namespace love
@@ -55,12 +55,13 @@ class Event : public Module
{
public:
typedef void (*ModalDrawCallback)(void *context);
typedef void (*ModalDrawCallback)(void *context, Variant *returnValue0, Variant *returnValue1);
typedef void (*ModalCleanupCallback)(void *context);
struct ModalDrawData
{
ModalDrawCallback draw;
ModalDrawCallback cleanup;
ModalCleanupCallback cleanup;
void *context;
};
@@ -84,12 +85,15 @@ protected:
Event(const char *name);
void push(Message *msg, bool pushFront);
ModalDrawData modalDrawData;
ModalDrawData defaultModalDrawData;
std::string deferredExceptionMessage;
Variant deferredReturnValues[2];
love::thread::MutexRef mutex;
std::queue<Message *> queue;
std::deque<Message *> queue;
}; // Event
+17
View File
@@ -174,8 +174,25 @@ void Event::pump(float waitTimeout)
{
std::string exceptionstr = deferredExceptionMessage;
deferredExceptionMessage.clear();
deferredReturnValues[0] = Variant();
deferredReturnValues[1] = Variant();
throw love::Exception("%s", exceptionstr.c_str());
}
if (deferredReturnValues[0].getType() != Variant::NIL)
{
// Third arg being true will tell love.run to skip the love.quit callback,
// since the original modal draw function already processed that.
std::vector<Variant> args = {deferredReturnValues[0], deferredReturnValues[1], Variant(true)};
StrongRef<Message> msg(new Message("quit", args), Acquire::NORETAIN);
// Push to the front of queue so it's dealt with before any other event.
push(msg, true);
deferredReturnValues[0] = Variant();
deferredReturnValues[1] = Variant();
}
}
if (shouldPoll)
+29 -3
View File
@@ -152,18 +152,44 @@ int w_restart(lua_State *L)
return 1;
}
static void drawCallback(void *context)
struct DrawCallbackData
{
Variant returnValues[2];
Reference *r;
};
static int drawCallbackInner(lua_State *L)
{
auto data = (DrawCallbackData *)lua_touserdata(L, 1);
data->r->push(L);
lua_call(L, 0, 2);
data->returnValues[0] = luax_checkvariant(L, -2, false);
data->returnValues[1] = luax_checkvariant(L, -1, false);
lua_pop(L, 2);
return 0;
}
static void drawCallback(void *context, Variant *returnVal0, Variant *returnVal1)
{
auto r = (Reference *)context;
lua_State *L = r->getPinnedL();
r->push(L);
DrawCallbackData data = {};
data.r = r;
int err = lua_pcall(L, 0, 0, 0);
// pcall into C code to catch errors from checkvariant as well as the lua_call.
int err = lua_cpcall(L, drawCallbackInner, &data);
// Unfortunately, this eats the stack trace, too bad.
if (err != 0)
throw love::Exception("Error in modal draw callback: %s", lua_tostring(L, -1));
*returnVal0 = data.returnValues[0];
*returnVal1 = data.returnValues[1];
}
static void cleanupCallback(void *context)
+1 -1
View File
@@ -175,7 +175,7 @@ function love.run()
love.event.pump()
for name, a,b,c,d,e,f,g,h in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
if c or not love.quit or not love.quit() then
return a or 0, b
end
end