Browse Source

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.
Sasha Szpakowski 6 months ago
parent
commit
8900a72ac4

+ 31 - 0
src/modules/event/Event.cpp

@@ -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 - 0
src/modules/event/Event.h

@@ -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
src/modules/event/sdl/Event.cpp

@@ -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);
+
+		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.
+				// 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 - 0
src/modules/event/sdl/Event.h

@@ -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 - 0
src/modules/event/wrap_Event.cpp

@@ -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 - 0
src/modules/love/boot.lua

@@ -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()