The main loop runs while resizing or moving the window, on Windows.

Add love.event.setModalDrawCallback to override the function that's called during those resize/move operations.

Fixes #2148.
This commit is contained in:
Sasha Szpakowski
2025-02-08 16:32:30 -04:00
parent b20a0b81a4
commit 8900a72ac4
6 changed files with 186 additions and 20 deletions
+31
View File
@@ -40,11 +40,18 @@ Message::~Message()
Event::Event(const char *name)
: Module(M_EVENT, name)
, modalDrawData()
, defaultModalDrawData()
{
}
Event::~Event()
{
if (modalDrawData.cleanup != nullptr)
modalDrawData.cleanup(modalDrawData.context);
if (defaultModalDrawData.cleanup != nullptr)
defaultModalDrawData.cleanup(defaultModalDrawData.context);
}
void Event::push(Message *msg)
@@ -75,5 +82,29 @@ void Event::clear()
}
}
void Event::setModalDrawData(const ModalDrawData &data)
{
if (modalDrawData.cleanup != nullptr)
modalDrawData.cleanup(modalDrawData.context);
modalDrawData = data;
}
void Event::setDefaultModalDrawData(const ModalDrawData &data)
{
if (defaultModalDrawData.cleanup != nullptr)
defaultModalDrawData.cleanup(defaultModalDrawData.context);
defaultModalDrawData = data;
}
void Event::modalDraw()
{
if (modalDrawData.draw != nullptr)
modalDrawData.draw(modalDrawData.context);
else if (defaultModalDrawData.draw != nullptr)
defaultModalDrawData.draw(defaultModalDrawData.context);
}
} // event
} // love
+19
View File
@@ -55,6 +55,15 @@ class Event : public Module
{
public:
typedef void (*ModalDrawCallback)(void *context);
struct ModalDrawData
{
ModalDrawCallback draw;
ModalDrawCallback cleanup;
void *context;
};
virtual ~Event();
void push(Message *msg);
@@ -64,10 +73,20 @@ public:
virtual void pump(float waitTimeout = 0.0f) = 0;
virtual Message *wait() = 0;
void setModalDrawData(const ModalDrawData &data);
const ModalDrawData &getModalDrawData() const { return modalDrawData; }
void setDefaultModalDrawData(const ModalDrawData &data);
void modalDraw();
protected:
Event(const char *name);
ModalDrawData modalDrawData;
ModalDrawData defaultModalDrawData;
love::thread::MutexRef mutex;
std::queue<Message *> queue;
+59 -20
View File
@@ -79,19 +79,27 @@ static void normalizedToDPICoords(love::window::Window *window, double *x, doubl
// SDL's event watch callbacks trigger when the event is actually posted inside
// SDL, unlike with SDL_PollEvents. This is useful for some events which require
// handling inside the function which triggered them on some backends.
static bool SDLCALL watchAppEvents(void * /*udata*/, SDL_Event *event)
// Note: this may run on non-main threads on some platforms (Android?)
static bool SDLCALL watchAppEvents(void *udata, SDL_Event *event)
{
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
auto eventModule = (Event *)udata;
switch (event->type)
{
// On iOS, calling any OpenGL ES function after the function which triggers
// SDL_APP_DIDENTERBACKGROUND is called will kill the app, so we handle it
// with an event watch callback, which will be called inside that function.
case SDL_EVENT_DID_ENTER_BACKGROUND:
case SDL_EVENT_WILL_ENTER_FOREGROUND:
if (gfx)
gfx->setActive(event->type == SDL_EVENT_WILL_ENTER_FOREGROUND);
// On iOS, calling any OpenGL ES function after the function which triggers
// SDL_APP_DIDENTERBACKGROUND is called will kill the app, so we handle it
// with an event watch callback, which will be called inside that function.
{
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
if (gfx && SDL_IsMainThread())
gfx->setActive(event->type == SDL_EVENT_WILL_ENTER_FOREGROUND);
}
break;
case SDL_EVENT_WINDOW_EXPOSED:
if (eventModule != nullptr && SDL_IsMainThread())
eventModule->modalDraw();
break;
default:
break;
@@ -119,24 +127,55 @@ void Event::pump(float waitTimeout)
{
exceptionIfInRenderPass("love.event.pump");
int waitTimeoutMS = 0;
if (std::isinf(waitTimeout) || waitTimeout < 0.0f)
waitTimeoutMS = -1; // Wait forever.
else if (waitTimeout > 0.0f)
waitTimeoutMS = (int)std::min<int64>(LOVE_INT32_MAX, 1000LL * waitTimeout);
bool shouldPoll = false;
// Wait for the first event, if requested.
SDL_Event e;
if (SDL_WaitEventTimeout(&e, waitTimeoutMS))
if (insideEventPump)
{
StrongRef<Message> msg(convert(e), Acquire::NORETAIN);
if (msg)
push(msg);
// Don't pump if we're inside the event pump already, but do allow
// polling what's in the SDL queue.
shouldPoll = true;
}
else
{
int waitTimeoutMS = 0;
if (std::isinf(waitTimeout) || waitTimeout < 0.0f)
waitTimeoutMS = -1; // Wait forever.
else if (waitTimeout > 0.0f)
waitTimeoutMS = (int)std::min<int64>(LOVE_INT32_MAX, 1000LL * waitTimeout);
// Fetch any extra events that came in during WaitEvent.
try
{
// Wait for the first event, if requested.
SDL_Event e;
insideEventPump = true;
if (SDL_WaitEventTimeout(&e, waitTimeoutMS))
{
insideEventPump = false;
StrongRef<Message> msg(convert(e), Acquire::NORETAIN);
if (msg)
push(msg);
// Fetch any extra events that came in during WaitEvent.
shouldPoll = true;
}
else
{
insideEventPump = false;
}
}
catch (std::exception &)
{
insideEventPump = false;
throw;
}
}
if (shouldPoll)
{
SDL_Event e;
while (SDL_PollEvent(&e))
{
msg.set(convert(e), Acquire::NORETAIN);
StrongRef<Message> msg(convert(e), Acquire::NORETAIN);
if (msg)
push(msg);
}
+2
View File
@@ -70,6 +70,8 @@ private:
Message *convertJoystickEvent(const SDL_Event &e) const;
Message *convertWindowEvent(const SDL_Event &e, love::window::Window *win);
bool insideEventPump = false;
}; // Event
} // sdl
+68
View File
@@ -22,6 +22,7 @@
// LOVE
#include "common/runtime.h"
#include "common/Reference.h"
#include "sdl/Event.h"
#include <algorithm>
@@ -151,6 +152,71 @@ int w_restart(lua_State *L)
return 1;
}
static void drawCallback(void *context)
{
auto r = (Reference *)context;
lua_State *L = r->getPinnedL();
r->push(L);
int err = lua_pcall(L, 0, 0, 0);
// Unfortunately, this eats the stack trace, too bad.
if (err != 0)
throw love::Exception("Error in modal draw callback: %s", lua_tostring(L, -1));
}
static void cleanupCallback(void *context)
{
auto r = (Reference *)context;
delete r;
}
int w_setModalDrawCallback(lua_State *L)
{
Event::ModalDrawData data = {};
if (!lua_isnoneornil(L, 1))
{
luaL_checktype(L, 1, LUA_TFUNCTION);
// Save the callback function as a Reference.
lua_pushvalue(L, 1);
Reference *r = new Reference(L);
lua_pop(L, 1);
data.draw = drawCallback;
data.cleanup = cleanupCallback;
data.context = r;
}
luax_catchexcept(L, [&]() { instance()->setModalDrawData(data); });
return 0;
}
int w__setDefaultModalDrawCallback(lua_State *L)
{
Event::ModalDrawData data = {};
if (!lua_isnoneornil(L, 1))
{
luaL_checktype(L, 1, LUA_TFUNCTION);
// Save the callback function as a Reference.
lua_pushvalue(L, 1);
Reference *r = new Reference(L);
lua_pop(L, 1);
Event::ModalDrawData data = {};
data.draw = drawCallback;
data.cleanup = cleanupCallback;
data.context = r;
}
luax_catchexcept(L, [&]() { instance()->setDefaultModalDrawData(data); });
return 0;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
@@ -161,6 +227,8 @@ static const luaL_Reg functions[] =
{ "clear", w_clear },
{ "quit", w_quit },
{ "restart", w_restart },
{ "setModalDrawCallback", w_setModalDrawCallback },
{ "_setDefaultModalDrawCallback", w__setDefaultModalDrawCallback },
{ 0, 0 }
};
+7
View File
@@ -453,6 +453,7 @@ end
return function()
local func
local setModalDrawFunc = false
local inerror = false
local function deferErrhand(...)
@@ -481,14 +482,20 @@ return function()
result, main = xpcall(love.run, deferErrhand)
if result then
func = main
setModalDrawFunc = true
elseif inerror then -- Error in error handler
print("Error: " .. tostring(main))
end
end
func = earlyinit
local prevFunc = nil
while func do
if setModalDrawFunc and love.event and func ~= prevFunc then
prevFunc = func
love.event._setDefaultModalDrawCallback(func)
end
local _, retval, restartvalue = xpcall(func, deferErrhand)
if retval then return retval, restartvalue end
coroutine.yield()