mirror of
https://github.com/love2d/love-android.git
synced 2026-08-12 00:20:57 +02:00
Using latest love-android branch (9a508a54700d)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#include <string.h>
|
||||
#include "glad.hpp"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define GLAD_USE_SDL
|
||||
|
||||
namespace glad {
|
||||
|
||||
|
||||
#ifdef GLAD_USE_SDL
|
||||
#include <SDL.h>
|
||||
#if !SDL_VERSION_ATLEAST(2,0,0)
|
||||
@@ -15,17 +13,9 @@ namespace glad {
|
||||
#include <assert.h>
|
||||
#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);
|
||||
|
||||
+27
-27
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ private:
|
||||
volatile bool finish;
|
||||
|
||||
// finish lock
|
||||
thread::Mutex *mutex;
|
||||
love::thread::MutexRef mutex;
|
||||
|
||||
public:
|
||||
PoolThread(Pool *pool);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<Message *> queue;
|
||||
|
||||
}; // Event
|
||||
|
||||
@@ -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<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(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<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(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<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
@@ -153,7 +153,6 @@ Message *Event::convert(const SDL_Event &e) const
|
||||
std::vector<StrongRef<Variant>> 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<love::keyboard::Keyboard>(Module::M_KEYBOARD);
|
||||
auto kb = Module::getInstance<love::keyboard::Keyboard>(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<joystick::JoystickModule>(Module::M_JOYSTICK);
|
||||
auto joymodule = Module::getInstance<joystick::JoystickModule>(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<audio::Audio>(Module::M_AUDIO);
|
||||
auto audio = Module::getInstance<audio::Audio>(Module::M_AUDIO);
|
||||
if (audio)
|
||||
audio->pause();
|
||||
}
|
||||
break;
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
{
|
||||
audio::Audio *audio = Module::getInstance<audio::Audio>(Module::M_AUDIO);
|
||||
auto audio = Module::getInstance<audio::Audio>(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<SDL_Keycode, love::keyboard::Keyboard::Key> 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;
|
||||
|
||||
@@ -50,10 +50,10 @@
|
||||
# include "common/iOS.h"
|
||||
#endif
|
||||
|
||||
#include "SDL.h"
|
||||
#include <string>
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
#include <SDL.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
@@ -190,8 +190,8 @@ void BMFontRasterizer::parseConfig(const std::string &configtext)
|
||||
{
|
||||
using namespace love::filesystem;
|
||||
|
||||
Filesystem *filesystem = Module::getInstance<Filesystem>(Module::M_FILESYSTEM);
|
||||
image::Image *imagemodule = Module::getInstance<image::Image>(Module::M_IMAGE);
|
||||
auto filesystem = Module::getInstance<Filesystem>(Module::M_FILESYSTEM);
|
||||
auto imagemodule = Module::getInstance<image::Image>(Module::M_IMAGE);
|
||||
|
||||
if (!filesystem)
|
||||
throw love::Exception("Filesystem module not loaded!");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<love::window::Window>(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<font::Font>(M_FONT);
|
||||
auto fontmodule = Module::getInstance<font::Font>(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<love::window::Window>(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<bool> &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<love::image::ImageData *> &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<love::image::CompressedImageData *> &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<float>(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.
|
||||
|
||||
@@ -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<love::image::ImageData *> &data, const Image::Flags &flags);
|
||||
Image *newImage(const std::vector<love::image::CompressedImageData *> &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<love::window::Window> currentWindow;
|
||||
|
||||
StrongRef<Font> defaultFont;
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "common/int.h"
|
||||
|
||||
// STD
|
||||
#include <cstring> // For memcpy
|
||||
#include <algorithm> // 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 <typename T>
|
||||
static bool verifyMipmapLevels(const std::vector<T> &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<love::image::ImageData *> &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<love::image::CompressedImageData *> &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<StrongRef<love::image::ImageData>> &Image::getImageData() const
|
||||
{
|
||||
return data.get();
|
||||
return data;
|
||||
}
|
||||
|
||||
love::image::CompressedImageData *Image::getCompressedData() const
|
||||
const std::vector<StrongRef<love::image::CompressedImageData>> &Image::getCompressedData() const
|
||||
{
|
||||
return cdata.get();
|
||||
return cdata;
|
||||
}
|
||||
|
||||
void Image::setFilter(const Texture::Filter &f)
|
||||
|
||||
@@ -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<love::image::ImageData *> &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<love::image::CompressedImageData *> &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<StrongRef<love::image::ImageData>> &getImageData() const;
|
||||
const std::vector<StrongRef<love::image::CompressedImageData>> &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<love::image::ImageData> data;
|
||||
// Each element in the array is a mipmap level.
|
||||
std::vector<StrongRef<love::image::ImageData>> 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<love::image::CompressedImageData> cdata;
|
||||
// empty if raw ImageData was used to create the texture.
|
||||
std::vector<StrongRef<love::image::CompressedImageData>> cdata;
|
||||
|
||||
// OpenGL texture identifier.
|
||||
GLuint texture;
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
#include <SDL_syswm.h>
|
||||
#endif
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
#include <dlfcn.h>
|
||||
#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();
|
||||
|
||||
@@ -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<Graphics>(Module::M_GRAPHICS);
|
||||
auto graphics = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
|
||||
if (graphics)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "image/Image.h"
|
||||
#include "font/Rasterizer.h"
|
||||
#include "filesystem/wrap_Filesystem.h"
|
||||
#include "image/wrap_Image.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
@@ -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<love::image::ImageData *> data;
|
||||
std::vector<love::image::CompressedImageData *> 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<love::image::Image>(Module::M_IMAGE);
|
||||
if (image == nullptr)
|
||||
auto imagemodule = Module::getInstance<love::image::Image>(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<love::image::CompressedImageData>(L, 1, IMAGE_COMPRESSED_IMAGE_DATA_ID);
|
||||
cdata.push_back(love::image::luax_checkcompressedimagedata(L, 1));
|
||||
else
|
||||
data = luax_checktype<love::image::ImageData>(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<Image>(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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -39,19 +39,17 @@ SpriteBatch *luax_checkspritebatch(lua_State *L, int idx)
|
||||
return luax_checktype<SpriteBatch>(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<Quad>(L, 2, GRAPHICS_QUAD_ID);
|
||||
startidx = 3;
|
||||
quad = luax_totype<Quad>(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<Quad>(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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -30,12 +30,10 @@ namespace image
|
||||
ImageData::ImageData()
|
||||
: data(nullptr)
|
||||
{
|
||||
mutex = thread::newMutex();
|
||||
}
|
||||
|
||||
ImageData::~ImageData()
|
||||
{
|
||||
delete mutex;
|
||||
}
|
||||
|
||||
size_t ImageData::getSize() const
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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<love::filesystem::File>(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)
|
||||
|
||||
@@ -188,7 +188,6 @@ StringMap<Keyboard::Key, Keyboard::KEY_MAX_ENUM>::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},
|
||||
|
||||
@@ -181,7 +181,6 @@ public:
|
||||
KEY_EXECUTE,
|
||||
KEY_HELP,
|
||||
KEY_MENU,
|
||||
KEY_SEARCH,
|
||||
KEY_SELECT,
|
||||
KEY_STOP,
|
||||
KEY_AGAIN,
|
||||
|
||||
@@ -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<window::Window>(M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(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<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(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<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(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<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
if (window)
|
||||
window->setMouseGrab(grab);
|
||||
}
|
||||
|
||||
bool Mouse::isGrabbed() const
|
||||
{
|
||||
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
if (window)
|
||||
return window->isMouseGrabbed();
|
||||
else
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -48,7 +48,7 @@ class Fixture;
|
||||
struct bodyudata
|
||||
{
|
||||
// Reference to arbitrary data.
|
||||
Reference *ref;
|
||||
Reference *ref = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace box2d
|
||||
struct fixtureudata
|
||||
{
|
||||
// Reference to arbitrary data.
|
||||
Reference *ref;
|
||||
Reference *ref = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -46,7 +46,7 @@ class World;
|
||||
struct jointudata
|
||||
{
|
||||
// Reference to arbitrary data.
|
||||
Reference *ref;
|
||||
Reference *ref = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,12 @@
|
||||
#include "common/Data.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
// SDL_sound
|
||||
// libmodplug
|
||||
#ifdef LOVE_ANDROID
|
||||
#include <modplug.h>
|
||||
#else
|
||||
#include <libmodplug/modplug.h>
|
||||
#endif
|
||||
|
||||
namespace love
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,6 @@ public:
|
||||
* Vibrates for the specified amount of seconds.
|
||||
*
|
||||
* @param number of seconds to vibrate.
|
||||
*
|
||||
*/
|
||||
virtual void vibrate(double seconds) const;
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ int w_openURL(lua_State *L)
|
||||
|
||||
int w_vibrate(lua_State *L)
|
||||
{
|
||||
double seconds = static_cast<double>(luaL_checknumber(L, 1));
|
||||
double seconds = luaL_checknumber(L, 1);
|
||||
instance()->vibrate(seconds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -118,8 +118,8 @@ void LuaThread::onError()
|
||||
if (error.empty())
|
||||
return;
|
||||
|
||||
event::Event *event = Module::getInstance<event::Event>(Module::M_EVENT);
|
||||
if (!event)
|
||||
auto eventmodule = Module::getInstance<event::Event>(Module::M_EVENT);
|
||||
if (!eventmodule)
|
||||
return;
|
||||
|
||||
Proxy p;
|
||||
@@ -136,7 +136,7 @@ void LuaThread::onError()
|
||||
for (const StrongRef<Variant> &v : vargs)
|
||||
v->release();
|
||||
|
||||
event->push(msg);
|
||||
eventmodule->push(msg);
|
||||
msg->release();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -96,6 +96,18 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
class MutexRef
|
||||
{
|
||||
public:
|
||||
MutexRef();
|
||||
~MutexRef();
|
||||
|
||||
operator Mutex*() const;
|
||||
|
||||
private:
|
||||
Mutex *mutex;
|
||||
};
|
||||
|
||||
Mutex *newMutex();
|
||||
Conditional *newConditional();
|
||||
Thread *newThread(Threadable *t);
|
||||
|
||||
@@ -26,12 +26,8 @@ namespace love
|
||||
namespace window
|
||||
{
|
||||
|
||||
Window *Window::singleton = nullptr;
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
if (singleton == this)
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
void Window::swapBuffers()
|
||||
|
||||
@@ -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<Setting, SETTING_MAX_ENUM>::Entry settingEntries[];
|
||||
|
||||
@@ -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<graphics::Graphics>(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<graphics::Graphics>(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<graphics::Graphics>(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<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(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<graphics::Graphics>(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<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user