From f3dd769e982cfbc3a98d6bf6918756a4c8cdd709 Mon Sep 17 00:00:00 2001 From: fysx Date: Thu, 13 Aug 2015 11:12:17 +0200 Subject: [PATCH] Using latest love-android branch (9a508a54700d) --- .gitignore | 1 + jni/love/src/common/Reference.cpp | 40 ++-- jni/love/src/common/Reference.h | 33 +-- jni/love/src/common/Variant.cpp | 2 +- jni/love/src/common/runtime.cpp | 57 ++++- jni/love/src/common/runtime.h | 19 +- jni/love/src/libraries/glad/glad.cpp | 14 +- jni/love/src/love.cpp | 54 ++--- jni/love/src/modules/audio/openal/Audio.cpp | 4 +- jni/love/src/modules/audio/openal/Audio.h | 2 +- jni/love/src/modules/audio/openal/Pool.cpp | 6 - jni/love/src/modules/audio/openal/Pool.h | 2 +- jni/love/src/modules/event/Event.cpp | 6 - jni/love/src/modules/event/Event.h | 3 +- jni/love/src/modules/event/sdl/Event.cpp | 24 +- .../modules/filesystem/physfs/Filesystem.cpp | 84 ++++--- .../src/modules/font/BMFontRasterizer.cpp | 4 +- jni/love/src/modules/font/BMFontRasterizer.h | 2 +- .../src/modules/graphics/opengl/Graphics.cpp | 77 +++++-- .../src/modules/graphics/opengl/Graphics.h | 6 +- .../src/modules/graphics/opengl/Image.cpp | 208 ++++++++++++------ jni/love/src/modules/graphics/opengl/Image.h | 21 +- .../src/modules/graphics/opengl/OpenGL.cpp | 17 +- .../modules/graphics/opengl/wrap_Canvas.cpp | 2 +- .../modules/graphics/opengl/wrap_Graphics.cpp | 80 ++++--- .../modules/graphics/opengl/wrap_Image.cpp | 19 +- .../graphics/opengl/wrap_SpriteBatch.cpp | 56 ++--- .../src/modules/image/CompressedImageData.h | 4 +- jni/love/src/modules/image/ImageData.cpp | 2 - jni/love/src/modules/image/ImageData.h | 6 +- .../src/modules/image/magpie/ImageData.cpp | 13 +- jni/love/src/modules/image/magpie/ImageData.h | 3 +- jni/love/src/modules/image/wrap_ImageData.cpp | 44 ++-- jni/love/src/modules/keyboard/Keyboard.cpp | 1 - jni/love/src/modules/keyboard/Keyboard.h | 1 - .../src/modules/keyboard/sdl/Keyboard.cpp | 3 +- jni/love/src/modules/love/love.cpp | 4 +- jni/love/src/modules/mouse/sdl/Mouse.cpp | 10 +- jni/love/src/modules/physics/box2d/Body.cpp | 10 +- jni/love/src/modules/physics/box2d/Body.h | 2 +- .../src/modules/physics/box2d/Fixture.cpp | 10 +- jni/love/src/modules/physics/box2d/Fixture.h | 2 +- jni/love/src/modules/physics/box2d/Joint.cpp | 10 +- jni/love/src/modules/physics/box2d/Joint.h | 2 +- jni/love/src/modules/physics/box2d/World.cpp | 75 ++++--- jni/love/src/modules/physics/box2d/World.h | 23 +- .../src/modules/physics/box2d/wrap_World.cpp | 2 + .../modules/sound/lullaby/ModPlugDecoder.h | 6 +- .../modules/sound/lullaby/VorbisDecoder.cpp | 6 +- jni/love/src/modules/system/System.cpp | 9 +- jni/love/src/modules/system/System.h | 1 - jni/love/src/modules/system/wrap_System.cpp | 2 +- jni/love/src/modules/thread/LuaThread.cpp | 6 +- jni/love/src/modules/thread/threads.cpp | 15 ++ jni/love/src/modules/thread/threads.h | 12 + jni/love/src/modules/window/Window.cpp | 4 - jni/love/src/modules/window/Window.h | 11 +- jni/love/src/modules/window/sdl/Window.cpp | 110 ++++----- jni/love/src/modules/window/sdl/Window.h | 8 +- jni/love/src/modules/window/wrap_Window.cpp | 27 ++- jni/love/src/modules/window/wrap_Window.h | 3 +- jni/love/src/scripts/boot.lua | 2 +- jni/love/src/scripts/boot.lua.h | 2 +- 63 files changed, 738 insertions(+), 556 deletions(-) diff --git a/.gitignore b/.gitignore index 6809e077..bf449d17 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ local.properties *.so *.a *.orig +*.rej !jni/LuaJIT-2.0.1/android/armeabi/libluajit.a !jni/LuaJIT-2.0.1/android/armeabi-v7a/libluajit.a diff --git a/jni/love/src/common/Reference.cpp b/jni/love/src/common/Reference.cpp index e077c3d6..a54ef38e 100644 --- a/jni/love/src/common/Reference.cpp +++ b/jni/love/src/common/Reference.cpp @@ -26,13 +26,13 @@ namespace love const char REFERENCE_TABLE_NAME[] = "love-references"; Reference::Reference() - : L(nullptr) + : pinnedL(nullptr) , idx(LUA_REFNIL) { } Reference::Reference(lua_State *L) - : L(L) + : pinnedL(nullptr) , idx(LUA_REFNIL) { ref(L); @@ -46,7 +46,7 @@ Reference::~Reference() void Reference::ref(lua_State *L) { unref(); // Just to be safe. - this->L = L; + pinnedL = luax_getpinnedthread(L); luax_insist(L, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME); lua_insert(L, -2); // Move reference table behind value. idx = luaL_ref(L, -2); @@ -57,38 +57,26 @@ void Reference::unref() { if (idx != LUA_REFNIL) { - luax_insist(L, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME); - luaL_unref(L, -1, idx); - lua_pop(L, 1); + // We use a pinned thread/coroutine for the Lua state because we know it + // hasn't been garbage collected and is valid, as long as the whole lua + // state is still open. + luax_insist(pinnedL, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME); + luaL_unref(pinnedL, -1, idx); + lua_pop(pinnedL, 1); idx = LUA_REFNIL; } } -void Reference::push(lua_State *newL) +void Reference::push(lua_State *L) { if (idx != LUA_REFNIL) { - luax_insist(newL, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME); - lua_rawgeti(newL, -1, idx); - lua_remove(newL, -2); + luax_insist(L, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME); + lua_rawgeti(L, -1, idx); + lua_remove(L, -2); } else - lua_pushnil(newL); -} - -void Reference::push() -{ - push(L); -} - -lua_State *Reference::getL() const -{ - return L; -} - -void Reference::setL(lua_State *newL) -{ - L = newL; + lua_pushnil(L); } } // love diff --git a/jni/love/src/common/Reference.h b/jni/love/src/common/Reference.h index 29506953..3c1e9079 100644 --- a/jni/love/src/common/Reference.h +++ b/jni/love/src/common/Reference.h @@ -64,36 +64,17 @@ public: void unref(); /** - * Pushes the referred value onto the stack of a different coroutine - * in the same main Lua state. - * THIS SHOULD NOT BE USED FOR DIFFERENT LUA STATES (created with - * luaL_newstate)! Only with different coroutines! + * Pushes the referred value onto the stack of the specified Lua coroutine. + * NOTE: The coroutine *must* belong to the same Lua state that was used for + * Reference::ref. **/ - void push(lua_State *newL); - - /** - * Pushes the referred value onto the stack. - **/ - void push(); - - /** - * Gets the Lua state associated with this - * reference. - **/ - lua_State *getL() const; - - /** - * Associates a new Lua state with this reference. - * THIS IS DANGEROUS! It is only designed to be - * used with different coroutines from the same - * main Lua state! - **/ - void setL(lua_State *newL); + void push(lua_State *L); private: - // The Lua state in which the reference resides. - lua_State *L; + // A pinned coroutine (probably the main thread) belonging to the Lua state + // in which the reference resides. + lua_State *pinnedL; // Index to the Lua reference. int idx; diff --git a/jni/love/src/common/Variant.cpp b/jni/love/src/common/Variant.cpp index 743a5b62..24305d2a 100644 --- a/jni/love/src/common/Variant.cpp +++ b/jni/love/src/common/Variant.cpp @@ -31,7 +31,7 @@ static love::Type extractudatatype(lua_State *L, int idx) Type t = INVALID_ID; if (!lua_isuserdata(L, idx)) return t; - if (luaL_getmetafield(L, idx, "__tostring") == 0) + if (luaL_getmetafield(L, idx, "type") == 0) return t; lua_pushvalue(L, idx); int result = lua_pcall(L, 1, 1, 0); diff --git a/jni/love/src/common/runtime.cpp b/jni/love/src/common/runtime.cpp index e7ba8bd9..728f4c30 100644 --- a/jni/love/src/common/runtime.cpp +++ b/jni/love/src/common/runtime.cpp @@ -47,6 +47,14 @@ static int w__gc(lua_State *L) } static int w__tostring(lua_State *L) +{ + Proxy *p = (Proxy *) lua_touserdata(L, 1); + const char *typname = lua_tostring(L, lua_upvalueindex(1)); + lua_pushfstring(L, "%s: %p", typname, p->object); + return 1; +} + +static int w__type(lua_State *L) { lua_pushvalue(L, lua_upvalueindex(1)); return 1; @@ -83,7 +91,7 @@ Reference *luax_refif(lua_State *L, int type) void luax_printstack(lua_State *L) { - for (int i = 1; i<=lua_gettop(L); i++) + for (int i = 1; i <= lua_gettop(L); i++) std::cout << i << " - " << luaL_typename(L, i) << std::endl; } @@ -313,9 +321,9 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pu lua_pushcclosure(L, w__tostring, 1); lua_setfield(L, -2, "__tostring"); - // Add tostring to as type() as well. + // Add type lua_pushstring(L, tname); - lua_pushcclosure(L, w__tostring, 1); + lua_pushcclosure(L, w__type, 1); lua_setfield(L, -2, "type"); // Add typeOf @@ -590,8 +598,6 @@ int luax_insistregistry(lua_State *L, Registry r) { switch (r) { - case REGISTRY_GC: - return luax_insistlove(L, "_gc"); case REGISTRY_MODULES: return luax_insistlove(L, "_modules"); case REGISTRY_OBJECTS: @@ -605,8 +611,6 @@ int luax_getregistry(lua_State *L, Registry r) { switch (r) { - case REGISTRY_GC: - return luax_getlove(L, "_gc"); case REGISTRY_MODULES: return luax_getlove(L, "_modules"); case REGISTRY_OBJECTS: @@ -617,21 +621,54 @@ int luax_getregistry(lua_State *L, Registry r) } } +static const char *MAIN_THREAD_KEY = "_love_mainthread"; + +lua_State *luax_insistpinnedthread(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, MAIN_THREAD_KEY); + + if (lua_isnoneornil(L, -1)) + { + lua_pop(L, 1); + + // lua_pushthread returns 1 if it's actually the main thread, but we + // can't actually get the real main thread if lua_pushthread doesn't + // return it (in Lua 5.1 at least), so we ignore that for now... + // We do store a strong reference to the current thread/coroutine in + // the registry, however. + lua_pushthread(L); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, MAIN_THREAD_KEY); + } + + lua_State *thread = lua_tothread(L, -1); + lua_pop(L, 1); + return thread; +} + +lua_State *luax_getpinnedthread(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, MAIN_THREAD_KEY); + lua_State *thread = lua_tothread(L, -1); + lua_pop(L, 1); + return thread; +} + extern "C" int luax_typerror(lua_State *L, int narg, const char *tname) { int argtype = lua_type(L, narg); const char *argtname = 0; // We want to use the love type name for userdata, if possible. - if (argtype == LUA_TUSERDATA && luaL_getmetafield(L, narg, "__tostring") != 0) + if (argtype == LUA_TUSERDATA && luaL_getmetafield(L, narg, "type") != 0) { lua_pushvalue(L, narg); if (lua_pcall(L, 1, 1, 0) == 0 && lua_type(L, -1) == LUA_TSTRING) { argtname = lua_tostring(L, -1); - // Non-love userdata might have a tostring metamethod which doesn't - // describe its type, so we only use __tostring for love types. + // Non-love userdata might have a type metamethod which doesn't + // describe its type properly, so we only use it for love types. love::Type t; if (!love::getType(argtname, t)) argtname = 0; diff --git a/jni/love/src/common/runtime.h b/jni/love/src/common/runtime.h index f0facdee..6d7f8cde 100644 --- a/jni/love/src/common/runtime.h +++ b/jni/love/src/common/runtime.h @@ -49,7 +49,6 @@ class Reference; **/ enum Registry { - REGISTRY_GC, REGISTRY_MODULES, REGISTRY_OBJECTS }; @@ -390,6 +389,24 @@ int luax_insistregistry(lua_State *L, Registry r); **/ int luax_getregistry(lua_State *L, Registry r); +/** + * Gets (and pins if needed) a "pinned" Lua thread (coroutine) in the specified + * Lua state. This will usually be the main Lua thread, unless the first call + * to this function for a specific Lua state is made from within a coroutine. + * NOTE: This does not push anything to the stack. + **/ +lua_State *luax_insistpinnedthread(lua_State *L); + +/** + * Gets a "pinned" Lua thread (coroutine) in the specified Lua state. This will + * usually be the main Lua thread. This can be used to access global variables + * in a specific Lua state without needing another alive lua_State value. + * PRECONDITION: luax_insistpinnedthread must have been called on a lua_State + * value corresponding to the Lua state which will be used with this function. + * NOTE: This does not push anything to the stack. + **/ +lua_State *luax_getpinnedthread(lua_State *L); + extern "C" { // Also called from luasocket int luax_typerror(lua_State *L, int narg, const char *tname); } diff --git a/jni/love/src/libraries/glad/glad.cpp b/jni/love/src/libraries/glad/glad.cpp index ac4ff046..fffa3b89 100644 --- a/jni/love/src/libraries/glad/glad.cpp +++ b/jni/love/src/libraries/glad/glad.cpp @@ -1,11 +1,9 @@ #include #include "glad.hpp" -#include - -#define GLAD_USE_SDL namespace glad { + #ifdef GLAD_USE_SDL #include #if !SDL_VERSION_ATLEAST(2,0,0) @@ -15,17 +13,9 @@ namespace glad { #include #endif -void* LoaderDlsymOrSDLGetProc (const char* name) { - void* proc = dlsym(RTLD_DEFAULT, name); - if (!proc) { - proc = SDL_GL_GetProcAddress (name); - } - return proc; -} - bool gladLoadGL(void) { #ifdef GLAD_USE_SDL - return gladLoadGLLoader(LoaderDlsymOrSDLGetProc); + return gladLoadGLLoader(SDL_GL_GetProcAddress); #else // generic gladLoadGL is not implemented, use gladLoadGLLoader or define GLAD_USE_SDL assert(0); diff --git a/jni/love/src/love.cpp b/jni/love/src/love.cpp index f7701f60..30619e8e 100644 --- a/jni/love/src/love.cpp +++ b/jni/love/src/love.cpp @@ -164,7 +164,9 @@ static int love_preload(lua_State *L, lua_CFunction f, const char *name) return 0; } -static int l_print_sdl_log (lua_State *L) { +#ifdef LOVE_ANDROID +static int l_print_sdl_log(lua_State *L) +{ int nargs = lua_gettop(L); if (nargs == 0) @@ -172,44 +174,42 @@ static int l_print_sdl_log (lua_State *L) { std::string out_string = ""; - for (int i = 1; i <= nargs; i++) { - int type = lua_type (L, i); + for (int i = 1; i <= nargs; i++) + { + int type = lua_type(L, i); char pointer_buf[16]; - switch (type) { - case LUA_TNUMBER: - case LUA_TSTRING: - out_string += lua_tostring (L, i); - break; - case LUA_TNIL: - out_string += "nil"; - break; - case LUA_TBOOLEAN: - out_string += lua_toboolean (L, i) ? "true" : "false"; - break; - default: - out_string += lua_typename (L, lua_type(L, i)); - sprintf (pointer_buf, ": 0x%x", (unsigned int) lua_topointer (L, i)); - out_string += pointer_buf; - break; + switch (type) + { + case LUA_TNUMBER: + case LUA_TSTRING: + out_string += lua_tostring(L, i); + break; + case LUA_TNIL: + out_string += "nil"; + break; + case LUA_TBOOLEAN: + out_string += lua_toboolean(L, i) ? "true" : "false"; + break; + default: + out_string += lua_typename(L, lua_type(L, i)); + sprintf(pointer_buf, ": 0x%lx", (size_t) lua_topointer(L, i)); + out_string += pointer_buf; + break; } - if (i != nargs - 1) { + if (i != nargs - 1) out_string += "\t"; - } } - SDL_Log ("[LOVE] %s", out_string.c_str()); + SDL_Log("[LOVE] %s", out_string.c_str()); return 0; } +#endif int main(int argc, char **argv) { int retval = 0; -#ifdef LOVE_ANDROID - SDL_SetHint("LOVE_GRAPHICS_USE_OPENGLES", "1"); -#endif - #ifdef LOVE_IOS int orig_argc = argc; char **orig_argv = argv; @@ -262,7 +262,7 @@ int main(int argc, char **argv) luaL_openlibs(L); #ifdef LOVE_ANDROID - lua_register (L, "print", l_print_sdl_log); + lua_register(L, "print", l_print_sdl_log); #endif // Add love to package.preload for easy requiring. diff --git a/jni/love/src/modules/audio/openal/Audio.cpp b/jni/love/src/modules/audio/openal/Audio.cpp index 4d6fefad..470f668d 100644 --- a/jni/love/src/modules/audio/openal/Audio.cpp +++ b/jni/love/src/modules/audio/openal/Audio.cpp @@ -36,13 +36,11 @@ Audio::PoolThread::PoolThread(Pool *pool) : pool(pool) , finish(false) { - mutex = thread::newMutex(); threadName = "AudioPool"; } Audio::PoolThread::~PoolThread() { - delete mutex; } @@ -193,7 +191,7 @@ void Audio::pause() { pool->pause(); #ifdef LOVE_ANDROID - alcDevicePauseSOFT (device); + alcDevicePauseSOFT(device); #endif } diff --git a/jni/love/src/modules/audio/openal/Audio.h b/jni/love/src/modules/audio/openal/Audio.h index 1540acdc..43211c80 100644 --- a/jni/love/src/modules/audio/openal/Audio.h +++ b/jni/love/src/modules/audio/openal/Audio.h @@ -127,7 +127,7 @@ private: volatile bool finish; // finish lock - thread::Mutex *mutex; + love::thread::MutexRef mutex; public: PoolThread(Pool *pool); diff --git a/jni/love/src/modules/audio/openal/Pool.cpp b/jni/love/src/modules/audio/openal/Pool.cpp index bec0d8c1..a1018f55 100644 --- a/jni/love/src/modules/audio/openal/Pool.cpp +++ b/jni/love/src/modules/audio/openal/Pool.cpp @@ -32,7 +32,6 @@ namespace openal Pool::Pool() : sources() , totalSources(0) - , mutex(nullptr) { // Clear errors. alGetError(); @@ -53,9 +52,6 @@ Pool::Pool() if (totalSources < 4) throw love::Exception("Could not generate sources."); - // Create the mutex. - mutex = thread::newMutex(); - #ifdef AL_SOFT_direct_channels ALboolean hasext = alIsExtensionPresent("AL_SOFT_direct_channels"); #endif @@ -79,8 +75,6 @@ Pool::~Pool() { stop(); - delete mutex; - // Free all sources. alDeleteSources(totalSources, sources); } diff --git a/jni/love/src/modules/audio/openal/Pool.h b/jni/love/src/modules/audio/openal/Pool.h index eccf59c7..e875b4f5 100644 --- a/jni/love/src/modules/audio/openal/Pool.h +++ b/jni/love/src/modules/audio/openal/Pool.h @@ -124,7 +124,7 @@ private: // Only one thread can access this object at the same time. This mutex will // make sure of that. - thread::Mutex *mutex; + love::thread::MutexRef mutex; }; // Pool diff --git a/jni/love/src/modules/event/Event.cpp b/jni/love/src/modules/event/Event.cpp index 518f1f82..92d416da 100644 --- a/jni/love/src/modules/event/Event.cpp +++ b/jni/love/src/modules/event/Event.cpp @@ -81,14 +81,8 @@ Message *Message::fromLua(lua_State *L, int n) return new Message(name, vargs); } -Event::Event() -{ - mutex = thread::newMutex(); -} - Event::~Event() { - delete mutex; } void Event::push(Message *msg) diff --git a/jni/love/src/modules/event/Event.h b/jni/love/src/modules/event/Event.h index 47b539b7..35073d4b 100644 --- a/jni/love/src/modules/event/Event.h +++ b/jni/love/src/modules/event/Event.h @@ -59,7 +59,6 @@ private: class Event : public Module { public: - Event(); virtual ~Event(); // Implements Module. @@ -73,7 +72,7 @@ public: virtual Message *wait() = 0; protected: - thread::Mutex *mutex; + love::thread::MutexRef mutex; std::queue queue; }; // Event diff --git a/jni/love/src/modules/event/sdl/Event.cpp b/jni/love/src/modules/event/sdl/Event.cpp index b7d54c52..c7decc61 100644 --- a/jni/love/src/modules/event/sdl/Event.cpp +++ b/jni/love/src/modules/event/sdl/Event.cpp @@ -45,7 +45,7 @@ namespace sdl // we want them in pixel coordinates (may be different with high-DPI enabled.) static void windowToPixelCoords(double *x, double *y) { - window::Window *window = Module::getInstance(Module::M_WINDOW); + auto window = Module::getInstance(Module::M_WINDOW); if (window) window->windowToPixelCoords(x, y); } @@ -53,7 +53,7 @@ static void windowToPixelCoords(double *x, double *y) #ifndef LOVE_MACOSX static void normalizedToPixelCoords(double *x, double *y) { - window::Window *window = Module::getInstance(Module::M_WINDOW); + auto window = Module::getInstance(Module::M_WINDOW); int w = 1, h = 1; if (window) @@ -71,7 +71,7 @@ static void normalizedToPixelCoords(double *x, double *y) // handling inside the function which triggered them on some backends. static int SDLCALL watchAppEvents(void * /*udata*/, SDL_Event *event) { - graphics::Graphics *gfx = Module::getInstance(Module::M_GRAPHICS); + auto gfx = Module::getInstance(Module::M_GRAPHICS); switch (event->type) { @@ -153,7 +153,6 @@ Message *Event::convert(const SDL_Event &e) const std::vector> vargs; vargs.reserve(4); - love::keyboard::Keyboard *kb = nullptr; love::filesystem::Filesystem *filesystem = nullptr; love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_UNKNOWN; @@ -177,7 +176,7 @@ Message *Event::convert(const SDL_Event &e) const case SDL_KEYDOWN: if (e.key.repeat) { - kb = Module::getInstance(Module::M_KEYBOARD); + auto kb = Module::getInstance(Module::M_KEYBOARD); if (kb && !kb->hasKeyRepeat()) break; } @@ -395,7 +394,7 @@ Message *Event::convert(const SDL_Event &e) const Message *Event::convertJoystickEvent(const SDL_Event &e) const { - joystick::JoystickModule *joymodule = Module::getInstance(Module::M_JOYSTICK); + auto joymodule = Module::getInstance(Module::M_JOYSTICK); if (!joymodule) return nullptr; @@ -511,24 +510,24 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const msg = new Message("joystickremoved", vargs); } break; - default: - break; #ifdef LOVE_ANDROID - case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_MINIMIZED: { - audio::Audio *audio = Module::getInstance(Module::M_AUDIO); + auto audio = Module::getInstance(Module::M_AUDIO); if (audio) audio->pause(); } break; - case SDL_WINDOWEVENT_RESTORED: + case SDL_WINDOWEVENT_RESTORED: { - audio::Audio *audio = Module::getInstance(Module::M_AUDIO); + auto audio = Module::getInstance(Module::M_AUDIO); if (audio) audio->resume(); } break; #endif + default: + break; } // We gave +1 refs to the StrongRef list, so we should release them. @@ -754,7 +753,6 @@ std::map Event::createKeyMap() k[SDLK_EXECUTE] = Keyboard::KEY_EXECUTE; k[SDLK_HELP] = Keyboard::KEY_HELP; k[SDLK_MENU] = Keyboard::KEY_MENU; - k[SDLK_AC_SEARCH] = Keyboard::KEY_SEARCH; k[SDLK_SELECT] = Keyboard::KEY_SELECT; k[SDLK_STOP] = Keyboard::KEY_STOP; k[SDLK_AGAIN] = Keyboard::KEY_AGAIN; diff --git a/jni/love/src/modules/filesystem/physfs/Filesystem.cpp b/jni/love/src/modules/filesystem/physfs/Filesystem.cpp index 8b868d6e..d1f225da 100644 --- a/jni/love/src/modules/filesystem/physfs/Filesystem.cpp +++ b/jni/love/src/modules/filesystem/physfs/Filesystem.cpp @@ -50,10 +50,10 @@ # include "common/iOS.h" #endif -#include "SDL.h" #include #ifdef LOVE_ANDROID +#include #include "common/android.h" #endif @@ -180,22 +180,22 @@ bool Filesystem::setIdentity(const char *ident, bool appendToPath) save_path_full = std::string(SDL_AndroidGetInternalStoragePath()) + std::string("/save/") + save_identity; - if (love::android::directoryExists (save_path_full.c_str())) { - SDL_Log ("dir exists"); - } else { - SDL_Log ("does not exist"); - } + if (love::android::directoryExists(save_path_full.c_str())) + SDL_Log("dir exists"); + else + SDL_Log("does not exist"); - if (!love::android::directoryExists (save_path_full.c_str())) { - if (!love::android::mkdir (save_path_full.c_str())) { - SDL_Log ("Error: Could not create save directory %s!", save_path_full.c_str()); - } else { - SDL_Log ("Save directory %s successfuly created!", save_path_full.c_str()); - } - } else { - SDL_Log ("Save directory %s exists!", save_path_full.c_str()); + if (!love::android::directoryExists(save_path_full.c_str())) + { + if (!love::android::mkdir(save_path_full.c_str())) + SDL_Log("Error: Could not create save directory %s!", save_path_full.c_str()); + else + SDL_Log("Save directory %s successfuly created!", save_path_full.c_str()); } + else + SDL_Log("Save directory %s exists!", save_path_full.c_str()); #endif + // We now have something like: // save_identity: game // save_path_relative: ./LOVE/game @@ -235,52 +235,61 @@ bool Filesystem::setSource(const char *source) std::string new_search_path = source; #ifdef LOVE_ANDROID - if (!love::android::createStorageDirectories ()) { - SDL_Log ("Error creating storage directories!"); - } + if (!love::android::createStorageDirectories ()) + SDL_Log("Error creating storage directories!"); char* game_archive_ptr = NULL; size_t game_archive_size = 0; bool archive_loaded = false; // try to load the game that was sent to LÖVE via a Intent - archive_loaded = love::android::loadGameArchiveToMemory (love::android::getSelectedGameFile(), &game_archive_ptr, &game_archive_size); + archive_loaded = love::android::loadGameArchiveToMemory(love::android::getSelectedGameFile(), &game_archive_ptr, &game_archive_size); - if (!archive_loaded) { + if (!archive_loaded) + { // try to load the game in the assets/ folder - archive_loaded = love::android::loadGameArchiveToMemory ("game.love", &game_archive_ptr, &game_archive_size); + archive_loaded = love::android::loadGameArchiveToMemory("game.love", &game_archive_ptr, &game_archive_size); } - if (archive_loaded) { - if (PHYSFS_mountMemory (game_archive_ptr, game_archive_size, love::android::freeGameArchiveMemory, "archive.zip", "/", 0)) { - SDL_Log ("Mounting of in-memory game archive successful!"); - } else { - SDL_Log ("Mounting of in-memory game archive failed!"); - love::android::freeGameArchiveMemory (game_archive_ptr); + if (archive_loaded) + { + if (PHYSFS_mountMemory(game_archive_ptr, game_archive_size, love::android::freeGameArchiveMemory, "archive.zip", "/", 0)) + SDL_Log("Mounting of in-memory game archive successful!"); + else + { + SDL_Log("Mounting of in-memory game archive failed!"); + love::android::freeGameArchiveMemory(game_archive_ptr); return false; } - } else { + } + else + { // try to load the game in the directory that was sent to LÖVE via an // Intent ... std::string game_path = std::string(love::android::getSelectedGameFile()); - if (game_path == "") { + if (game_path == "") + { // ... or fall back to the game at /sdcard/lovegame game_path = "/sdcard/lovegame/"; } SDL_RWops *sdcard_main = SDL_RWFromFile(std::string(game_path + "main.lua").c_str(), "rb"); - if (sdcard_main) { - SDL_Log ("using game from %s", game_path.c_str()); + if (sdcard_main) + { + SDL_Log("using game from %s", game_path.c_str()); new_search_path = game_path; sdcard_main->close(sdcard_main); - if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1)) { - SDL_Log ("mounting of %s failed", new_search_path.c_str()); + if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1)) + { + SDL_Log("mounting of %s failed", new_search_path.c_str()); return false; } - } else { + } + else + { // Neither assets/game.love or /sdcard/lovegame was mounted // sucessfully, therefore simply fail. return false; @@ -288,8 +297,7 @@ bool Filesystem::setSource(const char *source) } #else // Add the directory. - if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1)) { - SDL_Log ("mounting of %s failed", new_search_path.c_str()); + if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1)) return false; #endif @@ -501,7 +509,13 @@ const char *Filesystem::getWorkingDirectory() std::string Filesystem::getUserDirectory() { +#ifdef LOVE_IOS + // PHYSFS_getUserDir doesn't give exactly the path we want on iOS. + static std::string userDir = normalize(love::ios::getHomeDirectory()); +#else static std::string userDir = normalize(PHYSFS_getUserDir()); +#endif + return userDir; } diff --git a/jni/love/src/modules/font/BMFontRasterizer.cpp b/jni/love/src/modules/font/BMFontRasterizer.cpp index 70b3db56..b6df24b0 100644 --- a/jni/love/src/modules/font/BMFontRasterizer.cpp +++ b/jni/love/src/modules/font/BMFontRasterizer.cpp @@ -190,8 +190,8 @@ void BMFontRasterizer::parseConfig(const std::string &configtext) { using namespace love::filesystem; - Filesystem *filesystem = Module::getInstance(Module::M_FILESYSTEM); - image::Image *imagemodule = Module::getInstance(Module::M_IMAGE); + auto filesystem = Module::getInstance(Module::M_FILESYSTEM); + auto imagemodule = Module::getInstance(Module::M_IMAGE); if (!filesystem) throw love::Exception("Filesystem module not loaded!"); diff --git a/jni/love/src/modules/font/BMFontRasterizer.h b/jni/love/src/modules/font/BMFontRasterizer.h index 40591d08..291186a0 100644 --- a/jni/love/src/modules/font/BMFontRasterizer.h +++ b/jni/love/src/modules/font/BMFontRasterizer.h @@ -50,7 +50,7 @@ public: GlyphData *getGlyphData(uint32 glyph) const override; int getGlyphCount() const override; bool hasGlyph(uint32 glyph) const override; - float getKerning(uint32 leftglyph, uint32 rightglyph) const; + float getKerning(uint32 leftglyph, uint32 rightglyph) const override; static bool accepts(love::filesystem::FileData *fontdef); diff --git a/jni/love/src/modules/graphics/opengl/Graphics.cpp b/jni/love/src/modules/graphics/opengl/Graphics.cpp index 46d4d06b..0c5bccd9 100644 --- a/jni/love/src/modules/graphics/opengl/Graphics.cpp +++ b/jni/love/src/modules/graphics/opengl/Graphics.cpp @@ -24,7 +24,6 @@ #include "common/Vector.h" #include "Graphics.h" -#include "window/sdl/Window.h" #include "font/Font.h" #include "Polyline.h" @@ -50,7 +49,8 @@ namespace opengl { Graphics::Graphics() - : quadIndices(nullptr) + : currentWindow(Module::getInstance(Module::M_WINDOW)) + , quadIndices(nullptr) , width(0) , height(0) , created(false) @@ -62,20 +62,21 @@ Graphics::Graphics() states.reserve(10); states.push_back(DisplayState()); - currentWindow = love::window::sdl::Window::createSingleton(); + if (currentWindow.get()) + { + int w, h; + love::window::WindowSettings wsettings; - int w, h; - love::window::WindowSettings wsettings; + currentWindow->getWindow(w, h, wsettings); - currentWindow->getWindow(w, h, wsettings); - - if (currentWindow->isCreated()) - setMode(w, h, wsettings.sRGB); + if (currentWindow->isOpen()) + setMode(w, h, wsettings.sRGB); + } } Graphics::~Graphics() { - // We do this manually so the love objects get released before the window. + // We do this manually so the graphics objects are released before the window. states.clear(); defaultFont.set(nullptr); @@ -87,8 +88,6 @@ Graphics::~Graphics() if (quadIndices) delete quadIndices; - - currentWindow->release(); } const char *Graphics::getName() const @@ -195,7 +194,7 @@ void Graphics::checkSetDefaultFont() // Create a new default font if we don't have one yet. if (!defaultFont.get()) { - font::Font *fontmodule = Module::getInstance(M_FONT); + auto fontmodule = Module::getInstance(M_FONT); if (!fontmodule) throw love::Exception("Font module has not been loaded."); @@ -238,6 +237,8 @@ void Graphics::setViewportSize(int width, int height) bool Graphics::setMode(int width, int height, bool &sRGB) { + currentWindow.set(Module::getInstance(Module::M_WINDOW)); + this->width = width; this->height = height; @@ -355,7 +356,7 @@ bool Graphics::isActive() const { // The graphics module is only completely 'active' if there's a window, a // context, and the active variable is set. - return active && isCreated() && currentWindow && currentWindow->isCreated(); + return active && isCreated() && currentWindow.get() && currentWindow->isOpen(); } static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/) @@ -494,11 +495,13 @@ void Graphics::discard(const std::vector &colorbuffers, bool stencil) } else { - int activecanvascount = (int) states.back().canvases.size(); + int rendertargetcount = 1; + if (Canvas::current) + rendertargetcount = (int) states.back().canvases.size(); for (int i = 0; i < (int) colorbuffers.size(); i++) { - if (colorbuffers[i] && i < activecanvascount) + if (colorbuffers[i] && i < rendertargetcount) attachments.push_back(GL_COLOR_ATTACHMENT0 + i); } @@ -536,7 +539,8 @@ void Graphics::present() glBindRenderbuffer(GL_RENDERBUFFER, info.info.uikit.colorbuffer); #endif - currentWindow->swapBuffers(); + if (currentWindow.get()) + currentWindow->swapBuffers(); // Restore the currently active canvas, if there is one. setCanvas(canvases); @@ -660,12 +664,12 @@ void Graphics::clearStencil() glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } -Image *Graphics::newImage(love::image::ImageData *data, const Image::Flags &flags) +Image *Graphics::newImage(const std::vector &data, const Image::Flags &flags) { return new Image(data, flags); } -Image *Graphics::newImage(love::image::CompressedImageData *cdata, const Image::Flags &flags) +Image *Graphics::newImage(const std::vector &cdata, const Image::Flags &flags) { return new Image(cdata, flags); } @@ -945,7 +949,7 @@ void Graphics::setBlendMode(BlendMode mode, bool multiplyalpha) func = GL_FUNC_REVERSE_SUBTRACT; case BLEND_ADD: srcRGB = GL_ONE; - srcA = GL_SRC_ALPHA; // FIXME: This isn't correct... + srcA = GL_ZERO; dstRGB = dstA = GL_ONE; break; case BLEND_SCREEN: @@ -1133,6 +1137,13 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, floa return; } + // Radius values that are more than half the rectangle's size aren't handled + // correctly (for now)... + if (w >= 0.02f) + rx = std::min(rx, w / 2.0f - 0.01f); + if (h >= 0.02f) + ry = std::min(ry, h / 2.0f - 0.01f); + points = std::max(points, 1); const float half_pi = static_cast(LOVE_M_PI / 2); @@ -1308,8 +1319,34 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool throw love::Exception("Out of memory."); } +#ifdef LOVE_IOS + SDL_SysWMinfo info = {}; + SDL_VERSION(&info.version); + SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info); + + if (info.info.uikit.resolveFramebuffer != 0) + { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, info.info.uikit.resolveFramebuffer); + + // We need to do an explicit MSAA resolve on iOS, because it uses GLES + // FBOs rather than a system framebuffer. + if (GLAD_ES_VERSION_3_0) + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + else if (GLAD_APPLE_framebuffer_multisample) + glResolveMultisampleFramebufferAPPLE(); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, info.info.uikit.resolveFramebuffer); + } +#endif + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); +#ifdef LOVE_IOS + // Restore the previous binding for the main framebuffer. + if (info.info.uikit.resolveFramebuffer != 0) + glBindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO()); +#endif + if (!copyAlpha) { // Replace alpha values with full opacity. diff --git a/jni/love/src/modules/graphics/opengl/Graphics.h b/jni/love/src/modules/graphics/opengl/Graphics.h index ba9a97ac..c8457752 100644 --- a/jni/love/src/modules/graphics/opengl/Graphics.h +++ b/jni/love/src/modules/graphics/opengl/Graphics.h @@ -157,8 +157,8 @@ public: /** * Creates an Image object with padding and/or optimization. **/ - Image *newImage(love::image::ImageData *data, const Image::Flags &flags); - Image *newImage(love::image::CompressedImageData *cdata, const Image::Flags &flags); + Image *newImage(const std::vector &data, const Image::Flags &flags); + Image *newImage(const std::vector &cdata, const Image::Flags &flags); Quad *newQuad(Quad::Viewport v, float sw, float sh); @@ -508,7 +508,7 @@ private: void checkSetDefaultFont(); - love::window::Window *currentWindow; + StrongRef currentWindow; StrongRef defaultFont; diff --git a/jni/love/src/modules/graphics/opengl/Image.cpp b/jni/love/src/modules/graphics/opengl/Image.cpp index e591c234..a217e7fb 100644 --- a/jni/love/src/modules/graphics/opengl/Image.cpp +++ b/jni/love/src/modules/graphics/opengl/Image.cpp @@ -23,7 +23,6 @@ #include "common/int.h" // STD -#include // For memcpy #include // for min/max #ifdef LOVE_ANDROID @@ -50,18 +49,62 @@ float Image::maxMipmapSharpness = 0.0f; Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_LINEAR; float Image::defaultMipmapSharpness = 0.0f; -Image::Image(love::image::ImageData *data, const Flags &flags) - : data(data) - , cdata(nullptr) - , texture(0) +static int getMipmapCount(int basewidth, int baseheight) +{ + return (int) log2(std::max(basewidth, baseheight)) + 1; +} + +template +static bool verifyMipmapLevels(const std::vector &miplevels) +{ + int numlevels = (int) miplevels.size(); + + if (numlevels == 1) + return false; + + int width = miplevels[0]->getWidth(); + int height = miplevels[0]->getHeight(); + + int expectedlevels = getMipmapCount(width, height); + + // All mip levels must be present when not using auto-generated mipmaps. + if (numlevels != expectedlevels) + throw love::Exception("Image does not have all required mipmap levels (expected %d, got %d)", expectedlevels, numlevels); + + // Verify the size of each mip level. + for (int i = 1; i < numlevels; i++) + { + width = std::max(width / 2, 1); + height = std::max(height / 2, 1); + + if (miplevels[i]->getWidth() != width) + throw love::Exception("Width of image mipmap level %d is incorrect (expected %d, got %d)", i+1, width, miplevels[i]->getWidth()); + if (miplevels[i]->getHeight() != height) + throw love::Exception("Height of image mipmap level %d is incorrect (expected %d, got %d)", i+1, height, miplevels[i]->getHeight()); + } + + return true; +} + +Image::Image(const std::vector &imagedata, const Flags &flags) + : texture(0) , mipmapSharpness(defaultMipmapSharpness) , compressed(false) , flags(flags) , usingDefaultTexture(false) , textureMemorySize(0) { - width = data->getWidth(); - height = data->getHeight(); + if (imagedata.empty()) + throw love::Exception(""); + + width = imagedata[0]->getWidth(); + height = imagedata[0]->getHeight(); + + if (verifyMipmapLevels(imagedata)) + this->flags.mipmaps = true; + + for (const auto &id : imagedata) + data.push_back(id); preload(); loadVolatile(); @@ -69,27 +112,29 @@ Image::Image(love::image::ImageData *data, const Flags &flags) ++imageCount; } -Image::Image(love::image::CompressedImageData *cdata, const Flags &flags) - : data(nullptr) - , cdata(cdata) - , texture(0) +Image::Image(const std::vector &compresseddata, const Flags &flags) + : texture(0) , mipmapSharpness(defaultMipmapSharpness) , compressed(true) , flags(flags) , usingDefaultTexture(false) , textureMemorySize(0) { - this->flags.sRGB = (flags.sRGB || cdata->isSRGB()); + this->flags.sRGB = (flags.sRGB || compresseddata[0]->isSRGB()); - width = cdata->getWidth(0); - height = cdata->getHeight(0); + width = compresseddata[0]->getWidth(0); + height = compresseddata[0]->getHeight(0); - if (flags.mipmaps) + if (verifyMipmapLevels(compresseddata)) + this->flags.mipmaps = true; + else if (flags.mipmaps && getMipmapCount(width, height) != compresseddata[0]->getMipmapCount()) + throw love::Exception("Image cannot have mipmaps: compressed image data does not have all required mipmap levels."); + + for (const auto &cd : compresseddata) { - // The mipmap texture data comes from the CompressedImageData in this case, - // so we should make sure it has all necessary mipmap levels. - if (cdata->getMipmapCount() < (int) log2(std::max(width, height)) + 1) - throw love::Exception("Image cannot have mipmaps: compressed image data does not have all required mipmap levels."); + cdata.push_back(cd); + if (cd->getFormat() != cdata[0]->getFormat()) + throw love::Exception("All image mipmap levels must have the same format."); } preload(); @@ -169,14 +214,25 @@ void Image::loadDefaultTexture() void Image::loadFromCompressedData() { - GLenum iformat = getCompressedFormat(cdata->getFormat()); - int count = flags.mipmaps ? cdata->getMipmapCount() : 1; + GLenum iformat = getCompressedFormat(cdata[0]->getFormat()); + + int count = 1; + + if (flags.mipmaps && cdata.size() > 1) + count = (int) cdata.size(); + else if (flags.mipmaps) + count = cdata[0]->getMipmapCount(); for (int i = 0; i < count; i++) { - glCompressedTexImage2D(GL_TEXTURE_2D, i, iformat, - cdata->getWidth(i), cdata->getHeight(i), 0, - (GLsizei) cdata->getSize(i), cdata->getData(i)); + // Compressed image mipmaps can come from separate CompressedImageData + // objects, or all from a single object. + auto cd = cdata.size() > 1 ? cdata[i].get() : cdata[0].get(); + int datamip = cdata.size() > 1 ? 0 : i; + + glCompressedTexImage2D(GL_TEXTURE_2D, i, iformat, cd->getWidth(datamip), + cd->getHeight(datamip), 0, + (GLsizei) cd->getSize(datamip), cd->getData(datamip)); } } @@ -192,23 +248,29 @@ void Image::loadFromImageData() iformat = format; } + int mipcount = flags.mipmaps ? (int) data.size() : 1; + + for (int i = 0; i < mipcount; i++) { - love::thread::Lock lock(data->getMutex()); - glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, format, - GL_UNSIGNED_BYTE, data->getData()); + love::image::ImageData *id = data[i].get(); + love::thread::Lock lock(id->getMutex()); + + glTexImage2D(GL_TEXTURE_2D, i, iformat, id->getWidth(), id->getHeight(), + 0, format, GL_UNSIGNED_BYTE, id->getData()); } - generateMipmaps(); + if (data.size() <= 1) + generateMipmaps(); } bool Image::loadVolatile() { OpenGL::TempDebugGroup debuggroup("Image load"); - if (isCompressed() && !hasCompressedTextureSupport(cdata->getFormat(), flags.sRGB)) + if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), flags.sRGB)) { const char *str; - if (image::CompressedImageData::getConstant(cdata->getFormat(), str)) + if (image::CompressedImageData::getConstant(cdata[0]->getFormat(), str)) { throw love::Exception("Cannot create image: " "%s%s compressed images are not supported on this system.", flags.sRGB ? "sRGB " : "", str); @@ -222,7 +284,8 @@ bool Image::loadVolatile() throw love::Exception("sRGB images are not supported on this system."); // GL_EXT_sRGB doesn't support glGenerateMipmap for sRGB textures. - if (flags.sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0)) + if (flags.sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0) + && data.size() <= 1) { flags.mipmaps = false; filter.mipmap = FILTER_NONE; @@ -254,13 +317,10 @@ bool Image::loadVolatile() return true; } - if ((isCompressed() || !flags.mipmaps) && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0)) - { - int count = (flags.mipmaps && isCompressed()) ? cdata->getMipmapCount() : 1; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1); - } + if (!flags.mipmaps && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0)) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - if (flags.mipmaps && !isCompressed() && + if (flags.mipmaps && !isCompressed() && data.size() <= 1 && !(GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)) { // Auto-generate mipmaps every time the texture is modified, if @@ -291,17 +351,12 @@ bool Image::loadVolatile() size_t prevmemsize = textureMemorySize; if (isCompressed()) - { - textureMemorySize = 0; - for (int i = 0; i < (flags.mipmaps ? cdata->getMipmapCount() : 1); i++) - textureMemorySize += cdata->getSize(i); - } + textureMemorySize = cdata[0]->getSize(); else - { - textureMemorySize = width * height * 4; - if (flags.mipmaps) - textureMemorySize *= 1.333; - } + textureMemorySize = data[0]->getSize(); + + if (flags.mipmaps) + textureMemorySize *= 1.33334; gl.updateTextureMemorySize(prevmemsize, textureMemorySize); @@ -333,31 +388,44 @@ bool Image::refresh(int xoffset, int yoffset, int w, int h) throw love::Exception("Invalid rectangle dimensions."); } + OpenGL::TempDebugGroup debuggroup("Image refresh"); + gl.bindTexture(texture); if (isCompressed()) - loadFromCompressedData(); - else { - const image::pixel *pdata = (const image::pixel *) data->getData(); - pdata += yoffset * data->getWidth() + xoffset; - - GLenum format = GL_RGBA; - - // In ES2, the format parameter of TexSubImage must match the internal - // format of the texture. - if (flags.sRGB && (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)) - format = GL_SRGB_ALPHA; - - { - thread::Lock lock(data->getMutex()); - glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, w, h, format, - GL_UNSIGNED_BYTE, pdata); - } - - generateMipmaps(); + loadFromCompressedData(); + return true; } + GLenum format = GL_RGBA; + + // In ES2, the format parameter of TexSubImage must match the internal + // format of the texture. + if (flags.sRGB && (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)) + format = GL_SRGB_ALPHA; + + int mipcount = flags.mipmaps ? (int) data.size() : 1; + + // Reupload the sub-rectangle of each mip level (if we have custom mipmaps.) + for (int i = 0; i < mipcount; i++) + { + const image::pixel *pdata = (const image::pixel *) data[i]->getData(); + pdata += yoffset * data[i]->getWidth() + xoffset; + + thread::Lock lock(data[i]->getMutex()); + glTexSubImage2D(GL_TEXTURE_2D, i, xoffset, yoffset, w, h, format, + GL_UNSIGNED_BYTE, pdata); + + xoffset /= 2; + yoffset /= 2; + w = std::max(w / 2, 1); + h = std::max(h / 2, 1); + } + + if (data.size() <= 1) + generateMipmaps(); + return true; } @@ -398,14 +466,14 @@ const void *Image::getHandle() const return &texture; } -love::image::ImageData *Image::getImageData() const +const std::vector> &Image::getImageData() const { - return data.get(); + return data; } -love::image::CompressedImageData *Image::getCompressedData() const +const std::vector> &Image::getCompressedData() const { - return cdata.get(); + return cdata; } void Image::setFilter(const Texture::Filter &f) diff --git a/jni/love/src/modules/graphics/opengl/Image.h b/jni/love/src/modules/graphics/opengl/Image.h index 65f8cfc1..57308df2 100644 --- a/jni/love/src/modules/graphics/opengl/Image.h +++ b/jni/love/src/modules/graphics/opengl/Image.h @@ -69,16 +69,18 @@ public: * Creates a new Image. Not that anything is ready to use * before load is called. * - * @param data The data from which to load the image. + * @param data The data from which to load the image. Each element in the + * array is a mipmap level. If more than the base level is present, all + * mip levels must be present. **/ - Image(love::image::ImageData *data, const Flags &flags); + Image(const std::vector &data, const Flags &flags); /** * Creates a new Image with compressed image data. * * @param cdata The compressed data from which to load the image. **/ - Image(love::image::CompressedImageData *cdata, const Flags &flags); + Image(const std::vector &cdata, const Flags &flags); virtual ~Image(); @@ -98,8 +100,8 @@ public: virtual const void *getHandle() const; - love::image::ImageData *getImageData() const; - love::image::CompressedImageData *getCompressedData() const; + const std::vector> &getImageData() const; + const std::vector> &getCompressedData() const; virtual void setFilter(const Texture::Filter &f); virtual bool setWrap(const Texture::Wrap &w); @@ -147,13 +149,14 @@ private: GLenum getCompressedFormat(image::CompressedImageData::Format cformat) const; - // The ImageData from which the texture is created. May be null if + // The ImageData from which the texture is created. May be empty if // Compressed image data was used to create the texture. - StrongRef data; + // Each element in the array is a mipmap level. + std::vector> data; // Or the Compressed Image Data from which the texture is created. May be - // null if raw ImageData was used to create the texture. - StrongRef cdata; + // empty if raw ImageData was used to create the texture. + std::vector> cdata; // OpenGL texture identifier. GLuint texture; diff --git a/jni/love/src/modules/graphics/opengl/OpenGL.cpp b/jni/love/src/modules/graphics/opengl/OpenGL.cpp index a6d7d2aa..df1092a0 100644 --- a/jni/love/src/modules/graphics/opengl/OpenGL.cpp +++ b/jni/love/src/modules/graphics/opengl/OpenGL.cpp @@ -40,6 +40,10 @@ #include #endif +#ifdef LOVE_ANDROID +#include +#endif + namespace love { namespace graphics @@ -47,6 +51,17 @@ namespace graphics namespace opengl { +static void *LOVEGetProcAddress(const char *name) +{ +#ifdef LOVE_ANDROID + void *proc = dlsym(RTLD_DEFAULT, name); + if (proc) + return proc; +#endif + + return SDL_GL_GetProcAddress(name); +} + OpenGL::OpenGL() : stats() , contextInitialized(false) @@ -67,7 +82,7 @@ bool OpenGL::initContext() if (contextInitialized) return true; - if (!gladLoadGLLoader(SDL_GL_GetProcAddress)) + if (!gladLoadGLLoader(LOVEGetProcAddress)) return false; initOpenGLFunctions(); diff --git a/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp b/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp index 48d916e1..b1d9cdbf 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -38,7 +38,7 @@ int w_Canvas_renderTo(lua_State *L) Canvas *canvas = luax_checkcanvas(L, 1); luaL_checktype(L, 2, LUA_TFUNCTION); - Graphics *graphics = Module::getInstance(Module::M_GRAPHICS); + auto graphics = Module::getInstance(Module::M_GRAPHICS); if (graphics) { diff --git a/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp b/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp index 6057a14b..a27e333f 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -25,6 +25,7 @@ #include "image/Image.h" #include "font/Rasterizer.h" #include "filesystem/wrap_Filesystem.h" +#include "image/wrap_Image.h" #include #include @@ -236,8 +237,8 @@ static const char *imageFlagName(Image::FlagType flagtype) int w_newImage(lua_State *L) { - love::image::ImageData *data = nullptr; - love::image::CompressedImageData *cdata = nullptr; + std::vector data; + std::vector cdata; Image::Flags flags; if (!lua_isnoneornil(L, 2)) @@ -252,23 +253,23 @@ int w_newImage(lua_State *L) // Convert to ImageData / CompressedImageData, if necessary. if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_ID) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_ID)) { - love::image::Image *image = Module::getInstance(Module::M_IMAGE); - if (image == nullptr) + auto imagemodule = Module::getInstance(Module::M_IMAGE); + if (imagemodule == nullptr) return luaL_error(L, "Cannot load images without the love.image module."); love::filesystem::FileData *fdata = love::filesystem::luax_getfiledata(L, 1); - if (image->isCompressed(fdata)) + if (imagemodule->isCompressed(fdata)) { luax_catchexcept(L, - [&]() { cdata = image->newCompressedData(fdata); }, + [&]() { cdata.push_back(imagemodule->newCompressedData(fdata)); }, [&](bool) { fdata->release(); } ); } else { luax_catchexcept(L, - [&]() { data = image->newImageData(fdata); }, + [&]() { data.push_back(imagemodule->newImageData(fdata)); }, [&](bool) { fdata->release(); } ); } @@ -277,27 +278,61 @@ int w_newImage(lua_State *L) releasedata = true; } else if (luax_istype(L, 1, IMAGE_COMPRESSED_IMAGE_DATA_ID)) - cdata = luax_checktype(L, 1, IMAGE_COMPRESSED_IMAGE_DATA_ID); + cdata.push_back(love::image::luax_checkcompressedimagedata(L, 1)); else - data = luax_checktype(L, 1, IMAGE_IMAGE_DATA_ID); + data.push_back(love::image::luax_checkimagedata(L, 1)); - if (!data && !cdata) - return luaL_error(L, "Error creating image (could not load data.)"); + if (lua_istable(L, 2)) + { + lua_getfield(L, 2, imageFlagName(Image::FLAG_TYPE_MIPMAPS)); + + // Add all manually specified mipmap images to the array of imagedata. + // i.e. flags = {mipmaps = {mip1, mip2, ...}}. + if (lua_istable(L, -1)) + { + for (size_t i = 1; i <= luax_objlen(L, -1); i++) + { + lua_rawgeti(L, -1, i); + + if (!data.empty()) + { + if (!luax_istype(L, -1, IMAGE_IMAGE_DATA_ID)) + luax_convobj(L, -1, "image", "newImageData"); + + data.push_back(love::image::luax_checkimagedata(L, -1)); + } + else if (!cdata.empty()) + { + if (!luax_istype(L, -1, IMAGE_COMPRESSED_IMAGE_DATA_ID)) + luax_convobj(L, -1, "image", "newCompressedData"); + + cdata.push_back(love::image::luax_checkcompressedimagedata(L, -1)); + } + + lua_pop(L, 1); + } + } + + lua_pop(L, 1); + } // Create the image. Image *image = nullptr; luax_catchexcept(L, [&]() { - if (cdata) + if (!cdata.empty()) image = instance()->newImage(cdata, flags); - else if (data) + else if (!data.empty()) image = instance()->newImage(data, flags); }, [&](bool) { - if (releasedata && data) - data->release(); - else if (releasedata && cdata) - cdata->release(); + if (releasedata) + { + for (auto d : data) + d->release(); + for (auto d : cdata) + d->release(); + } } ); @@ -363,10 +398,10 @@ int w_newImageFont(lua_State *L) { Image *i = luax_checktype(L, 1, GRAPHICS_IMAGE_ID); filter = i->getFilter(); - love::image::ImageData *id = i->getImageData(); - if (!id) + const auto &idlevels = i->getImageData(); + if (idlevels.empty()) return luaL_argerror(L, 1, "Image must not be compressed."); - luax_pushtype(L, IMAGE_IMAGE_DATA_ID, id); + luax_pushtype(L, IMAGE_IMAGE_DATA_ID, idlevels[0].get()); lua_replace(L, 1); } @@ -1557,11 +1592,6 @@ int w_rectangle(lua_State *L) float rx = (float)luaL_optnumber(L, 6, 0.0); float ry = (float)luaL_optnumber(L, 7, rx); - if (w > 0.0 && rx >= w / 2.0) - return luaL_error(L, "Invalid rectangle x-axis radius (must be less than half the width)"); - if (h > 0.0 && ry >= h / 2.0) - return luaL_error(L, "Invalid rectangle y-axis radius (must be less than half the height)"); - int points; if (lua_isnoneornil(L, 8)) points = std::max(rx, ry) > 20.0 ? (int)(std::max(rx, ry) / 2) : 10; diff --git a/jni/love/src/modules/graphics/opengl/wrap_Image.cpp b/jni/love/src/modules/graphics/opengl/wrap_Image.cpp index 3e2790e3..d598bde8 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Image.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Image.cpp @@ -92,13 +92,26 @@ int w_Image_refresh(lua_State *L) int w_Image_getData(lua_State *L) { Image *i = luax_checkimage(L, 1); + int n = 0; if (i->isCompressed()) - luax_pushtype(L, IMAGE_COMPRESSED_IMAGE_DATA_ID, i->getCompressedData()); + { + for (const auto &cdata : i->getCompressedData()) + { + luax_pushtype(L, IMAGE_COMPRESSED_IMAGE_DATA_ID, cdata.get()); + n++; + } + } else - luax_pushtype(L, IMAGE_IMAGE_DATA_ID, i->getImageData()); + { + for (const auto &data : i->getImageData()) + { + luax_pushtype(L, IMAGE_IMAGE_DATA_ID, data.get()); + n++; + } + } - return 1; + return n; } static const char *imageFlagName(Image::FlagType flagtype) diff --git a/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.cpp b/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.cpp index 08ff147f..c550fd6f 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_SpriteBatch.cpp @@ -39,19 +39,17 @@ SpriteBatch *luax_checkspritebatch(lua_State *L, int idx) return luax_checktype(L, idx, GRAPHICS_SPRITE_BATCH_ID); } -int w_SpriteBatch_add(lua_State *L) +static inline int w_SpriteBatch_add_or_set(lua_State *L, SpriteBatch *t, int startidx, int index) { - SpriteBatch *t = luax_checkspritebatch(L, 1); Quad *quad = nullptr; - int startidx = 2; - if (luax_istype(L, 2, GRAPHICS_QUAD_ID)) + if (luax_istype(L, startidx, GRAPHICS_QUAD_ID)) { - quad = luax_totype(L, 2, GRAPHICS_QUAD_ID); - startidx = 3; + quad = luax_totype(L, startidx, GRAPHICS_QUAD_ID); + startidx++; } - else if (lua_isnil(L, 2) && !lua_isnoneornil(L, 3)) - return luax_typerror(L, 2, "Quad"); + else if (lua_isnil(L, startidx) && !lua_isnoneornil(L, startidx + 1)) + return luax_typerror(L, startidx, "Quad"); float x = (float) luaL_optnumber(L, startidx + 0, 0.0); float y = (float) luaL_optnumber(L, startidx + 1, 0.0); @@ -63,15 +61,23 @@ int w_SpriteBatch_add(lua_State *L) float kx = (float) luaL_optnumber(L, startidx + 7, 0.0); float ky = (float) luaL_optnumber(L, startidx + 8, 0.0); - int index = 0; luax_catchexcept(L, [&]() { if (quad) - index = t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky); + index = t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky, index); else - index = t->add(x, y, a, sx, sy, ox, oy, kx, ky); + index = t->add(x, y, a, sx, sy, ox, oy, kx, ky, index); }); + return index; +} + +int w_SpriteBatch_add(lua_State *L) +{ + SpriteBatch *t = luax_checkspritebatch(L, 1); + + int index = w_SpriteBatch_add_or_set(L, t, 2, -1); lua_pushinteger(L, index + 1); + return 1; } @@ -80,33 +86,7 @@ int w_SpriteBatch_set(lua_State *L) SpriteBatch *t = luax_checkspritebatch(L, 1); int index = (int) luaL_checknumber(L, 2) - 1; - Quad *quad = nullptr; - int startidx = 3; - - if (luax_istype(L, 3, GRAPHICS_QUAD_ID)) - { - quad = luax_totype(L, 3, GRAPHICS_QUAD_ID); - startidx = 4; - } - else if (lua_isnil(L, 3) && !lua_isnoneornil(L, 4)) - return luax_typerror(L, 3, "Quad"); - - float x = (float) luaL_optnumber(L, startidx + 0, 0.0); - float y = (float) luaL_optnumber(L, startidx + 1, 0.0); - float a = (float) luaL_optnumber(L, startidx + 2, 0.0); - float sx = (float) luaL_optnumber(L, startidx + 3, 1.0); - float sy = (float) luaL_optnumber(L, startidx + 4, sx); - float ox = (float) luaL_optnumber(L, startidx + 5, 0.0); - float oy = (float) luaL_optnumber(L, startidx + 6, 0.0); - float kx = (float) luaL_optnumber(L, startidx + 7, 0.0); - float ky = (float) luaL_optnumber(L, startidx + 8, 0.0); - - luax_catchexcept(L, [&]() { - if (quad) - t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky, index); - else - t->add(x, y, a, sx, sy, ox, oy, kx, ky, index); - }); + w_SpriteBatch_add_or_set(L, t, 3, index); return 0; } diff --git a/jni/love/src/modules/image/CompressedImageData.h b/jni/love/src/modules/image/CompressedImageData.h index 8a7a1a7e..7dcaa258 100644 --- a/jni/love/src/modules/image/CompressedImageData.h +++ b/jni/love/src/modules/image/CompressedImageData.h @@ -107,12 +107,12 @@ public: /** * Gets the width of a sub-image at the specified mipmap level. **/ - int getWidth(int miplevel) const; + int getWidth(int miplevel = 0) const; /** * Gets the height of a sub-image at the specified mipmap level. **/ - int getHeight(int miplevel) const; + int getHeight(int miplevel = 0) const; /** * Gets the format of the compressed data. diff --git a/jni/love/src/modules/image/ImageData.cpp b/jni/love/src/modules/image/ImageData.cpp index 2ccbd3a7..841dea7a 100644 --- a/jni/love/src/modules/image/ImageData.cpp +++ b/jni/love/src/modules/image/ImageData.cpp @@ -30,12 +30,10 @@ namespace image ImageData::ImageData() : data(nullptr) { - mutex = thread::newMutex(); } ImageData::~ImageData() { - delete mutex; } size_t ImageData::getSize() const diff --git a/jni/love/src/modules/image/ImageData.h b/jni/love/src/modules/image/ImageData.h index 81e39266..9626f458 100644 --- a/jni/love/src/modules/image/ImageData.h +++ b/jni/love/src/modules/image/ImageData.h @@ -23,7 +23,7 @@ // LOVE #include "common/Data.h" -#include "filesystem/File.h" +#include "filesystem/FileData.h" #include "thread/threads.h" using love::thread::Mutex; @@ -121,7 +121,7 @@ public: * @param f The file to save the encoded image data to. * @param format The format of the encoded data. **/ - virtual void encode(love::filesystem::File *f, EncodedFormat format) = 0; + virtual love::filesystem::FileData *encode(EncodedFormat format, const char *filename) = 0; love::thread::Mutex *getMutex() const; @@ -146,7 +146,7 @@ protected: // We need to be thread-safe // so we lock when we're accessing our // data - Mutex *mutex; + love::thread::MutexRef mutex; private: diff --git a/jni/love/src/modules/image/magpie/ImageData.cpp b/jni/love/src/modules/image/magpie/ImageData.cpp index 7920c78a..a0b76e87 100644 --- a/jni/love/src/modules/image/magpie/ImageData.cpp +++ b/jni/love/src/modules/image/magpie/ImageData.cpp @@ -144,7 +144,7 @@ void ImageData::decode(love::filesystem::FileData *data) decodeHandler = decoder; } -void ImageData::encode(love::filesystem::File *f, ImageData::EncodedFormat format) +love::filesystem::FileData *ImageData::encode(EncodedFormat format, const char *filename) { FormatHandler *encoder = nullptr; FormatHandler::EncodedImage encodedimage; @@ -174,14 +174,14 @@ void ImageData::encode(love::filesystem::File *f, ImageData::EncodedFormat forma { const char *fname = "unknown"; getConstant(format, fname); - throw love::Exception("no suitable image encoder for %s format.", fname); + throw love::Exception("No suitable image encoder for %s format.", fname); } + love::filesystem::FileData *filedata = nullptr; + try { - f->open(love::filesystem::File::MODE_WRITE); - f->write(encodedimage.data, encodedimage.size); - f->close(); + filedata = new love::filesystem::FileData(encodedimage.size, filename); } catch (love::Exception &) { @@ -189,7 +189,10 @@ void ImageData::encode(love::filesystem::File *f, ImageData::EncodedFormat forma throw; } + memcpy(filedata->getData(), encodedimage.data, encodedimage.size); encoder->free(encodedimage.data); + + return filedata; } } // magpie diff --git a/jni/love/src/modules/image/magpie/ImageData.h b/jni/love/src/modules/image/magpie/ImageData.h index 181b3a67..6c396c8f 100644 --- a/jni/love/src/modules/image/magpie/ImageData.h +++ b/jni/love/src/modules/image/magpie/ImageData.h @@ -23,7 +23,6 @@ // LOVE #include "FormatHandler.h" -#include "filesystem/File.h" #include "image/ImageData.h" // C++ @@ -46,7 +45,7 @@ public: virtual ~ImageData(); // Implements image::ImageData. - virtual void encode(love::filesystem::File *f, ImageData::EncodedFormat format); + virtual love::filesystem::FileData *encode(EncodedFormat format, const char *filename); private: diff --git a/jni/love/src/modules/image/wrap_ImageData.cpp b/jni/love/src/modules/image/wrap_ImageData.cpp index 970bf486..02128790 100644 --- a/jni/love/src/modules/image/wrap_ImageData.cpp +++ b/jni/love/src/modules/image/wrap_ImageData.cpp @@ -217,31 +217,37 @@ int w_ImageData_paste(lua_State *L) int w_ImageData_encode(lua_State *L) { - std::string ext; - const char *fmt; - ImageData::EncodedFormat format = ImageData::ENCODED_MAX_ENUM; ImageData *t = luax_checkimagedata(L, 1); - if (lua_isstring(L, 2)) - luax_convobj(L, 2, "filesystem", "newFile"); - love::filesystem::File *file = luax_checktype(L, 2, FILESYSTEM_FILE_ID); + ImageData::EncodedFormat format; + const char *fmt = luaL_checkstring(L, 2); + if (!ImageData::getConstant(fmt, format)) + return luaL_error(L, "Invalid encoded image format '%s'.", fmt); - if (lua_isnoneornil(L, 3)) + bool hasfilename = false; + + std::string filename = "Image." + std::string(fmt); + if (!lua_isnoneornil(L, 3)) { - ext = file->getExtension(); - fmt = ext.c_str(); - if (!ImageData::getConstant(fmt, format)) - return luaL_error(L, "Invalid image format '%s'.", fmt); - } - else - { - fmt = luaL_checkstring(L, 3); - if (!ImageData::getConstant(fmt, format)) - return luaL_error(L, "Invalid image format '%s'.", fmt); + hasfilename = true; + filename = luax_checkstring(L, 3); } - luax_catchexcept(L, [&](){ t->encode(file, format); }); - return 0; + love::filesystem::FileData *filedata = nullptr; + luax_catchexcept(L, [&](){ filedata = t->encode(format, filename.c_str()); }); + + luax_pushtype(L, FILESYSTEM_FILE_DATA_ID, filedata); + filedata->release(); + + if (hasfilename) + { + luax_getfunction(L, "filesystem", "write"); + lua_pushvalue(L, 3); // filename + lua_pushvalue(L, -3); // FileData + lua_call(L, 2, 0); + } + + return 1; } int w_ImageData__performAtomic(lua_State *L) diff --git a/jni/love/src/modules/keyboard/Keyboard.cpp b/jni/love/src/modules/keyboard/Keyboard.cpp index 804b46c1..969e27f2 100644 --- a/jni/love/src/modules/keyboard/Keyboard.cpp +++ b/jni/love/src/modules/keyboard/Keyboard.cpp @@ -188,7 +188,6 @@ StringMap::Entry Keyboard::keyEntries[] = {"execute", Keyboard::KEY_EXECUTE}, {"help", Keyboard::KEY_HELP}, {"menu", Keyboard::KEY_MENU}, - {"search", Keyboard::KEY_SEARCH}, {"select", Keyboard::KEY_SELECT}, {"stop", Keyboard::KEY_STOP}, {"again", Keyboard::KEY_AGAIN}, diff --git a/jni/love/src/modules/keyboard/Keyboard.h b/jni/love/src/modules/keyboard/Keyboard.h index 82c90691..3ac7cb43 100644 --- a/jni/love/src/modules/keyboard/Keyboard.h +++ b/jni/love/src/modules/keyboard/Keyboard.h @@ -181,7 +181,6 @@ public: KEY_EXECUTE, KEY_HELP, KEY_MENU, - KEY_SEARCH, KEY_SELECT, KEY_STOP, KEY_AGAIN, diff --git a/jni/love/src/modules/keyboard/sdl/Keyboard.cpp b/jni/love/src/modules/keyboard/sdl/Keyboard.cpp index c4ad0509..43f39e2e 100644 --- a/jni/love/src/modules/keyboard/sdl/Keyboard.cpp +++ b/jni/love/src/modules/keyboard/sdl/Keyboard.cpp @@ -122,7 +122,7 @@ void Keyboard::setTextInput(bool enable, double x, double y, double w, double h) { // SDL_SetTextInputRect expects coordinates in window-space but setTextInput // takes pixels, so we should convert. - window::Window *window = Module::getInstance(M_WINDOW); + auto window = Module::getInstance(M_WINDOW); if (window) { window->pixelToWindowCoords(&x, &y); @@ -299,7 +299,6 @@ const SDL_Keycode *Keyboard::createKeyMap() k[Keyboard::KEY_EXECUTE] = SDLK_EXECUTE; k[Keyboard::KEY_HELP] = SDLK_HELP; k[Keyboard::KEY_MENU] = SDLK_MENU; - k[Keyboard::KEY_SEARCH] = SDLK_AC_SEARCH; k[Keyboard::KEY_SELECT] = SDLK_SELECT; k[Keyboard::KEY_STOP] = SDLK_STOP; k[Keyboard::KEY_AGAIN] = SDLK_AGAIN; diff --git a/jni/love/src/modules/love/love.cpp b/jni/love/src/modules/love/love.cpp index b06a2b48..86345976 100644 --- a/jni/love/src/modules/love/love.cpp +++ b/jni/love/src/modules/love/love.cpp @@ -238,8 +238,10 @@ static int w_love_isVersionCompatible(lua_State *L) return 1; } -int luaopen_love(lua_State * L) +int luaopen_love(lua_State *L) { + love::luax_insistpinnedthread(L); + love::luax_insistglobal(L, "love"); // Set version information. diff --git a/jni/love/src/modules/mouse/sdl/Mouse.cpp b/jni/love/src/modules/mouse/sdl/Mouse.cpp index 27f895be..36722eab 100644 --- a/jni/love/src/modules/mouse/sdl/Mouse.cpp +++ b/jni/love/src/modules/mouse/sdl/Mouse.cpp @@ -36,7 +36,7 @@ namespace sdl // we want them in pixel coordinates (may be different with high-DPI enabled.) static void windowToPixelCoords(double *x, double *y) { - window::Window *window = Module::getInstance(Module::M_WINDOW); + auto window = Module::getInstance(Module::M_WINDOW); if (window) window->windowToPixelCoords(x, y); } @@ -44,7 +44,7 @@ static void windowToPixelCoords(double *x, double *y) // And vice versa for setting mouse coordinates. static void pixelToWindowCoords(double *x, double *y) { - window::Window *window = Module::getInstance(Module::M_WINDOW); + auto window = Module::getInstance(Module::M_WINDOW); if (window) window->pixelToWindowCoords(x, y); } @@ -146,7 +146,7 @@ void Mouse::getPosition(double &x, double &y) const void Mouse::setPosition(double x, double y) { - window::Window *window = Module::getInstance(Module::M_WINDOW); + auto window = Module::getInstance(Module::M_WINDOW); SDL_Window *handle = nullptr; if (window) @@ -211,14 +211,14 @@ bool Mouse::isVisible() const void Mouse::setGrabbed(bool grab) { - window::Window *window = Module::getInstance(Module::M_WINDOW); + auto window = Module::getInstance(Module::M_WINDOW); if (window) window->setMouseGrab(grab); } bool Mouse::isGrabbed() const { - window::Window *window = Module::getInstance(Module::M_WINDOW); + auto window = Module::getInstance(Module::M_WINDOW); if (window) return window->isMouseGrabbed(); else diff --git a/jni/love/src/modules/physics/box2d/Body.cpp b/jni/love/src/modules/physics/box2d/Body.cpp index 998685d4..01e7da9b 100644 --- a/jni/love/src/modules/physics/box2d/Body.cpp +++ b/jni/love/src/modules/physics/box2d/Body.cpp @@ -535,15 +535,7 @@ int Body::setUserData(lua_State *L) body->SetUserData((void *) udata); } - if (udata->ref != nullptr) - { - // We set the Reference's lua_State to this one before deleting it, so - // it unrefs using the current lua_State's stack. This is necessary - // if setUserData is called in a coroutine. - udata->ref->setL(L); - delete udata->ref; - } - + delete udata->ref; udata->ref = new Reference(L); return 0; diff --git a/jni/love/src/modules/physics/box2d/Body.h b/jni/love/src/modules/physics/box2d/Body.h index e399cbc9..d222a19f 100644 --- a/jni/love/src/modules/physics/box2d/Body.h +++ b/jni/love/src/modules/physics/box2d/Body.h @@ -48,7 +48,7 @@ class Fixture; struct bodyudata { // Reference to arbitrary data. - Reference *ref; + Reference *ref = nullptr; }; /** diff --git a/jni/love/src/modules/physics/box2d/Fixture.cpp b/jni/love/src/modules/physics/box2d/Fixture.cpp index ff014355..af33e128 100644 --- a/jni/love/src/modules/physics/box2d/Fixture.cpp +++ b/jni/love/src/modules/physics/box2d/Fixture.cpp @@ -227,15 +227,7 @@ int Fixture::setUserData(lua_State *L) { love::luax_assert_argc(L, 1, 1); - if (data->ref != nullptr) - { - // We set the Reference's lua_State to this one before deleting it, so - // it unrefs using the current lua_State's stack. This is necessary - // if setUserData is called in a coroutine. - data->ref->setL(L); - delete data->ref; - } - + delete data->ref; data->ref = new Reference(L); return 0; diff --git a/jni/love/src/modules/physics/box2d/Fixture.h b/jni/love/src/modules/physics/box2d/Fixture.h index f942592c..32c36245 100644 --- a/jni/love/src/modules/physics/box2d/Fixture.h +++ b/jni/love/src/modules/physics/box2d/Fixture.h @@ -47,7 +47,7 @@ namespace box2d struct fixtureudata { // Reference to arbitrary data. - Reference *ref; + Reference *ref = nullptr; }; /** diff --git a/jni/love/src/modules/physics/box2d/Joint.cpp b/jni/love/src/modules/physics/box2d/Joint.cpp index acc76d54..a7f2d4e3 100644 --- a/jni/love/src/modules/physics/box2d/Joint.cpp +++ b/jni/love/src/modules/physics/box2d/Joint.cpp @@ -193,15 +193,7 @@ int Joint::setUserData(lua_State *L) { love::luax_assert_argc(L, 1, 1); - if (udata->ref != nullptr) - { - // We set the Reference's lua_State to this one before deleting it, so - // it unrefs using the current lua_State's stack. This is necessary - // if setUserData is called in a coroutine. - udata->ref->setL(L); - delete udata->ref; - } - + delete udata->ref; udata->ref = new Reference(L); return 0; diff --git a/jni/love/src/modules/physics/box2d/Joint.h b/jni/love/src/modules/physics/box2d/Joint.h index d20bb385..36149c9d 100644 --- a/jni/love/src/modules/physics/box2d/Joint.h +++ b/jni/love/src/modules/physics/box2d/Joint.h @@ -46,7 +46,7 @@ class World; struct jointudata { // Reference to arbitrary data. - Reference *ref; + Reference *ref = nullptr; }; /** diff --git a/jni/love/src/modules/physics/box2d/World.cpp b/jni/love/src/modules/physics/box2d/World.cpp index ef151a7a..564d0daf 100644 --- a/jni/love/src/modules/physics/box2d/World.cpp +++ b/jni/love/src/modules/physics/box2d/World.cpp @@ -36,6 +36,7 @@ namespace box2d World::ContactCallback::ContactCallback() : ref(nullptr) + , L(nullptr) { } @@ -48,10 +49,9 @@ World::ContactCallback::~ContactCallback() void World::ContactCallback::process(b2Contact *contact, const b2ContactImpulse *impulse) { // Process contacts. - if (ref != nullptr) + if (ref != nullptr && L != nullptr) { - lua_State *L = ref->getL(); - ref->push(); + ref->push(L); // Push first fixture. { @@ -97,6 +97,7 @@ void World::ContactCallback::process(b2Contact *contact, const b2ContactImpulse World::ContactFilter::ContactFilter() : ref(nullptr) + , L(nullptr) { } @@ -124,10 +125,9 @@ bool World::ContactFilter::process(Fixture *a, Fixture *b) (filterB[1] & filterA[0]) == 0) return false; // A and B aren't set to collide - if (ref != nullptr) + if (ref != nullptr && L != nullptr) { - lua_State *L = ref->getL(); - ref->push(); + ref->push(L); luax_pushtype(L, PHYSICS_FIXTURE_ID, a); luax_pushtype(L, PHYSICS_FIXTURE_ID, b); lua_call(L, 2, 1); @@ -136,50 +136,51 @@ bool World::ContactFilter::process(Fixture *a, Fixture *b) return true; } -World::QueryCallback::QueryCallback() - : ref(nullptr) +World::QueryCallback::QueryCallback(lua_State *L, int idx) + : L(L) + , funcidx(idx) { + luaL_checktype(L, funcidx, LUA_TFUNCTION); } World::QueryCallback::~QueryCallback() { - if (ref != nullptr) - delete ref; } bool World::QueryCallback::ReportFixture(b2Fixture *fixture) { - if (ref != nullptr) + if (L != nullptr) { - lua_State *L = ref->getL(); - ref->push(); + lua_pushvalue(L, funcidx); Fixture *f = (Fixture *)Memoizer::find(fixture); if (!f) throw love::Exception("A fixture has escaped Memoizer!"); luax_pushtype(L, PHYSICS_FIXTURE_ID, f); lua_call(L, 1, 1); - return luax_toboolean(L, -1); + bool cont = luax_toboolean(L, -1); + lua_pop(L, 1); + return cont; } + return true; } -World::RayCastCallback::RayCastCallback() - : ref(nullptr) +World::RayCastCallback::RayCastCallback(lua_State *L, int idx) + : L(L) + , funcidx(idx) { + luaL_checktype(L, funcidx, LUA_TFUNCTION); } World::RayCastCallback::~RayCastCallback() { - if (ref != nullptr) - delete ref; } float32 World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction) { - if (ref != nullptr) + if (L != nullptr) { - lua_State *L = ref->getL(); - ref->push(); + lua_pushvalue(L, funcidx); Fixture *f = (Fixture *)Memoizer::find(fixture); if (!f) throw love::Exception("A fixture has escaped Memoizer!"); @@ -193,8 +194,11 @@ float32 World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 & lua_call(L, 6, 1); if (!lua_isnumber(L, -1)) luaL_error(L, "Raycast callback didn't return a number!"); - return (float32)lua_tonumber(L, -1); + float32 fraction = (float32) lua_tonumber(L, -1); + lua_pop(L, 1); + return fraction; } + return 0; } @@ -345,24 +349,28 @@ int World::setCallbacks(lua_State *L) { lua_pushvalue(L, 1); begin.ref = luax_refif(L, LUA_TFUNCTION); + begin.L = L; } if (nargs >= 2) { lua_pushvalue(L, 2); end.ref = luax_refif(L, LUA_TFUNCTION); + end.L = L; } if (nargs >= 3) { lua_pushvalue(L, 3); presolve.ref = luax_refif(L, LUA_TFUNCTION); + presolve.L = L; } if (nargs >= 4) { lua_pushvalue(L, 4); postsolve.ref = luax_refif(L, LUA_TFUNCTION); + postsolve.L = L; } return 0; @@ -370,13 +378,18 @@ int World::setCallbacks(lua_State *L) int World::getCallbacks(lua_State *L) { - begin.ref ? begin.ref->push() : lua_pushnil(L); - end.ref ? end.ref->push() : lua_pushnil(L); - presolve.ref ? presolve.ref->push() : lua_pushnil(L); - postsolve.ref ? postsolve.ref->push() : lua_pushnil(L); + begin.ref ? begin.ref->push(L) : lua_pushnil(L); + end.ref ? end.ref->push(L) : lua_pushnil(L); + presolve.ref ? presolve.ref->push(L) : lua_pushnil(L); + postsolve.ref ? postsolve.ref->push(L) : lua_pushnil(L); return 4; } +void World::setCallbacksL(lua_State *L) +{ + begin.L = end.L = presolve.L = postsolve.L = filter.L = L; +} + int World::setContactFilter(lua_State *L) { if (!lua_isnoneornil(L, 1)) @@ -385,12 +398,13 @@ int World::setContactFilter(lua_State *L) if (filter.ref) delete filter.ref; filter.ref = luax_refif(L, LUA_TFUNCTION); + filter.L = L; return 0; } int World::getContactFilter(lua_State *L) { - filter.ref ? filter.ref->push() : lua_pushnil(L); + filter.ref ? filter.ref->push(L) : lua_pushnil(L); return 1; } @@ -519,8 +533,7 @@ int World::queryBoundingBox(lua_State *L) box.lowerBound = Physics::scaleDown(b2Vec2(lx, ly)); box.upperBound = Physics::scaleDown(b2Vec2(ux, uy)); luaL_checktype(L, 5, LUA_TFUNCTION); - if (query.ref) delete query.ref; - query.ref = luax_refif(L, LUA_TFUNCTION); + QueryCallback query(L, 5); world->QueryAABB(&query, box); return 0; } @@ -534,9 +547,7 @@ int World::rayCast(lua_State *L) b2Vec2 v1 = Physics::scaleDown(b2Vec2(x1, y1)); b2Vec2 v2 = Physics::scaleDown(b2Vec2(x2, y2)); luaL_checktype(L, 5, LUA_TFUNCTION); - if (raycast.ref) - delete raycast.ref; - raycast.ref = luax_refif(L, LUA_TFUNCTION); + RayCastCallback raycast(L, 5); world->RayCast(&raycast, v1, v2); return 0; } diff --git a/jni/love/src/modules/physics/box2d/World.h b/jni/love/src/modules/physics/box2d/World.h index 6e4e7da7..da35d4f7 100644 --- a/jni/love/src/modules/physics/box2d/World.h +++ b/jni/love/src/modules/physics/box2d/World.h @@ -70,6 +70,7 @@ public: { public: Reference *ref; + lua_State *L; ContactCallback(); ~ContactCallback(); void process(b2Contact *contact, const b2ContactImpulse *impulse = NULL); @@ -79,6 +80,7 @@ public: { public: Reference *ref; + lua_State *L; ContactFilter(); ~ContactFilter(); bool process(Fixture *a, Fixture *b); @@ -87,19 +89,23 @@ public: class QueryCallback : public b2QueryCallback { public: - Reference *ref; - QueryCallback(); + QueryCallback(lua_State *L, int idx); ~QueryCallback(); virtual bool ReportFixture(b2Fixture *fixture); + private: + lua_State *L; + int funcidx; }; class RayCastCallback : public b2RayCastCallback { public: - Reference *ref; - RayCastCallback(); + RayCastCallback(lua_State *L, int idx); ~RayCastCallback(); virtual float32 ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction); + private: + lua_State *L; + int funcidx; }; /** @@ -157,6 +163,13 @@ public: **/ int getCallbacks(lua_State *L); + /** + * Updates the Lua thread/coroutine used when callbacks are executed in + * the update method. This should be called in the same Lua function which + * calls update(). + **/ + void setCallbacksL(lua_State *L); + /** * Sets the ContactFilter callback. **/ @@ -281,8 +294,6 @@ private: // Contact callbacks. ContactCallback begin, end, presolve, postsolve; ContactFilter filter; - QueryCallback query; - RayCastCallback raycast; }; } // box2d diff --git a/jni/love/src/modules/physics/box2d/wrap_World.cpp b/jni/love/src/modules/physics/box2d/wrap_World.cpp index e88b9345..a0c86082 100644 --- a/jni/love/src/modules/physics/box2d/wrap_World.cpp +++ b/jni/love/src/modules/physics/box2d/wrap_World.cpp @@ -39,6 +39,8 @@ int w_World_update(lua_State *L) { World *t = luax_checkworld(L, 1); float dt = (float)luaL_checknumber(L, 2); + // Make sure the world callbacks are using the calling Lua thread. + t->setCallbacksL(L); luax_catchexcept(L, [&](){ t->update(dt); }); return 0; } diff --git a/jni/love/src/modules/sound/lullaby/ModPlugDecoder.h b/jni/love/src/modules/sound/lullaby/ModPlugDecoder.h index 155a9df7..58dd3742 100644 --- a/jni/love/src/modules/sound/lullaby/ModPlugDecoder.h +++ b/jni/love/src/modules/sound/lullaby/ModPlugDecoder.h @@ -29,8 +29,12 @@ #include "common/Data.h" #include "Decoder.h" -// SDL_sound +// libmodplug +#ifdef LOVE_ANDROID #include +#else +#include +#endif namespace love { diff --git a/jni/love/src/modules/sound/lullaby/VorbisDecoder.cpp b/jni/love/src/modules/sound/lullaby/VorbisDecoder.cpp index 7cff4aab..4c2ee13c 100644 --- a/jni/love/src/modules/sound/lullaby/VorbisDecoder.cpp +++ b/jni/love/src/modules/sound/lullaby/VorbisDecoder.cpp @@ -106,10 +106,12 @@ static int vorbisSeek(void *datasource /* ptr to the data that the vorbis files vorbisData->dataRead += (int)actualOffset; break; case SEEK_END: // Seek from the end of the file - vorbisData->dataRead = vorbisData->dataSize+1; + if (offset < 0) + vorbisData->dataRead = vorbisData->dataSize + offset; + else + vorbisData->dataRead = vorbisData->dataSize; break; default: - throw love::Exception("Unknown seek command in vorbisSeek"); break; }; diff --git a/jni/love/src/modules/system/System.cpp b/jni/love/src/modules/system/System.cpp index a7205cad..3cfeb236 100755 --- a/jni/love/src/modules/system/System.cpp +++ b/jni/love/src/modules/system/System.cpp @@ -104,7 +104,7 @@ bool System::openURL(const std::string &url) const #elif defined(LOVE_ANDROID) - return love::android::openURL (url); + return love::android::openURL(url); #elif defined(LOVE_LINUX) @@ -142,9 +142,12 @@ bool System::openURL(const std::string &url) const #endif } -void System::vibrate(double seconds) const { +void System::vibrate(double seconds) const +{ #ifdef LOVE_ANDROID - love::android::vibrate (seconds); + love::android::vibrate(seconds); +#else + LOVE_UNUSED(seconds); #endif } diff --git a/jni/love/src/modules/system/System.h b/jni/love/src/modules/system/System.h index 92ed8725..be850e27 100644 --- a/jni/love/src/modules/system/System.h +++ b/jni/love/src/modules/system/System.h @@ -103,7 +103,6 @@ public: * Vibrates for the specified amount of seconds. * * @param number of seconds to vibrate. - * */ virtual void vibrate(double seconds) const; diff --git a/jni/love/src/modules/system/wrap_System.cpp b/jni/love/src/modules/system/wrap_System.cpp index 1f0f138d..50586545 100644 --- a/jni/love/src/modules/system/wrap_System.cpp +++ b/jni/love/src/modules/system/wrap_System.cpp @@ -88,7 +88,7 @@ int w_openURL(lua_State *L) int w_vibrate(lua_State *L) { - double seconds = static_cast(luaL_checknumber(L, 1)); + double seconds = luaL_checknumber(L, 1); instance()->vibrate(seconds); return 0; } diff --git a/jni/love/src/modules/thread/LuaThread.cpp b/jni/love/src/modules/thread/LuaThread.cpp index edef4964..36a4e926 100644 --- a/jni/love/src/modules/thread/LuaThread.cpp +++ b/jni/love/src/modules/thread/LuaThread.cpp @@ -118,8 +118,8 @@ void LuaThread::onError() if (error.empty()) return; - event::Event *event = Module::getInstance(Module::M_EVENT); - if (!event) + auto eventmodule = Module::getInstance(Module::M_EVENT); + if (!eventmodule) return; Proxy p; @@ -136,7 +136,7 @@ void LuaThread::onError() for (const StrongRef &v : vargs) v->release(); - event->push(msg); + eventmodule->push(msg); msg->release(); } diff --git a/jni/love/src/modules/thread/threads.cpp b/jni/love/src/modules/thread/threads.cpp index 86bd1fcf..fc97bc0d 100644 --- a/jni/love/src/modules/thread/threads.cpp +++ b/jni/love/src/modules/thread/threads.cpp @@ -104,5 +104,20 @@ const char *Threadable::getThreadName() const return threadName.empty() ? nullptr : threadName.c_str(); } +MutexRef::MutexRef() + : mutex(newMutex()) +{ +} + +MutexRef::~MutexRef() +{ + delete mutex; +} + +MutexRef::operator Mutex*() const +{ + return mutex; +} + } // thread } // love diff --git a/jni/love/src/modules/thread/threads.h b/jni/love/src/modules/thread/threads.h index 87f434d5..85bdba39 100644 --- a/jni/love/src/modules/thread/threads.h +++ b/jni/love/src/modules/thread/threads.h @@ -96,6 +96,18 @@ protected: }; +class MutexRef +{ +public: + MutexRef(); + ~MutexRef(); + + operator Mutex*() const; + +private: + Mutex *mutex; +}; + Mutex *newMutex(); Conditional *newConditional(); Thread *newThread(Threadable *t); diff --git a/jni/love/src/modules/window/Window.cpp b/jni/love/src/modules/window/Window.cpp index c513bc4e..0131b3c2 100644 --- a/jni/love/src/modules/window/Window.cpp +++ b/jni/love/src/modules/window/Window.cpp @@ -26,12 +26,8 @@ namespace love namespace window { -Window *Window::singleton = nullptr; - Window::~Window() { - if (singleton == this) - singleton = nullptr; } void Window::swapBuffers() diff --git a/jni/love/src/modules/window/Window.h b/jni/love/src/modules/window/Window.h index e0738011..8c70b153 100644 --- a/jni/love/src/modules/window/Window.h +++ b/jni/love/src/modules/window/Window.h @@ -112,6 +112,8 @@ public: virtual bool setWindow(int width = 800, int height = 600, WindowSettings *settings = nullptr) = 0; virtual void getWindow(int &width, int &height, WindowSettings &settings) = 0; + virtual void close() = 0; + virtual bool setFullscreen(bool fullscreen, FullscreenType fstype) = 0; virtual bool setFullscreen(bool fullscreen) = 0; @@ -128,7 +130,7 @@ public: virtual void setPosition(int x, int y, int displayindex) = 0; virtual void getPosition(int &x, int &y, int &displayindex) = 0; - virtual bool isCreated() const = 0; + virtual bool isOpen() const = 0; virtual void setWindowTitle(const std::string &title) = 0; virtual const std::string &getWindowTitle() const = 0; @@ -173,9 +175,6 @@ public: virtual void requestAttention(bool continuous) = 0; - //virtual static Window *createSingleton() = 0; - // No virtual statics, of course, but you are supposed to implement this static. - static bool getConstant(const char *in, Setting &out); static bool getConstant(Setting in, const char *&out); @@ -185,10 +184,6 @@ public: static bool getConstant(const char *in, MessageBoxType &out); static bool getConstant(MessageBoxType in, const char *&out); -protected: - - static Window *singleton; - private: static StringMap::Entry settingEntries[]; diff --git a/jni/love/src/modules/window/sdl/Window.cpp b/jni/love/src/modules/window/sdl/Window.cpp index 7d94fe7b..cd04baa3 100644 --- a/jni/love/src/modules/window/sdl/Window.cpp +++ b/jni/love/src/modules/window/sdl/Window.cpp @@ -56,7 +56,7 @@ namespace sdl { Window::Window() - : created(false) + : open(false) , mouseGrabbed(false) , window(nullptr) , context(nullptr) @@ -69,17 +69,7 @@ Window::Window() Window::~Window() { - if (context) - { - graphics::Graphics *gfx = Module::getInstance(Module::M_GRAPHICS); - if (gfx != nullptr) - gfx->unSetMode(); - - SDL_GL_DeleteContext(context); - } - - if (window) - SDL_DestroyWindow(window); + close(); SDL_QuitSubSystem(SDL_INIT_VIDEO); } @@ -104,6 +94,13 @@ void Window::setGLFramebufferAttributes(int msaa, bool sRGB) #if !defined(LOVE_LINUX) SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0); #endif + +#if defined(LOVE_WINDOWS) + // Avoid the Microsoft OpenGL 1.1 software renderer on Windows. Apparently + // older Intel drivers like to use it as a fallback when requesting some + // unsupported framebuffer attribute values, rather than properly failing. + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); +#endif } void Window::setGLContextAttributes(const ContextAttribs &attribs) @@ -349,22 +346,12 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla } } - if (context) - { - SDL_GL_DeleteContext(context); - context = nullptr; - } - - if (window) - { - SDL_DestroyWindow(window); - SDL_FlushEvent(SDL_WINDOWEVENT); - window = nullptr; - } + close(); return false; } + open = true; return true; } @@ -448,33 +435,11 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.display); } - graphics::Graphics *gfx = Module::getInstance(Module::M_GRAPHICS); - if (gfx != nullptr) - gfx->unSetMode(); - - if (context) - { - SDL_GL_DeleteContext(context); - context = nullptr; - } - - if (window) - { - SDL_DestroyWindow(window); - window = nullptr; - - // The old window may have generated pending events which are no longer - // relevant. Destroy them all! - SDL_FlushEvent(SDL_WINDOWEVENT); - } - - created = false; + close(); if (!createWindowAndContext(x, y, width, height, sdlflags, f.msaa, f.sRGB)) return false; - created = true; - // Make sure the window keeps any previously set icon. setIcon(curMode.icon.get()); @@ -493,6 +458,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) updateSettings(f); + auto gfx = Module::getInstance(Module::M_GRAPHICS); if (gfx != nullptr) gfx->setMode(curMode.pixelwidth, curMode.pixelheight, curMode.settings.sRGB); @@ -513,7 +479,7 @@ bool Window::onSizeChanged(int width, int height) SDL_GL_GetDrawableSize(window, &curMode.pixelwidth, &curMode.pixelheight); - graphics::Graphics *gfx = Module::getInstance(Module::M_GRAPHICS); + auto gfx = Module::getInstance(Module::M_GRAPHICS); if (gfx != nullptr) gfx->setViewportSize(curMode.pixelwidth, curMode.pixelheight); @@ -601,6 +567,31 @@ void Window::getWindow(int &width, int &height, WindowSettings &settings) settings = curMode.settings; } +void Window::close() +{ + auto gfx = Module::getInstance(Module::M_GRAPHICS); + if (gfx != nullptr) + gfx->unSetMode(); + + if (context) + { + SDL_GL_DeleteContext(context); + context = nullptr; + } + + if (window) + { + SDL_DestroyWindow(window); + window = nullptr; + + // The old window may have generated pending events which are no longer + // relevant. Destroy them all! + SDL_FlushEvent(SDL_WINDOWEVENT); + } + + open = false; +} + bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype) { if (!window) @@ -639,7 +630,7 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype) updateSettings(newsettings); // Update the viewport size now instead of waiting for event polling. - graphics::Graphics *gfx = Module::getInstance(Module::M_GRAPHICS); + auto gfx = Module::getInstance(Module::M_GRAPHICS); if (gfx != nullptr) gfx->setViewportSize(curMode.pixelwidth, curMode.pixelheight); @@ -739,21 +730,22 @@ void Window::getPosition(int &x, int &y, int &displayindex) SDL_GetWindowPosition(window, &x, &y); - // SDL always reports 0, 0 for fullscreen windows. - if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN)) + // In SDL <= 2.0.3, fullscreen windows are always reported as 0,0. In every + // other case we need to convert the position from global coordinates to the + // monitor's coordinate space. + if (x != 0 || y != 0) { SDL_Rect displaybounds = {}; SDL_GetDisplayBounds(displayindex, &displaybounds); - // The position needs to be in the monitor's coordinate space. x -= displaybounds.x; y -= displaybounds.y; } } -bool Window::isCreated() const +bool Window::isOpen() const { - return created; + return open; } void Window::setWindowTitle(const std::string &title) @@ -1034,16 +1026,6 @@ void Window::requestAttention(bool continuous) // TODO: Linux? } -love::window::Window *Window::createSingleton() -{ - if (!singleton) - singleton = new Window(); - else - singleton->retain(); - - return singleton; -} - const char *Window::getName() const { return "love.window.sdl"; diff --git a/jni/love/src/modules/window/sdl/Window.h b/jni/love/src/modules/window/sdl/Window.h index 9f1eda42..d985f7b7 100644 --- a/jni/love/src/modules/window/sdl/Window.h +++ b/jni/love/src/modules/window/sdl/Window.h @@ -44,6 +44,8 @@ public: bool setWindow(int width = 800, int height = 600, WindowSettings *settings = nullptr); void getWindow(int &width, int &height, WindowSettings &settings); + void close(); + bool setFullscreen(bool fullscreen, FullscreenType fstype); bool setFullscreen(bool fullscreen); @@ -60,7 +62,7 @@ public: void setPosition(int x, int y, int displayindex); void getPosition(int &x, int &y, int &displayindex); - bool isCreated() const; + bool isOpen() const; void setWindowTitle(const std::string &title); const std::string &getWindowTitle() const; @@ -102,8 +104,6 @@ public: void requestAttention(bool continuous); - static love::window::Window *createSingleton(); - const char *getName() const; private: @@ -139,7 +139,7 @@ private: } curMode; - bool created; + bool open; bool mouseGrabbed; diff --git a/jni/love/src/modules/window/wrap_Window.cpp b/jni/love/src/modules/window/wrap_Window.cpp index 91406239..b32c0196 100644 --- a/jni/love/src/modules/window/wrap_Window.cpp +++ b/jni/love/src/modules/window/wrap_Window.cpp @@ -194,7 +194,7 @@ int w_getFullscreenModes(lua_State *L) { int displayindex = 0; if (!lua_isnoneornil(L, 1)) - displayindex = (int) luaL_checknumber(L, 1); + displayindex = (int) luaL_checknumber(L, 1) - 1; else { int x, y; @@ -260,18 +260,24 @@ int w_getFullscreen(lua_State *L) return 2; } -int w_isCreated(lua_State *L) +int w_isOpen(lua_State *L) { - luax_pushboolean(L, instance()->isCreated()); + luax_pushboolean(L, instance()->isOpen()); return 1; } +int w_close(lua_State * /*L*/) +{ + instance()->close(); + return 0; +} + int w_getDesktopDimensions(lua_State *L) { int width = 0, height = 0; int displayindex = 0; if (!lua_isnoneornil(L, 1)) - displayindex = (int) luaL_checknumber(L, 1); + displayindex = (int) luaL_checknumber(L, 1) - 1; else { int x, y; @@ -290,7 +296,7 @@ int w_setPosition(lua_State *L) int displayindex = 0; if (!lua_isnoneornil(L, 3)) - displayindex = (int) luaL_checknumber(L, 3); + displayindex = (int) luaL_checknumber(L, 3) - 1; else { int x_unused, y_unused; @@ -499,7 +505,9 @@ static const luaL_Reg functions[] = { "getFullscreenModes", w_getFullscreenModes }, { "setFullscreen", w_setFullscreen }, { "getFullscreen", w_getFullscreen }, - { "isCreated", w_isCreated }, + { "isOpen", w_isOpen }, + { "isCreated", w_isOpen }, // For compatibility with old error handlers... + { "close", w_close }, { "getDesktopDimensions", w_getDesktopDimensions }, { "setPosition", w_setPosition }, { "getPosition", w_getPosition }, @@ -522,8 +530,11 @@ static const luaL_Reg functions[] = extern "C" int luaopen_love_window(lua_State *L) { - Window *instance = nullptr; - luax_catchexcept(L, [&](){ instance = sdl::Window::createSingleton(); }); + Window *instance = instance(); + if (instance == nullptr) + luax_catchexcept(L, [&](){ instance = new love::window::sdl::Window(); }); + else + instance->retain(); WrappedModule w; w.module = instance; diff --git a/jni/love/src/modules/window/wrap_Window.h b/jni/love/src/modules/window/wrap_Window.h index 882be996..324f39d9 100644 --- a/jni/love/src/modules/window/wrap_Window.h +++ b/jni/love/src/modules/window/wrap_Window.h @@ -36,7 +36,8 @@ int w_getMode(lua_State *L); int w_getFullscreenModes(lua_State *L); int w_setFullscreen(lua_State *L); int w_getFullscreen(lua_State *L); -int w_isCreated(lua_State *L); +int w_isOpen(lua_State *L); +int w_close(lua_State *L); int w_getDesktopDimensions(lua_State *L); int w_setPosition(lua_State *L); int w_getPosition(lua_State *L); diff --git a/jni/love/src/scripts/boot.lua b/jni/love/src/scripts/boot.lua index 24c6824e..164a334d 100644 --- a/jni/love/src/scripts/boot.lua +++ b/jni/love/src/scripts/boot.lua @@ -567,7 +567,7 @@ function love.errhand(msg) return end - if not love.graphics.isCreated() or not love.window.isCreated() then + if not love.graphics.isCreated() or not love.window.isOpen() then local success, status = pcall(love.window.setMode, 800, 600) if not success or not status then return diff --git a/jni/love/src/scripts/boot.lua.h b/jni/love/src/scripts/boot.lua.h index 71aaf9b0..28a238e8 100644 --- a/jni/love/src/scripts/boot.lua.h +++ b/jni/love/src/scripts/boot.lua.h @@ -1034,7 +1034,7 @@ const unsigned char boot_lua[] = 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x69, 0x73, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x28, 0x29, 0x20, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x69, - 0x73, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x38,