mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user