Using latest love-android branch (9a508a54700d)

This commit is contained in:
fysx
2015-08-13 11:12:17 +02:00
parent c91dbb6957
commit f3dd769e98
63 changed files with 738 additions and 556 deletions
+1
View File
@@ -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
+14 -26
View File
@@ -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
+7 -26
View File
@@ -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;
+1 -1
View File
@@ -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 -10
View File
@@ -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;
+18 -1
View File
@@ -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);
}
+2 -12
View File
@@ -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
View File
@@ -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.
+1 -3
View File
@@ -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
}
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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
-6
View File
@@ -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)
+1 -2
View File
@@ -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
+11 -13
View File
@@ -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!");
+1 -1
View File
@@ -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;
+138 -70
View File
@@ -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)
+12 -9
View File
@@ -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.
-2
View File
@@ -30,12 +30,10 @@ namespace image
ImageData::ImageData()
: data(nullptr)
{
mutex = thread::newMutex();
}
ImageData::~ImageData()
{
delete mutex;
}
size_t ImageData::getSize() const
+3 -3
View File
@@ -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:
+25 -19
View File
@@ -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},
-1
View File
@@ -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;
+3 -1
View File
@@ -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.
+5 -5
View File
@@ -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
+1 -9
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -47,7 +47,7 @@ namespace box2d
struct fixtureudata
{
// Reference to arbitrary data.
Reference *ref;
Reference *ref = nullptr;
};
/**
+1 -9
View File
@@ -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;
+1 -1
View File
@@ -46,7 +46,7 @@ class World;
struct jointudata
{
// Reference to arbitrary data.
Reference *ref;
Reference *ref = nullptr;
};
/**
+43 -32
View File
@@ -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;
}
+17 -6
View File
@@ -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;
};
+6 -3
View File
@@ -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
}
-1
View File
@@ -103,7 +103,6 @@ public:
* Vibrates for the specified amount of seconds.
*
* @param number of seconds to vibrate.
*
*/
virtual void vibrate(double seconds) const;
+1 -1
View File
@@ -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;
}
+3 -3
View File
@@ -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();
}
+15
View File
@@ -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
+12
View File
@@ -96,6 +96,18 @@ protected:
};
class MutexRef
{
public:
MutexRef();
~MutexRef();
operator Mutex*() const;
private:
Mutex *mutex;
};
Mutex *newMutex();
Conditional *newConditional();
Thread *newThread(Threadable *t);
-4
View File
@@ -26,12 +26,8 @@ namespace love
namespace window
{
Window *Window::singleton = nullptr;
Window::~Window()
{
if (singleton == this)
singleton = nullptr;
}
void Window::swapBuffers()
+3 -8
View File
@@ -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[];
+46 -64
View File
@@ -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";
+4 -4
View File
@@ -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;
+19 -8
View File
@@ -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;
+2 -1
View File
@@ -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);
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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,