Browse Source

Updated LÖVE source to changeset da332813bc05

Martin Felis 10 years ago
parent
commit
08d51fbf28
38 changed files with 836 additions and 448 deletions
  1. 5 1
      jni/love/src/common/runtime.cpp
  2. 17 17
      jni/love/src/modules/event/wrap_Event.cpp
  3. 12 6
      jni/love/src/modules/filesystem/Filesystem.h
  4. 8 0
      jni/love/src/modules/filesystem/physfs/File.cpp
  5. 7 0
      jni/love/src/modules/filesystem/physfs/File.h
  6. 46 7
      jni/love/src/modules/filesystem/physfs/Filesystem.cpp
  7. 2 1
      jni/love/src/modules/filesystem/physfs/Filesystem.h
  8. 16 8
      jni/love/src/modules/filesystem/wrap_Filesystem.cpp
  9. 67 4
      jni/love/src/modules/graphics/Graphics.cpp
  10. 48 0
      jni/love/src/modules/graphics/Graphics.h
  11. 1 1
      jni/love/src/modules/graphics/opengl/Canvas.cpp
  12. 16 4
      jni/love/src/modules/graphics/opengl/Font.cpp
  13. 1 1
      jni/love/src/modules/graphics/opengl/Font.h
  14. 28 10
      jni/love/src/modules/graphics/opengl/GLBuffer.cpp
  15. 17 0
      jni/love/src/modules/graphics/opengl/GLBuffer.h
  16. 109 39
      jni/love/src/modules/graphics/opengl/Graphics.cpp
  17. 10 8
      jni/love/src/modules/graphics/opengl/Graphics.h
  18. 8 5
      jni/love/src/modules/graphics/opengl/ParticleSystem.cpp
  19. 1 1
      jni/love/src/modules/graphics/opengl/Text.cpp
  20. 21 21
      jni/love/src/modules/graphics/opengl/Video.cpp
  21. 56 19
      jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp
  22. 1 1
      jni/love/src/modules/graphics/opengl/wrap_Graphics.lua
  23. 18 4
      jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp
  24. 1 0
      jni/love/src/modules/graphics/opengl/wrap_Mesh.h
  25. 1 12
      jni/love/src/modules/graphics/opengl/wrap_Video.lua
  26. 2 49
      jni/love/src/modules/math/MathModule.h
  27. 7 44
      jni/love/src/modules/math/wrap_Math.cpp
  28. 33 6
      jni/love/src/modules/math/wrap_Math.lua
  29. 46 30
      jni/love/src/modules/math/wrap_RandomGenerator.cpp
  30. 0 1
      jni/love/src/modules/math/wrap_RandomGenerator.h
  31. 80 0
      jni/love/src/modules/math/wrap_RandomGenerator.lua
  32. 1 1
      jni/love/src/modules/sound/lullaby/ModPlugDecoder.h
  33. 113 122
      jni/love/src/modules/window/sdl/Window.cpp
  34. 6 10
      jni/love/src/modules/window/sdl/Window.h
  35. 4 4
      jni/love/src/scripts/boot.lua
  36. 11 9
      jni/love/src/scripts/boot.lua.h
  37. 7 1
      jni/love/src/scripts/nogame.lua
  38. 9 1
      jni/love/src/scripts/nogame.lua.h

+ 5 - 1
jni/love/src/common/runtime.cpp

@@ -465,7 +465,11 @@ bool luax_istype(lua_State *L, int idx, love::Type type)
 		return false;
 		return false;
 
 
 	Proxy *p = (Proxy *) lua_touserdata(L, idx);
 	Proxy *p = (Proxy *) lua_touserdata(L, idx);
-	return typeFlags[p->type][type];
+
+	if (p->type > INVALID_ID && p->type < TYPE_MAX_ENUM)
+		return typeFlags[p->type][type];
+	else
+		return false;
 }
 }
 
 
 int luax_getfunction(lua_State *L, const char *mod, const char *fn)
 int luax_getfunction(lua_State *L, const char *mod, const char *fn)

+ 17 - 17
jni/love/src/modules/event/wrap_Event.cpp

@@ -32,11 +32,11 @@ namespace event
 
 
 #define instance() (Module::getInstance<Event>(Module::M_EVENT))
 #define instance() (Module::getInstance<Event>(Module::M_EVENT))
 
 
-static int poll_i(lua_State *L)
+static int w_poll_i(lua_State *L)
 {
 {
-	Message *m;
+	Message *m = nullptr;
 
 
-	while (instance()->poll(m))
+	if (instance()->poll(m))
 	{
 	{
 		int args = m->toLua(L);
 		int args = m->toLua(L);
 		m->release();
 		m->release();
@@ -47,23 +47,22 @@ static int poll_i(lua_State *L)
 	return 0;
 	return 0;
 }
 }
 
 
-int w_pump(lua_State *)
+int w_poll(lua_State *L)
 {
 {
-	instance()->pump();
-	return 0;
+	lua_pushcclosure(L, &w_poll_i, 0);
+	return 1;
 }
 }
 
 
-int w_poll(lua_State *L)
+int w_pump(lua_State *)
 {
 {
-	lua_pushcclosure(L, &poll_i, 0);
-	return 1;
+	instance()->pump();
+	return 0;
 }
 }
 
 
 int w_wait(lua_State *L)
 int w_wait(lua_State *L)
 {
 {
-	Message *m;
-
-	if ((m = instance()->wait()))
+	Message *m = instance()->wait();
+	if (m)
 	{
 	{
 		int args = m->toLua(L);
 		int args = m->toLua(L);
 		m->release();
 		m->release();
@@ -75,12 +74,11 @@ int w_wait(lua_State *L)
 
 
 int w_push(lua_State *L)
 int w_push(lua_State *L)
 {
 {
-	Message *m;
+	Message *m = Message::fromLua(L, 1);
 
 
-	bool success = (m = Message::fromLua(L, 1)) != NULL;
-	luax_pushboolean(L, success);
+	luax_pushboolean(L, m != nullptr);
 
 
-	if (!success)
+	if (m == nullptr)
 		return 1;
 		return 1;
 
 
 	instance()->push(m);
 	instance()->push(m);
@@ -143,7 +141,9 @@ extern "C" int luaopen_love_event(lua_State *L)
 	w.functions = functions;
 	w.functions = functions;
 	w.types = nullptr;
 	w.types = nullptr;
 
 
-	return luax_register_module(L, w);
+	int ret = luax_register_module(L, w);
+
+	return ret;
 }
 }
 
 
 } // event
 } // event

+ 12 - 6
jni/love/src/modules/filesystem/Filesystem.h

@@ -156,6 +156,12 @@ public:
 	 **/
 	 **/
 	virtual std::string getRealDirectory(const char *filename) const = 0;
 	virtual std::string getRealDirectory(const char *filename) const = 0;
 
 
+	/**
+	 * Checks if a path exists.
+	 * @param path The path to check.
+	 **/
+	virtual bool exists(const char *path) const = 0;
+
 	/**
 	/**
 	 * Checks if a path is a directory.
 	 * Checks if a path is a directory.
 	 * @param dir The directory name to check.
 	 * @param dir The directory name to check.
@@ -168,6 +174,12 @@ public:
 	 **/
 	 **/
 	virtual bool isFile(const char *file) const = 0;
 	virtual bool isFile(const char *file) const = 0;
 
 
+	/**
+	 * Gets whether a filepath is actually a symlink.
+	 * Always returns false if symlinks are not enabled.
+	 **/
+	virtual bool isSymlink(const char *filename) const = 0;
+
 	/**
 	/**
 	 * Creates a directory. Write dir must be set.
 	 * Creates a directory. Write dir must be set.
 	 * @param dir The directory to create.
 	 * @param dir The directory to create.
@@ -232,12 +244,6 @@ public:
 	 **/
 	 **/
 	virtual bool areSymlinksEnabled() const = 0;
 	virtual bool areSymlinksEnabled() const = 0;
 
 
-	/**
-	 * Gets whether a filepath is actually a symlink.
-	 * Always returns false if symlinks are not enabled.
-	 **/
-	virtual bool isSymlink(const char *filename) const = 0;
-
 	// Require path accessors
 	// Require path accessors
 	// Not const because it's R/W
 	// Not const because it's R/W
 	virtual std::vector<std::string> &getRequirePath() = 0;
 	virtual std::vector<std::string> &getRequirePath() = 0;

+ 8 - 0
jni/love/src/modules/filesystem/physfs/File.cpp

@@ -154,7 +154,11 @@ int64 File::read(void *dst, int64 size)
 	if (size < 0)
 	if (size < 0)
 		throw love::Exception("Invalid read size.");
 		throw love::Exception("Invalid read size.");
 
 
+#ifdef LOVE_USE_PHYSFS_2_1
+	int64 read = PHYSFS_readBytes(file, dst, (PHYSFS_uint64) size);
+#else
 	int64 read = (int64)PHYSFS_read(file, dst, 1, (PHYSFS_uint32) size);
 	int64 read = (int64)PHYSFS_read(file, dst, 1, (PHYSFS_uint32) size);
+#endif
 
 
 	return read;
 	return read;
 }
 }
@@ -171,7 +175,11 @@ bool File::write(const void *data, int64 size)
 		throw love::Exception("Invalid write size.");
 		throw love::Exception("Invalid write size.");
 
 
 	// Try to write.
 	// Try to write.
+#ifdef LOVE_USE_PHYSFS_2_1
+	int64 written = PHYSFS_writeBytes(file, data, (PHYSFS_uint64) size);
+#else
 	int64 written = (int64) PHYSFS_write(file, data, 1, (PHYSFS_uint32) size);
 	int64 written = (int64) PHYSFS_write(file, data, 1, (PHYSFS_uint32) size);
+#endif
 
 
 	// Check that correct amount of data was written.
 	// Check that correct amount of data was written.
 	if (written != size)
 	if (written != size)

+ 7 - 0
jni/love/src/modules/filesystem/physfs/File.h

@@ -22,6 +22,7 @@
 #define LOVE_FILESYSTEM_PHYSFS_FILE_H
 #define LOVE_FILESYSTEM_PHYSFS_FILE_H
 
 
 // LOVE
 // LOVE
+#include "common/config.h"
 #include "filesystem/File.h"
 #include "filesystem/File.h"
 
 
 // PhysFS
 // PhysFS
@@ -34,6 +35,12 @@
 // STD
 // STD
 #include <string>
 #include <string>
 
 
+// These platforms always use PhysFS 2.1.
+#if (defined(LOVE_IOS) || defined(LOVE_ANDROID)) \
+&& (PHYSFS_VER_MAJOR == 2 && PHYSFS_VER_MINOR >= 1)
+#define LOVE_USE_PHYSFS_2_1
+#endif
+
 namespace love
 namespace love
 {
 {
 namespace filesystem
 namespace filesystem

+ 46 - 7
jni/love/src/modules/filesystem/physfs/Filesystem.cpp

@@ -188,7 +188,13 @@ bool Filesystem::setIdentity(const char *ident, bool appendToPath)
 	// We don't want old read-only save paths to accumulate when we set a new
 	// We don't want old read-only save paths to accumulate when we set a new
 	// identity.
 	// identity.
 	if (!old_save_path.empty())
 	if (!old_save_path.empty())
+	{
+#ifdef LOVE_USE_PHYSFS_2_1
+		PHYSFS_unmount(old_save_path.c_str());
+#else
 		PHYSFS_removeFromSearchPath(old_save_path.c_str());
 		PHYSFS_removeFromSearchPath(old_save_path.c_str());
+#endif
+	}
 
 
 	// Try to add the save directory to the search path.
 	// Try to add the save directory to the search path.
 	// (No error on fail, it means that the path doesn't exist).
 	// (No error on fail, it means that the path doesn't exist).
@@ -433,7 +439,11 @@ bool Filesystem::unmount(const char *archive)
 	if (!mountPoint)
 	if (!mountPoint)
 		return false;
 		return false;
 
 
+#ifdef LOVE_USE_PHYSFS_2_1
+	return PHYSFS_unmount(realPath.c_str()) != 0;
+#else
 	return PHYSFS_removeFromSearchPath(realPath.c_str()) != 0;
 	return PHYSFS_removeFromSearchPath(realPath.c_str()) != 0;
+#endif
 }
 }
 
 
 love::filesystem::File *Filesystem::newFile(const char *filename) const
 love::filesystem::File *Filesystem::newFile(const char *filename) const
@@ -569,14 +579,40 @@ std::string Filesystem::getRealDirectory(const char *filename) const
 	return std::string(dir);
 	return std::string(dir);
 }
 }
 
 
+bool Filesystem::exists(const char *path) const
+{
+	return PHYSFS_exists(path) != 0;
+}
+
 bool Filesystem::isDirectory(const char *dir) const
 bool Filesystem::isDirectory(const char *dir) const
 {
 {
+#ifdef LOVE_USE_PHYSFS_2_1
+	PHYSFS_Stat stat = {};
+	if (PHYSFS_stat(dir, &stat))
+		return stat.filetype == PHYSFS_FILETYPE_DIRECTORY;
+	else
+		return false;
+#else
 	return PHYSFS_isDirectory(dir) != 0;
 	return PHYSFS_isDirectory(dir) != 0;
+#endif
 }
 }
 
 
 bool Filesystem::isFile(const char *file) const
 bool Filesystem::isFile(const char *file) const
 {
 {
-	return PHYSFS_exists(file) && !PHYSFS_isDirectory(file);
+	return PHYSFS_exists(file) && !isDirectory(file);
+}
+
+bool Filesystem::isSymlink(const char *filename) const
+{
+#ifdef LOVE_USE_PHYSFS_2_1
+	PHYSFS_Stat stat = {};
+	if (PHYSFS_stat(filename, &stat))
+		return stat.filetype == PHYSFS_FILETYPE_SYMLINK;
+	else
+		return false;
+#else
+	return PHYSFS_isSymbolicLink(filename) != 0;
+#endif
 }
 }
 
 
 bool Filesystem::createDirectory(const char *dir)
 bool Filesystem::createDirectory(const char *dir)
@@ -643,7 +679,15 @@ void Filesystem::getDirectoryItems(const char *dir, std::vector<std::string> &it
 
 
 int64 Filesystem::getLastModified(const char *filename) const
 int64 Filesystem::getLastModified(const char *filename) const
 {
 {
-	PHYSFS_sint64 time = PHYSFS_getLastModTime(filename);
+	PHYSFS_sint64 time = -1;
+
+#ifdef LOVE_USE_PHYSFS_2_1
+	PHYSFS_Stat stat = {};
+	if (PHYSFS_stat(filename, &stat))
+		time = stat.modtime;
+#else
+	time = PHYSFS_getLastModTime(filename);
+#endif
 
 
 	if (time == -1)
 	if (time == -1)
 		throw love::Exception("Could not determine file modification date.");
 		throw love::Exception("Could not determine file modification date.");
@@ -679,11 +723,6 @@ bool Filesystem::areSymlinksEnabled() const
 	return PHYSFS_symbolicLinksPermitted() != 0;
 	return PHYSFS_symbolicLinksPermitted() != 0;
 }
 }
 
 
-bool Filesystem::isSymlink(const char *filename) const
-{
-	return PHYSFS_isSymbolicLink(filename) != 0;
-}
-
 std::vector<std::string> &Filesystem::getRequirePath()
 std::vector<std::string> &Filesystem::getRequirePath()
 {
 {
 	return requirePath;
 	return requirePath;

+ 2 - 1
jni/love/src/modules/filesystem/physfs/Filesystem.h

@@ -75,8 +75,10 @@ public:
 
 
 	std::string getRealDirectory(const char *filename) const;
 	std::string getRealDirectory(const char *filename) const;
 
 
+	bool exists(const char *path) const;
 	bool isDirectory(const char *dir) const;
 	bool isDirectory(const char *dir) const;
 	bool isFile(const char *file) const;
 	bool isFile(const char *file) const;
+	bool isSymlink(const char *filename) const;
 
 
 	bool createDirectory(const char *dir);
 	bool createDirectory(const char *dir);
 
 
@@ -93,7 +95,6 @@ public:
 
 
 	void setSymlinksEnabled(bool enable);
 	void setSymlinksEnabled(bool enable);
 	bool areSymlinksEnabled() const;
 	bool areSymlinksEnabled() const;
-	bool isSymlink(const char *filename) const;
 
 
 	std::vector<std::string> &getRequirePath();
 	std::vector<std::string> &getRequirePath();
 
 

+ 16 - 8
jni/love/src/modules/filesystem/wrap_Filesystem.cpp

@@ -316,6 +316,13 @@ int w_getExecutablePath(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
+int w_exists(lua_State *L)
+{
+	const char *arg = luaL_checkstring(L, 1);
+	luax_pushboolean(L, instance()->exists(arg));
+	return 1;
+}
+
 int w_isDirectory(lua_State *L)
 int w_isDirectory(lua_State *L)
 {
 {
 	const char *arg = luaL_checkstring(L, 1);
 	const char *arg = luaL_checkstring(L, 1);
@@ -330,6 +337,13 @@ int w_isFile(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
+int w_isSymlink(lua_State *L)
+{
+	const char *filename = luaL_checkstring(L, 1);
+	luax_pushboolean(L, instance()->isSymlink(filename));
+	return 1;
+}
+
 int w_createDirectory(lua_State *L)
 int w_createDirectory(lua_State *L)
 {
 {
 	const char *arg = luaL_checkstring(L, 1);
 	const char *arg = luaL_checkstring(L, 1);
@@ -551,13 +565,6 @@ int w_areSymlinksEnabled(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_isSymlink(lua_State *L)
-{
-	const char *filename = luaL_checkstring(L, 1);
-	luax_pushboolean(L, instance()->isSymlink(filename));
-	return 1;
-}
-
 int w_getRequirePath(lua_State *L)
 int w_getRequirePath(lua_State *L)
 {
 {
 	std::stringstream path;
 	std::stringstream path;
@@ -715,8 +722,10 @@ static const luaL_Reg functions[] =
 	{ "getSourceBaseDirectory", w_getSourceBaseDirectory },
 	{ "getSourceBaseDirectory", w_getSourceBaseDirectory },
 	{ "getRealDirectory", w_getRealDirectory },
 	{ "getRealDirectory", w_getRealDirectory },
 	{ "getExecutablePath", w_getExecutablePath },
 	{ "getExecutablePath", w_getExecutablePath },
+	{ "exists", w_exists },
 	{ "isDirectory", w_isDirectory },
 	{ "isDirectory", w_isDirectory },
 	{ "isFile", w_isFile },
 	{ "isFile", w_isFile },
+	{ "isSymlink", w_isSymlink },
 	{ "createDirectory", w_createDirectory },
 	{ "createDirectory", w_createDirectory },
 	{ "remove", w_remove },
 	{ "remove", w_remove },
 	{ "read", w_read },
 	{ "read", w_read },
@@ -729,7 +738,6 @@ static const luaL_Reg functions[] =
 	{ "getSize", w_getSize },
 	{ "getSize", w_getSize },
 	{ "setSymlinksEnabled", w_setSymlinksEnabled },
 	{ "setSymlinksEnabled", w_setSymlinksEnabled },
 	{ "areSymlinksEnabled", w_areSymlinksEnabled },
 	{ "areSymlinksEnabled", w_areSymlinksEnabled },
-	{ "isSymlink", w_isSymlink },
 	{ "newFileData", w_newFileData },
 	{ "newFileData", w_newFileData },
 	{ "getRequirePath", w_getRequirePath },
 	{ "getRequirePath", w_getRequirePath },
 	{ "setRequirePath", w_setRequirePath },
 	{ "setRequirePath", w_setRequirePath },

+ 67 - 4
jni/love/src/modules/graphics/Graphics.cpp

@@ -82,6 +82,16 @@ bool Graphics::getConstant(BlendMode in, const char *&out)
 	return blendModes.find(in, out);
 	return blendModes.find(in, out);
 }
 }
 
 
+bool Graphics::getConstant(const char *in, BlendAlpha &out)
+{
+	return blendAlphaModes.find(in, out);
+}
+
+bool Graphics::getConstant(BlendAlpha in, const char *&out)
+{
+	return blendAlphaModes.find(in, out);
+}
+
 bool Graphics::getConstant(const char *in, LineStyle &out)
 bool Graphics::getConstant(const char *in, LineStyle &out)
 {
 {
 	return lineStyles.find(in, out);
 	return lineStyles.find(in, out);
@@ -102,6 +112,26 @@ bool Graphics::getConstant(LineJoin in, const char *&out)
 	return lineJoins.find(in, out);
 	return lineJoins.find(in, out);
 }
 }
 
 
+bool Graphics::getConstant(const char *in, StencilAction &out)
+{
+	return stencilActions.find(in, out);
+}
+
+bool Graphics::getConstant(StencilAction in, const char *&out)
+{
+	return stencilActions.find(in, out);
+}
+
+bool Graphics::getConstant(const char *in, CompareMode &out)
+{
+	return compareModes.find(in, out);
+}
+
+bool Graphics::getConstant(CompareMode in, const char *&out)
+{
+	return compareModes.find(in, out);
+}
+
 bool Graphics::getConstant(const char *in, Support &out)
 bool Graphics::getConstant(const char *in, Support &out)
 {
 {
 	return support.find(in, out);
 	return support.find(in, out);
@@ -152,16 +182,24 @@ StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM> Graphics::drawModes(Graph
 
 
 StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM>::Entry Graphics::blendModeEntries[] =
 StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM>::Entry Graphics::blendModeEntries[] =
 {
 {
-	{ "alpha", BLEND_ALPHA },
-	{ "add", BLEND_ADD },
+	{ "alpha",    BLEND_ALPHA    },
+	{ "add",      BLEND_ADD      },
 	{ "subtract", BLEND_SUBTRACT },
 	{ "subtract", BLEND_SUBTRACT },
 	{ "multiply", BLEND_MULTIPLY },
 	{ "multiply", BLEND_MULTIPLY },
-	{ "screen", BLEND_SCREEN },
-	{ "replace", BLEND_REPLACE },
+	{ "screen",   BLEND_SCREEN   },
+	{ "replace",  BLEND_REPLACE  },
 };
 };
 
 
 StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM> Graphics::blendModes(Graphics::blendModeEntries, sizeof(Graphics::blendModeEntries));
 StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM> Graphics::blendModes(Graphics::blendModeEntries, sizeof(Graphics::blendModeEntries));
 
 
+StringMap<Graphics::BlendAlpha, Graphics::BLENDALPHA_MAX_ENUM>::Entry Graphics::blendAlphaEntries[] =
+{
+	{ "alphamultiply", BLENDALPHA_MULTIPLY      },
+	{ "premultiplied", BLENDALPHA_PREMULTIPLIED },
+};
+
+StringMap<Graphics::BlendAlpha, Graphics::BLENDALPHA_MAX_ENUM> Graphics::blendAlphaModes(Graphics::blendAlphaEntries, sizeof(Graphics::blendAlphaEntries));
+
 StringMap<Graphics::LineStyle, Graphics::LINE_MAX_ENUM>::Entry Graphics::lineStyleEntries[] =
 StringMap<Graphics::LineStyle, Graphics::LINE_MAX_ENUM>::Entry Graphics::lineStyleEntries[] =
 {
 {
 	{ "smooth", LINE_SMOOTH },
 	{ "smooth", LINE_SMOOTH },
@@ -179,6 +217,31 @@ StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM>::Entry Graphics::lin
 
 
 StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM> Graphics::lineJoins(Graphics::lineJoinEntries, sizeof(Graphics::lineJoinEntries));
 StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM> Graphics::lineJoins(Graphics::lineJoinEntries, sizeof(Graphics::lineJoinEntries));
 
 
+StringMap<Graphics::StencilAction, Graphics::STENCIL_MAX_ENUM>::Entry Graphics::stencilActionEntries[] =
+{
+	{ "replace", STENCIL_REPLACE },
+	{ "increment", STENCIL_INCREMENT },
+	{ "decrement", STENCIL_DECREMENT },
+	{ "incrementwrap", STENCIL_INCREMENT_WRAP },
+	{ "decrementwrap", STENCIL_DECREMENT_WRAP },
+	{ "invert", STENCIL_INVERT },
+};
+
+StringMap<Graphics::StencilAction, Graphics::STENCIL_MAX_ENUM> Graphics::stencilActions(Graphics::stencilActionEntries, sizeof(Graphics::stencilActionEntries));
+
+StringMap<Graphics::CompareMode, Graphics::COMPARE_MAX_ENUM>::Entry Graphics::compareModeEntries[] =
+{
+	{ "less",     COMPARE_LESS     },
+	{ "lequal",   COMPARE_LEQUAL   },
+	{ "equal",    COMPARE_EQUAL    },
+	{ "gequal",   COMPARE_GEQUAL   },
+	{ "greater",  COMPARE_GREATER  },
+	{ "notequal", COMPARE_NOTEQUAL },
+	{ "always",   COMPARE_ALWAYS   },
+};
+
+StringMap<Graphics::CompareMode, Graphics::COMPARE_MAX_ENUM> Graphics::compareModes(Graphics::compareModeEntries, sizeof(Graphics::compareModeEntries));
+
 StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
 StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
 {
 {
 	{ "multicanvasformats", SUPPORT_MULTI_CANVAS_FORMATS },
 	{ "multicanvasformats", SUPPORT_MULTI_CANVAS_FORMATS },

+ 48 - 0
jni/love/src/modules/graphics/Graphics.h

@@ -81,6 +81,13 @@ public:
 		BLEND_MAX_ENUM
 		BLEND_MAX_ENUM
 	};
 	};
 
 
+	enum BlendAlpha
+	{
+		BLENDALPHA_MULTIPLY,
+		BLENDALPHA_PREMULTIPLIED,
+		BLENDALPHA_MAX_ENUM
+	};
+
 	enum LineStyle
 	enum LineStyle
 	{
 	{
 		LINE_ROUGH,
 		LINE_ROUGH,
@@ -96,6 +103,29 @@ public:
 		LINE_JOIN_MAX_ENUM
 		LINE_JOIN_MAX_ENUM
 	};
 	};
 
 
+	enum StencilAction
+	{
+		STENCIL_REPLACE,
+		STENCIL_INCREMENT,
+		STENCIL_DECREMENT,
+		STENCIL_INCREMENT_WRAP,
+		STENCIL_DECREMENT_WRAP,
+		STENCIL_INVERT,
+		STENCIL_MAX_ENUM
+	};
+
+	enum CompareMode
+	{
+		COMPARE_LESS,
+		COMPARE_LEQUAL,
+		COMPARE_EQUAL,
+		COMPARE_GEQUAL,
+		COMPARE_GREATER,
+		COMPARE_NOTEQUAL,
+		COMPARE_ALWAYS,
+		COMPARE_MAX_ENUM
+	};
+
 	enum Support
 	enum Support
 	{
 	{
 		SUPPORT_MULTI_CANVAS_FORMATS,
 		SUPPORT_MULTI_CANVAS_FORMATS,
@@ -232,12 +262,21 @@ public:
 	static bool getConstant(const char *in, BlendMode &out);
 	static bool getConstant(const char *in, BlendMode &out);
 	static bool getConstant(BlendMode in, const char *&out);
 	static bool getConstant(BlendMode in, const char *&out);
 
 
+	static bool getConstant(const char *in, BlendAlpha &out);
+	static bool getConstant(BlendAlpha in, const char *&out);
+
 	static bool getConstant(const char *in, LineStyle &out);
 	static bool getConstant(const char *in, LineStyle &out);
 	static bool getConstant(LineStyle in, const char *&out);
 	static bool getConstant(LineStyle in, const char *&out);
 
 
 	static bool getConstant(const char *in, LineJoin &out);
 	static bool getConstant(const char *in, LineJoin &out);
 	static bool getConstant(LineJoin in, const char *&out);
 	static bool getConstant(LineJoin in, const char *&out);
 
 
+	static bool getConstant(const char *in, StencilAction &out);
+	static bool getConstant(StencilAction in, const char *&out);
+
+	static bool getConstant(const char *in, CompareMode &out);
+	static bool getConstant(CompareMode in, const char *&out);
+
 	static bool getConstant(const char *in, Support &out);
 	static bool getConstant(const char *in, Support &out);
 	static bool getConstant(Support in, const char *&out);
 	static bool getConstant(Support in, const char *&out);
 
 
@@ -258,12 +297,21 @@ private:
 	static StringMap<BlendMode, BLEND_MAX_ENUM>::Entry blendModeEntries[];
 	static StringMap<BlendMode, BLEND_MAX_ENUM>::Entry blendModeEntries[];
 	static StringMap<BlendMode, BLEND_MAX_ENUM> blendModes;
 	static StringMap<BlendMode, BLEND_MAX_ENUM> blendModes;
 
 
+	static StringMap<BlendAlpha, BLENDALPHA_MAX_ENUM>::Entry blendAlphaEntries[];
+	static StringMap<BlendAlpha, BLENDALPHA_MAX_ENUM> blendAlphaModes;
+
 	static StringMap<LineStyle, LINE_MAX_ENUM>::Entry lineStyleEntries[];
 	static StringMap<LineStyle, LINE_MAX_ENUM>::Entry lineStyleEntries[];
 	static StringMap<LineStyle, LINE_MAX_ENUM> lineStyles;
 	static StringMap<LineStyle, LINE_MAX_ENUM> lineStyles;
 
 
 	static StringMap<LineJoin, LINE_JOIN_MAX_ENUM>::Entry lineJoinEntries[];
 	static StringMap<LineJoin, LINE_JOIN_MAX_ENUM>::Entry lineJoinEntries[];
 	static StringMap<LineJoin, LINE_JOIN_MAX_ENUM> lineJoins;
 	static StringMap<LineJoin, LINE_JOIN_MAX_ENUM> lineJoins;
 
 
+	static StringMap<StencilAction, STENCIL_MAX_ENUM>::Entry stencilActionEntries[];
+	static StringMap<StencilAction, STENCIL_MAX_ENUM> stencilActions;
+
+	static StringMap<CompareMode, COMPARE_MAX_ENUM>::Entry compareModeEntries[];
+	static StringMap<CompareMode, COMPARE_MAX_ENUM> compareModes;
+
 	static StringMap<Support, SUPPORT_MAX_ENUM>::Entry supportEntries[];
 	static StringMap<Support, SUPPORT_MAX_ENUM>::Entry supportEntries[];
 	static StringMap<Support, SUPPORT_MAX_ENUM> support;
 	static StringMap<Support, SUPPORT_MAX_ENUM> support;
 
 

+ 1 - 1
jni/love/src/modules/graphics/opengl/Canvas.cpp

@@ -558,7 +558,7 @@ bool Canvas::checkCreateStencil()
 	if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
 	if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
 		|| GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
 		|| GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
 	{
 	{
-		format = GL_DEPTH_STENCIL;
+		format = GL_DEPTH24_STENCIL8;
 		attachment = GL_DEPTH_STENCIL_ATTACHMENT;
 		attachment = GL_DEPTH_STENCIL_ATTACHMENT;
 	}
 	}
 
 

+ 16 - 4
jni/love/src/modules/graphics/opengl/Font.cpp

@@ -617,7 +617,7 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
 	return drawcommands;
 	return drawcommands;
 }
 }
 
 
-void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
+void Font::drawVertices(const std::vector<DrawCommand> &drawcommands, bool bufferedvertices)
 {
 {
 	// Vertex attribute pointers need to be set before calling this function.
 	// Vertex attribute pointers need to be set before calling this function.
 	// This assumes that the attribute pointers are constant for all vertices.
 	// This assumes that the attribute pointers are constant for all vertices.
@@ -634,7 +634,12 @@ void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
 	const GLenum gltype = quadIndices.getType();
 	const GLenum gltype = quadIndices.getType();
 	const size_t elemsize = quadIndices.getElementSize();
 	const size_t elemsize = quadIndices.getElementSize();
 
 
-	GLBuffer::Bind bind(*quadIndices.getBuffer());
+	// We only get indices from the index buffer if we're also using vertex
+	// buffers, because at least one graphics driver (the one for Kepler nvidia
+	// GPUs in OS X 10.11) fails to render geometry if an index buffer is used
+	// with client-side vertex arrays.
+	if (bufferedvertices)
+		quadIndices.getBuffer()->bind();
 
 
 	// We need a separate draw call for every section of the text which uses a
 	// We need a separate draw call for every section of the text which uses a
 	// different texture than the previous section.
 	// different texture than the previous section.
@@ -645,8 +650,15 @@ void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
 
 
 		// TODO: Use glDrawElementsBaseVertex when supported?
 		// TODO: Use glDrawElementsBaseVertex when supported?
 		gl.bindTexture(cmd.texture);
 		gl.bindTexture(cmd.texture);
-		gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
+
+		if (bufferedvertices)
+			gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
+		else
+			gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getIndices(offset));
 	}
 	}
+
+	if (bufferedvertices)
+		quadIndices.getBuffer()->unbind();
 }
 }
 
 
 void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
 void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
@@ -665,7 +677,7 @@ void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands
 
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 
 
-	drawVertices(drawcommands);
+	drawVertices(drawcommands, false);
 }
 }
 
 
 void Font::print(const std::vector<ColoredString> &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
 void Font::print(const std::vector<ColoredString> &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)

+ 1 - 1
jni/love/src/modules/graphics/opengl/Font.h

@@ -109,7 +109,7 @@ public:
 
 
 	std::vector<DrawCommand> generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
 	std::vector<DrawCommand> generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
 
 
-	void drawVertices(const std::vector<DrawCommand> &drawcommands);
+	void drawVertices(const std::vector<DrawCommand> &drawcommands, bool bufferedvertices);
 
 
 	static void getCodepointsFromString(const std::string &str, Codepoints &codepoints);
 	static void getCodepointsFromString(const std::string &str, Codepoints &codepoints);
 	static void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints);
 	static void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints);

+ 28 - 10
jni/love/src/modules/graphics/opengl/GLBuffer.cpp

@@ -244,7 +244,9 @@ void GLBuffer::unload()
 size_t QuadIndices::maxSize = 0;
 size_t QuadIndices::maxSize = 0;
 size_t QuadIndices::elementSize = 0;
 size_t QuadIndices::elementSize = 0;
 size_t QuadIndices::objectCount = 0;
 size_t QuadIndices::objectCount = 0;
+
 GLBuffer *QuadIndices::indexBuffer = nullptr;
 GLBuffer *QuadIndices::indexBuffer = nullptr;
+char *QuadIndices::indices = nullptr;
 
 
 QuadIndices::QuadIndices(size_t size)
 QuadIndices::QuadIndices(size_t size)
 	: size(size)
 	: size(size)
@@ -259,6 +261,7 @@ QuadIndices::QuadIndices(size_t size)
 	if (indexBuffer == nullptr || size > maxSize)
 	if (indexBuffer == nullptr || size > maxSize)
 	{
 	{
 		GLBuffer *newbuffer = nullptr;
 		GLBuffer *newbuffer = nullptr;
+		char *newindices = nullptr;
 
 
 		// Depending on the size, a switch to int and more memory is needed.
 		// Depending on the size, a switch to int and more memory is needed.
 		GLenum targettype = getType(size);
 		GLenum targettype = getType(size);
@@ -271,9 +274,12 @@ QuadIndices::QuadIndices(size_t size)
 		try
 		try
 		{
 		{
 			newbuffer = new GLBuffer(buffersize, nullptr, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
 			newbuffer = new GLBuffer(buffersize, nullptr, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
+			newindices = new char[buffersize];
 		}
 		}
 		catch (std::bad_alloc &)
 		catch (std::bad_alloc &)
 		{
 		{
+			delete[] newbuffer;
+			delete[] newindices;
 			throw love::Exception("Out of memory.");
 			throw love::Exception("Out of memory.");
 		}
 		}
 
 
@@ -281,6 +287,10 @@ QuadIndices::QuadIndices(size_t size)
 		// The old GLBuffer can now be deleted.
 		// The old GLBuffer can now be deleted.
 		delete indexBuffer;
 		delete indexBuffer;
 		indexBuffer = newbuffer;
 		indexBuffer = newbuffer;
+
+		delete[] indices;
+		indices = newindices;
+
 		maxSize = size;
 		maxSize = size;
 		elementSize = elemsize;
 		elementSize = elemsize;
 
 
@@ -319,6 +329,9 @@ QuadIndices::~QuadIndices()
 	{
 	{
 		delete indexBuffer;
 		delete indexBuffer;
 		indexBuffer = nullptr;
 		indexBuffer = nullptr;
+
+		delete[] indices;
+		indices = nullptr;
 	}
 	}
 }
 }
 
 
@@ -355,13 +368,15 @@ const void *QuadIndices::getPointer(size_t offset) const
 	return indexBuffer->getPointer(offset);
 	return indexBuffer->getPointer(offset);
 }
 }
 
 
+const void *QuadIndices::getIndices(size_t offset) const
+{
+	return indices + offset;
+}
+
 template <typename T>
 template <typename T>
 void QuadIndices::fill()
 void QuadIndices::fill()
 {
 {
-	GLBuffer::Bind bind(*indexBuffer);
-	GLBuffer::Mapper mapper(*indexBuffer);
-
-	T *indices = (T *) mapper.get();
+	T *inds = (T *) indices;
 
 
 	// 0----2
 	// 0----2
 	// |  / |
 	// |  / |
@@ -369,14 +384,17 @@ void QuadIndices::fill()
 	// 1----3
 	// 1----3
 	for (size_t i = 0; i < maxSize; ++i)
 	for (size_t i = 0; i < maxSize; ++i)
 	{
 	{
-		indices[i*6+0] = T(i * 4 + 0);
-		indices[i*6+1] = T(i * 4 + 1);
-		indices[i*6+2] = T(i * 4 + 2);
+		inds[i*6+0] = T(i * 4 + 0);
+		inds[i*6+1] = T(i * 4 + 1);
+		inds[i*6+2] = T(i * 4 + 2);
 
 
-		indices[i*6+3] = T(i * 4 + 2);
-		indices[i*6+4] = T(i * 4 + 1);
-		indices[i*6+5] = T(i * 4 + 3);
+		inds[i*6+3] = T(i * 4 + 2);
+		inds[i*6+4] = T(i * 4 + 1);
+		inds[i*6+5] = T(i * 4 + 3);
 	}
 	}
+
+	GLBuffer::Bind bind(*indexBuffer);
+	indexBuffer->fill(0, indexBuffer->getSize(), indices);
 }
 }
 
 
 } // opengl
 } // opengl

+ 17 - 0
jni/love/src/modules/graphics/opengl/GLBuffer.h

@@ -384,6 +384,20 @@ public:
 	 */
 	 */
 	const void *getPointer(size_t offset) const;
 	const void *getPointer(size_t offset) const;
 
 
+	/**
+	 * Returns a direct pointer to the index data.
+	 *
+	 * At least one graphics driver (the one for Kepler nvidia GPUs in OS X)
+	 * fails to render geometry if the vertex data was a direct CPU pointer but
+	 * the index data came from an Index Buffer.
+	 * So the direct pointer to the index buffer should be used instead of the
+	 * index buffer when rendering using client-side vertex arrays.
+	 *
+	 * @param offset An offset in bytes into the index data.
+	 * @return A direct pointer to the index data at the specified offset.
+	 **/
+	const void *getIndices(size_t offset) const;
+
 private:
 private:
 
 
 	/**
 	/**
@@ -406,6 +420,9 @@ private:
 
 
 	// The GLBuffer for the element array. Can be null.
 	// The GLBuffer for the element array. Can be null.
 	static GLBuffer *indexBuffer;
 	static GLBuffer *indexBuffer;
+
+	// The array of indices that will also be stored in the index buffer.
+	static char *indices;
 };
 };
 
 
 } // opengl
 } // opengl

+ 109 - 39
jni/love/src/modules/graphics/opengl/Graphics.cpp

@@ -106,7 +106,7 @@ void Graphics::restoreState(const DisplayState &s)
 	setColor(s.color);
 	setColor(s.color);
 	setBackgroundColor(s.backgroundColor);
 	setBackgroundColor(s.backgroundColor);
 
 
-	setBlendMode(s.blendMode, s.blendMultiplyAlpha);
+	setBlendMode(s.blendMode, s.blendAlphaMode);
 
 
 	setLineWidth(s.lineWidth);
 	setLineWidth(s.lineWidth);
 	setLineStyle(s.lineStyle);
 	setLineStyle(s.lineStyle);
@@ -119,7 +119,7 @@ void Graphics::restoreState(const DisplayState &s)
 	else
 	else
 		setScissor();
 		setScissor();
 
 
-	setStencilTest(s.stencilTest, s.stencilInvert);
+	setStencilTest(s.stencilCompare, s.stencilTestValue);
 
 
 	setFont(s.font.get());
 	setFont(s.font.get());
 	setShader(s.shader.get());
 	setShader(s.shader.get());
@@ -141,8 +141,8 @@ void Graphics::restoreStateChecked(const DisplayState &s)
 
 
 	setBackgroundColor(s.backgroundColor);
 	setBackgroundColor(s.backgroundColor);
 
 
-	if (s.blendMode != cur.blendMode || s.blendMultiplyAlpha != cur.blendMultiplyAlpha)
-		setBlendMode(s.blendMode, s.blendMultiplyAlpha);
+	if (s.blendMode != cur.blendMode || s.blendAlphaMode != cur.blendAlphaMode)
+		setBlendMode(s.blendMode, s.blendAlphaMode);
 
 
 	// These are just simple assignments.
 	// These are just simple assignments.
 	setLineWidth(s.lineWidth);
 	setLineWidth(s.lineWidth);
@@ -160,8 +160,8 @@ void Graphics::restoreStateChecked(const DisplayState &s)
 			setScissor();
 			setScissor();
 	}
 	}
 
 
-	if (s.stencilTest != cur.stencilTest || s.stencilInvert != cur.stencilInvert)
-		setStencilTest(s.stencilTest, s.stencilInvert);
+	if (s.stencilCompare != cur.stencilCompare || s.stencilTestValue != cur.stencilTestValue)
+		setStencilTest(s.stencilCompare, s.stencilTestValue);
 
 
 	setFont(s.font.get());
 	setFont(s.font.get());
 	setShader(s.shader.get());
 	setShader(s.shader.get());
@@ -433,7 +433,7 @@ void Graphics::setDebug(bool enable)
 void Graphics::reset()
 void Graphics::reset()
 {
 {
 	DisplayState s;
 	DisplayState s;
-	drawToStencilBuffer(false);
+	stopDrawToStencilBuffer();
 	restoreState(s);
 	restoreState(s);
 	origin();
 	origin();
 }
 }
@@ -641,24 +641,9 @@ bool Graphics::getScissor(int &x, int &y, int &width, int &height) const
 	return state.scissor;
 	return state.scissor;
 }
 }
 
 
-void Graphics::drawToStencilBuffer(bool enable)
+void Graphics::drawToStencilBuffer(StencilAction action, int value)
 {
 {
-	if (writingToStencil == enable)
-		return;
-
-	writingToStencil = enable;
-
-	if (!enable)
-	{
-		const DisplayState &state = states.back();
-
-		// Revert the color write mask.
-		setColorMask(state.colorMask);
-
-		// Use the user-set stencil test state when writes are disabled.
-		setStencilTest(state.stencilTest, state.stencilInvert);
-		return;
-	}
+	writingToStencil = true;
 
 
 	// Make sure the active canvas has a stencil buffer.
 	// Make sure the active canvas has a stencil buffer.
 	if (Canvas::current)
 	if (Canvas::current)
@@ -667,23 +652,63 @@ void Graphics::drawToStencilBuffer(bool enable)
 	// Disable color writes but don't save the state for it.
 	// Disable color writes but don't save the state for it.
 	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 
 
+	GLenum glaction = GL_REPLACE;
+
+	switch (action)
+	{
+	case STENCIL_REPLACE:
+	default:
+		glaction = GL_REPLACE;
+		break;
+	case STENCIL_INCREMENT:
+		glaction = GL_INCR;
+		break;
+	case STENCIL_DECREMENT:
+		glaction = GL_DECR;
+		break;
+	case STENCIL_INCREMENT_WRAP:
+		glaction = GL_INCR_WRAP;
+		break;
+	case STENCIL_DECREMENT_WRAP:
+		glaction = GL_DECR_WRAP;
+		break;
+	case STENCIL_INVERT:
+		glaction = GL_INVERT;
+		break;
+	}
+
 	// The stencil test must be enabled in order to write to the stencil buffer.
 	// The stencil test must be enabled in order to write to the stencil buffer.
 	glEnable(GL_STENCIL_TEST);
 	glEnable(GL_STENCIL_TEST);
+	glStencilFunc(GL_ALWAYS, value, 0xFFFFFFFF);
+	glStencilOp(GL_KEEP, GL_KEEP, glaction);
+}
 
 
-	glStencilFunc(GL_ALWAYS, 1, 1);
-	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+void Graphics::stopDrawToStencilBuffer()
+{
+	if (!writingToStencil)
+		return;
+
+	writingToStencil = false;
+
+	const DisplayState &state = states.back();
+
+	// Revert the color write mask.
+	setColorMask(state.colorMask);
+
+	// Use the user-set stencil test state when writes are disabled.
+	setStencilTest(state.stencilCompare, state.stencilTestValue);
 }
 }
 
 
-void Graphics::setStencilTest(bool enable, bool invert)
+void Graphics::setStencilTest(CompareMode compare, int value)
 {
 {
 	DisplayState &state = states.back();
 	DisplayState &state = states.back();
-	state.stencilTest = enable;
-	state.stencilInvert = invert;
+	state.stencilCompare = compare;
+	state.stencilTestValue = value;
 
 
 	if (writingToStencil)
 	if (writingToStencil)
 		return;
 		return;
 
 
-	if (!enable)
+	if (compare == COMPARE_ALWAYS)
 	{
 	{
 		glDisable(GL_STENCIL_TEST);
 		glDisable(GL_STENCIL_TEST);
 		return;
 		return;
@@ -693,16 +718,61 @@ void Graphics::setStencilTest(bool enable, bool invert)
 	if (Canvas::current)
 	if (Canvas::current)
 		Canvas::current->checkCreateStencil();
 		Canvas::current->checkCreateStencil();
 
 
+	GLenum glcompare = GL_EQUAL;
+
+	/**
+	 * Q: Why are some of the compare modes inverted (e.g. COMPARE_LESS becomes
+	 * GL_GREATER)?
+	 *
+	 * A: OpenGL / GPUs do the comparison in the opposite way that makes sense
+	 * for this API. For example, if the compare function is GL_GREATER then the
+	 * stencil test will pass if the reference value is greater than the value
+	 * in the stencil buffer. With our API it's more intuitive to assume that
+	 * setStencilTest(COMPARE_GREATER, 4) will make it pass if the stencil
+	 * buffer has a value greater than 4.
+	 **/
+
+	switch (compare)
+	{
+	case COMPARE_LESS:
+		glcompare = GL_GREATER;
+		break;
+	case COMPARE_LEQUAL:
+		glcompare = GL_GEQUAL;
+		break;
+	case COMPARE_EQUAL:
+	default:
+		glcompare = GL_EQUAL;
+		break;
+	case COMPARE_GEQUAL:
+		glcompare = GL_LEQUAL;
+		break;
+	case COMPARE_GREATER:
+		glcompare = GL_LESS;
+		break;
+	case COMPARE_NOTEQUAL:
+		glcompare = GL_NOTEQUAL;
+		break;
+	case COMPARE_ALWAYS:
+		glcompare = GL_ALWAYS;
+		break;
+	}
+
 	glEnable(GL_STENCIL_TEST);
 	glEnable(GL_STENCIL_TEST);
-	glStencilFunc(GL_EQUAL, invert ? 0 : 1, 1);
+	glStencilFunc(glcompare, value, 0xFFFFFFFF);
 	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
 	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
 }
 }
 
 
-void Graphics::getStencilTest(bool &enable, bool &invert)
+void Graphics::setStencilTest()
+{
+	setStencilTest(COMPARE_ALWAYS, 0);
+}
+
+void Graphics::getStencilTest(CompareMode &compare, int &value)
 {
 {
 	const DisplayState &state = states.back();
 	const DisplayState &state = states.back();
-	enable = state.stencilTest;
-	invert = state.stencilInvert;
+	compare = state.stencilCompare;
+	value = state.stencilTestValue;
 }
 }
 
 
 void Graphics::clearStencil()
 void Graphics::clearStencil()
@@ -987,7 +1057,7 @@ Graphics::ColorMask Graphics::getColorMask() const
 	return states.back().colorMask;
 	return states.back().colorMask;
 }
 }
 
 
-void Graphics::setBlendMode(BlendMode mode, bool multiplyalpha)
+void Graphics::setBlendMode(BlendMode mode, BlendAlpha alphamode)
 {
 {
 	GLenum func   = GL_FUNC_ADD;
 	GLenum func   = GL_FUNC_ADD;
 	GLenum srcRGB = GL_ONE;
 	GLenum srcRGB = GL_ONE;
@@ -1024,19 +1094,19 @@ void Graphics::setBlendMode(BlendMode mode, bool multiplyalpha)
 	}
 	}
 
 
 	// We can only do alpha-multiplication when srcRGB would have been unmodified.
 	// We can only do alpha-multiplication when srcRGB would have been unmodified.
-	if (srcRGB == GL_ONE && multiplyalpha)
+	if (srcRGB == GL_ONE && alphamode == BLENDALPHA_MULTIPLY)
 		srcRGB = GL_SRC_ALPHA;
 		srcRGB = GL_SRC_ALPHA;
 
 
 	glBlendEquation(func);
 	glBlendEquation(func);
 	glBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA);
 	glBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA);
 
 
 	states.back().blendMode = mode;
 	states.back().blendMode = mode;
-	states.back().blendMultiplyAlpha = multiplyalpha;
+	states.back().blendAlphaMode = alphamode;
 }
 }
 
 
-Graphics::BlendMode Graphics::getBlendMode(bool &multiplyalpha) const
+Graphics::BlendMode Graphics::getBlendMode(BlendAlpha &alphamode) const
 {
 {
-	multiplyalpha = states.back().blendMultiplyAlpha;
+	alphamode = states.back().blendAlphaMode;
 	return states.back().blendMode;
 	return states.back().blendMode;
 }
 }
 
 

+ 10 - 8
jni/love/src/modules/graphics/opengl/Graphics.h

@@ -146,13 +146,15 @@ public:
 	 * Enables or disables drawing to the stencil buffer. When enabled, the
 	 * Enables or disables drawing to the stencil buffer. When enabled, the
 	 * color buffer is disabled.
 	 * color buffer is disabled.
 	 **/
 	 **/
-	void drawToStencilBuffer(bool enable);
+	void drawToStencilBuffer(StencilAction action, int value);
+	void stopDrawToStencilBuffer();
 
 
 	/**
 	/**
 	 * Sets whether stencil testing is enabled.
 	 * Sets whether stencil testing is enabled.
 	 **/
 	 **/
-	void setStencilTest(bool enable, bool invert);
-	void getStencilTest(bool &enable, bool &invert);
+	void setStencilTest(CompareMode compare, int value);
+	void setStencilTest();
+	void getStencilTest(CompareMode &compare, int &value);
 
 
 	/**
 	/**
 	 * Clear the stencil buffer in the active Canvas(es.)
 	 * Clear the stencil buffer in the active Canvas(es.)
@@ -241,12 +243,12 @@ public:
 	/**
 	/**
 	 * Sets the current blend mode.
 	 * Sets the current blend mode.
 	 **/
 	 **/
-	void setBlendMode(BlendMode mode, bool multiplyalpha);
+	void setBlendMode(BlendMode mode, BlendAlpha alphamode);
 
 
 	/**
 	/**
 	 * Gets the current blend mode.
 	 * Gets the current blend mode.
 	 **/
 	 **/
-	BlendMode getBlendMode(bool &multiplyalpha) const;
+	BlendMode getBlendMode(BlendAlpha &alphamode) const;
 
 
 	/**
 	/**
 	 * Sets the default filter for images, canvases, and fonts.
 	 * Sets the default filter for images, canvases, and fonts.
@@ -476,7 +478,7 @@ private:
 		Colorf backgroundColor = Colorf(0.0, 0.0, 0.0, 255.0);
 		Colorf backgroundColor = Colorf(0.0, 0.0, 0.0, 255.0);
 
 
 		BlendMode blendMode = BLEND_ALPHA;
 		BlendMode blendMode = BLEND_ALPHA;
-		bool blendMultiplyAlpha = true;
+		BlendAlpha blendAlphaMode = BLENDALPHA_MULTIPLY;
 
 
 		float lineWidth = 1.0f;
 		float lineWidth = 1.0f;
 		LineStyle lineStyle = LINE_SMOOTH;
 		LineStyle lineStyle = LINE_SMOOTH;
@@ -488,8 +490,8 @@ private:
 		ScissorRect scissorRect = ScissorRect();
 		ScissorRect scissorRect = ScissorRect();
 
 
 		// Stencil.
 		// Stencil.
-		bool stencilTest = false;
-		bool stencilInvert = false;
+		CompareMode stencilCompare = COMPARE_ALWAYS;
+		int stencilTestValue = 0;
 
 
 		StrongRef<Font> font;
 		StrongRef<Font> font;
 		StrongRef<Shader> shader;
 		StrongRef<Shader> shader;

+ 8 - 5
jni/love/src/modules/graphics/opengl/ParticleSystem.cpp

@@ -141,11 +141,14 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].x);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].x);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].s);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].s);
 
 
-	{
-		GLsizei count = (GLsizei) quadIndices.getIndexCount(pCount);
-		GLBuffer::Bind ibo_bind(*quadIndices.getBuffer());
-		gl.drawElements(GL_TRIANGLES, count, quadIndices.getType(), quadIndices.getPointer(0));
-	}
+	GLsizei count = (GLsizei) quadIndices.getIndexCount(pCount);
+	GLenum gltype = quadIndices.getType();
+
+	// We use a client-side index array instead of an Index Buffers, because
+	// at least one graphics driver (the one for Kepler nvidia GPUs in OS X
+	// 10.11) fails to render geometry if an index buffer is used with
+	// client-side vertex arrays.
+	gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getIndices(0));
 }
 }
 
 
 } // opengl
 } // opengl

+ 1 - 1
jni/love/src/modules/graphics/opengl/Text.cpp

@@ -245,7 +245,7 @@ void Text::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 
 
-	font->drawVertices(draw_commands);
+	font->drawVertices(draw_commands, true);
 }
 }
 
 
 void Text::setFont(Font *f)
 void Text::setFont(Font *f)

+ 21 - 21
jni/love/src/modules/graphics/opengl/Video.cpp

@@ -79,23 +79,23 @@ bool Video::loadVolatile()
 	// Create the textures using the initial frame data.
 	// Create the textures using the initial frame data.
 	auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
 	auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
 
 
-	gl.bindTexture(textures[0]);
-	gl.setTextureFilter(filter);
+	int widths[3]  = {frame->yw, frame->cw, frame->cw};
+	int heights[3] = {frame->yh, frame->ch, frame->ch};
 
 
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->yw, frame->yh,
-	             0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->yplane);
+	const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
 
 
-	gl.bindTexture(textures[1]);
-	gl.setTextureFilter(filter);
+	Texture::Wrap wrap; // Clamp wrap mode.
 
 
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->cw, frame->ch,
-	             0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->cbplane);
+	for (int i = 0; i < 3; i++)
+	{
+		gl.bindTexture(textures[i]);
 
 
-	gl.bindTexture(textures[2]);
-	gl.setTextureFilter(filter);
+		gl.setTextureFilter(filter);
+		gl.setTextureWrap(wrap);
 
 
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->cw, frame->ch,
-	             0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->crplane);
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, widths[i], heights[i], 0,
+		             GL_LUMINANCE, GL_UNSIGNED_BYTE, data[i]);
+	}
 
 
 	return true;
 	return true;
 }
 }
@@ -154,17 +154,17 @@ void Video::update()
 	{
 	{
 		auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
 		auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
 
 
-		gl.bindTexture(textures[0]);
-		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->yw, frame->yh,
-		                GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->yplane);
+		int widths[3]  = {frame->yw, frame->cw, frame->cw};
+		int heights[3] = {frame->yh, frame->ch, frame->ch};
 
 
-		gl.bindTexture(textures[1]);
-		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->cw, frame->ch,
-		                GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->cbplane);
+		const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
 
 
-		gl.bindTexture(textures[2]);
-		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->cw, frame->ch,
-		                GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->crplane);
+		for (int i = 0; i < 3; i++)
+		{
+			gl.bindTexture(textures[i]);
+			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, widths[i], heights[i],
+			                GL_LUMINANCE, GL_UNSIGNED_BYTE, data[i]);
+		}
 	}
 	}
 }
 }
 
 

+ 56 - 19
jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -217,35 +217,63 @@ int w_stencil(lua_State *L)
 {
 {
 	luaL_checktype(L, 1, LUA_TFUNCTION);
 	luaL_checktype(L, 1, LUA_TFUNCTION);
 
 
-	// Second argument: whether to keep the contents of the stencil buffer.
-	if (lua_toboolean(L, 2) == 0)
+	Graphics::StencilAction action = Graphics::STENCIL_REPLACE;
+
+	if (!lua_isnoneornil(L, 2))
+	{
+		const char *actionstr = luaL_checkstring(L, 2);
+		if (!Graphics::getConstant(actionstr, action))
+			return luaL_error(L, "Invalid stencil draw action: %s", actionstr);
+	}
+
+	int stencilvalue = (int) luaL_optnumber(L, 3, 1);
+
+	// Fourth argument: whether to keep the contents of the stencil buffer.
+	if (lua_toboolean(L, 4) == 0)
 		instance()->clearStencil();
 		instance()->clearStencil();
 
 
-	instance()->drawToStencilBuffer(true);
+	instance()->drawToStencilBuffer(action, stencilvalue);
 
 
 	// Call stencilfunc()
 	// Call stencilfunc()
 	lua_pushvalue(L, 1);
 	lua_pushvalue(L, 1);
 	lua_call(L, 0, 0);
 	lua_call(L, 0, 0);
 
 
-	instance()->drawToStencilBuffer(false);
-
+	instance()->stopDrawToStencilBuffer();
 	return 0;
 	return 0;
 }
 }
 
 
 int w_setStencilTest(lua_State *L)
 int w_setStencilTest(lua_State *L)
 {
 {
-	bool enable = luax_toboolean(L, 1);
-	bool invert = luax_toboolean(L, 2);
-	instance()->setStencilTest(enable, invert);
+	// COMPARE_ALWAYS effectively disables stencil testing.
+	Graphics::CompareMode compare = Graphics::COMPARE_ALWAYS;
+	int comparevalue = 0;
+
+	if (!lua_isnoneornil(L, 1))
+	{
+		const char *comparestr = luaL_checkstring(L, 1);
+		if (!Graphics::getConstant(comparestr, compare))
+			return luaL_error(L, "Invalid compare mode: %s", comparestr);
+
+		comparevalue = (int) luaL_checknumber(L, 2);
+	}
+
+	instance()->setStencilTest(compare, comparevalue);
 	return 0;
 	return 0;
 }
 }
 
 
 int w_getStencilTest(lua_State *L)
 int w_getStencilTest(lua_State *L)
 {
 {
-	bool enabled, inverted;
-	instance()->getStencilTest(enabled, inverted);
-	luax_pushboolean(L, enabled);
-	luax_pushboolean(L, inverted);
+	Graphics::CompareMode compare = Graphics::COMPARE_ALWAYS;
+	int comparevalue = 1;
+
+	instance()->getStencilTest(compare, comparevalue);
+
+	const char *comparestr;
+	if (!Graphics::getConstant(compare, comparestr))
+		return luaL_error(L, "Unknown compare mode.");
+
+	lua_pushstring(L, comparestr);
+	lua_pushnumber(L, comparevalue);
 	return 2;
 	return 2;
 }
 }
 
 
@@ -1014,25 +1042,34 @@ int w_setBlendMode(lua_State *L)
 	if (!Graphics::getConstant(str, mode))
 	if (!Graphics::getConstant(str, mode))
 		return luaL_error(L, "Invalid blend mode: %s", str);
 		return luaL_error(L, "Invalid blend mode: %s", str);
 
 
-	bool multiplyalpha = luax_optboolean(L, 2, true);
+	Graphics::BlendAlpha alphamode = Graphics::BLENDALPHA_MULTIPLY;
+	if (!lua_isnoneornil(L, 2))
+	{
+		const char *alphastr = luaL_checkstring(L, 2);
+		if (!Graphics::getConstant(alphastr, alphamode))
+			return luaL_error(L, "Invalid blend alpha mode: %s", alphastr);
+	}
 
 
-	luax_catchexcept(L, [&](){ instance()->setBlendMode(mode, multiplyalpha); });
+	luax_catchexcept(L, [&](){ instance()->setBlendMode(mode, alphamode); });
 	return 0;
 	return 0;
 }
 }
 
 
 int w_getBlendMode(lua_State *L)
 int w_getBlendMode(lua_State *L)
 {
 {
 	const char *str;
 	const char *str;
-	Graphics::BlendMode mode;
-	bool multiplyalpha = false;
+	const char *alphastr;
 
 
-	luax_catchexcept(L, [&](){ mode = instance()->getBlendMode(multiplyalpha); });
+	Graphics::BlendAlpha alphamode;
+	Graphics::BlendMode mode = instance()->getBlendMode(alphamode);
 
 
 	if (!Graphics::getConstant(mode, str))
 	if (!Graphics::getConstant(mode, str))
 		return luaL_error(L, "Unknown blend mode");
 		return luaL_error(L, "Unknown blend mode");
 
 
+	if (!Graphics::getConstant(alphamode, alphastr))
+		return luaL_error(L, "Unknown blend alpha mode");
+
 	lua_pushstring(L, str);
 	lua_pushstring(L, str);
-	lua_pushboolean(L, multiplyalpha);
+	lua_pushstring(L, alphastr);
 	return 2;
 	return 2;
 }
 }
 
 
@@ -1201,7 +1238,7 @@ int w_newScreenshot(lua_State *L)
 int w_setCanvas(lua_State *L)
 int w_setCanvas(lua_State *L)
 {
 {
 	// Disable stencil writes.
 	// Disable stencil writes.
-	instance()->drawToStencilBuffer(false);
+	instance()->stopDrawToStencilBuffer();
 
 
 	// called with none -> reset to default buffer
 	// called with none -> reset to default buffer
 	if (lua_isnoneornil(L, 1))
 	if (lua_isnoneornil(L, 1))

+ 1 - 1
jni/love/src/modules/graphics/opengl/wrap_Graphics.lua

@@ -366,7 +366,7 @@ function love.graphics.newVideo(file, loadaudio)
 	elseif loadaudio == true then
 	elseif loadaudio == true then
 		error("Video had no audio track", 2)
 		error("Video had no audio track", 2)
 	else
 	else
-		video:getStream():setSync(love.video.newRemote())
+		video:getStream():setSync()
 	end
 	end
 
 
 	return video
 	return video

+ 18 - 4
jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp

@@ -26,6 +26,7 @@
 
 
 // C++
 // C++
 #include <typeinfo>
 #include <typeinfo>
+#include <algorithm>
 
 
 namespace love
 namespace love
 {
 {
@@ -108,12 +109,28 @@ const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int compon
 int w_Mesh_setVertices(lua_State *L)
 int w_Mesh_setVertices(lua_State *L)
 {
 {
 	Mesh *t = luax_checkmesh(L, 1);
 	Mesh *t = luax_checkmesh(L, 1);
-	luaL_checktype(L, 2, LUA_TTABLE);
 	size_t vertoffset = (size_t) luaL_optnumber(L, 3, 1) - 1;
 	size_t vertoffset = (size_t) luaL_optnumber(L, 3, 1) - 1;
 
 
 	if (vertoffset >= t->getVertexCount())
 	if (vertoffset >= t->getVertexCount())
 		return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", (int) t->getVertexCount());
 		return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", (int) t->getVertexCount());
 
 
+	size_t stride = t->getVertexStride();
+	size_t byteoffset = vertoffset * stride;
+
+	if (luax_istype(L, 2, DATA_ID))
+	{
+		Data *d = luax_checktype<Data>(L, 2, DATA_ID);
+
+		size_t datasize = std::min(d->getSize(), (t->getVertexCount() - vertoffset) * stride);
+		char *bytedata = (char *) t->mapVertexData() + byteoffset;
+
+		memcpy(bytedata, d->getData(), datasize);
+
+		t->unmapVertexData(byteoffset, datasize);
+		return 0;
+	}
+
+	luaL_checktype(L, 2, LUA_TTABLE);
 	size_t nvertices = luax_objlen(L, 2);
 	size_t nvertices = luax_objlen(L, 2);
 
 
 	if (vertoffset + nvertices > t->getVertexCount())
 	if (vertoffset + nvertices > t->getVertexCount())
@@ -125,9 +142,6 @@ int w_Mesh_setVertices(lua_State *L)
 	for (const Mesh::AttribFormat &format : vertexformat)
 	for (const Mesh::AttribFormat &format : vertexformat)
 		ncomponents += format.components;
 		ncomponents += format.components;
 
 
-	size_t stride = t->getVertexStride();
-	size_t byteoffset = vertoffset * stride;
-
 	char *data = (char *) t->mapVertexData() + byteoffset;
 	char *data = (char *) t->mapVertexData() + byteoffset;
 
 
 	for (size_t i = 0; i < nvertices; i++)
 	for (size_t i = 0; i < nvertices; i++)

+ 1 - 0
jni/love/src/modules/graphics/opengl/wrap_Mesh.h

@@ -22,6 +22,7 @@
 #define LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
 #define LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
 
 
 // LOVE
 // LOVE
+#include "common/config.h"
 #include "common/runtime.h"
 #include "common/runtime.h"
 #include "Mesh.h"
 #include "Mesh.h"
 
 

+ 1 - 12
jni/love/src/modules/graphics/opengl/wrap_Video.lua

@@ -25,20 +25,9 @@ misrepresented as being the original software.
 local Video_mt = ...
 local Video_mt = ...
 local Video = Video_mt.__index
 local Video = Video_mt.__index
 
 
-function Video:loadRemote()
-	local stream = self:getStream()
-	local remote = love.video.newRemote()
-	stream:setSync(remote)
-	return remote
-end
-
 function Video:setSource(source)
 function Video:setSource(source)
 	self:_setSource(source)
 	self:_setSource(source)
-	if source then
-		self:getStream():setSync(source)
-	else
-		self:getStream():setSync(love.video.newRemote())
-	end
+	self:getStream():setSync(source)
 end
 end
 
 
 function Video:play()
 function Video:play()

+ 2 - 49
jni/love/src/modules/math/MathModule.h

@@ -55,56 +55,9 @@ public:
 
 
 	virtual ~Math();
 	virtual ~Math();
 
 
-	/**
-	 * @copydoc RandomGenerator::random()
-	 **/
-	inline double random()
-	{
-		return rng.random();
-	}
-
-	/**
-	 * @copydoc RandomGenerator::random(double)
-	 **/
-	inline double random(double max)
-	{
-		return rng.random(max);
-	}
-
-	/**
-	 * @copydoc RandomGenerator::random(double,double)
-	 **/
-	inline double random(double min, double max)
-	{
-		return rng.random(min, max);
-	}
-
-	/**
-	 * @copydoc RandomGenerator::randomNormal()
-	 **/
-	inline double randomNormal(double stddev)
-	{
-		return rng.randomNormal(stddev);
-	}
-
-	inline void setRandomSeed(RandomGenerator::Seed seed)
-	{
-		rng.setSeed(seed);
-	}
-
-	inline RandomGenerator::Seed getRandomSeed() const
-	{
-		return rng.getSeed();
-	}
-
-	inline void setRandomState(const std::string &statestr)
-	{
-		rng.setState(statestr);
-	}
-
-	inline std::string getRandomState() const
+	RandomGenerator *getRandomGenerator()
 	{
 	{
-		return rng.getState();
+		return &rng;
 	}
 	}
 
 
 	/**
 	/**

+ 7 - 44
jni/love/src/modules/math/wrap_Math.cpp

@@ -39,44 +39,10 @@ namespace love
 namespace math
 namespace math
 {
 {
 
 
-int w_random(lua_State *L)
+int w__getRandomGenerator(lua_State *L)
 {
 {
-	return luax_getrandom(L, 1, Math::instance.random());
-}
-
-int w_randomNormal(lua_State *L)
-{
-	double stddev = luaL_optnumber(L, 1, 1.0);
-	double mean = luaL_optnumber(L, 2, 0.0);
-	double r = Math::instance.randomNormal(stddev);
-
-	lua_pushnumber(L, r + mean);
-	return 1;
-}
-
-int w_setRandomSeed(lua_State *L)
-{
-	luax_catchexcept(L, [&](){ Math::instance.setRandomSeed(luax_checkrandomseed(L, 1)); });
-	return 0;
-}
-
-int w_getRandomSeed(lua_State *L)
-{
-	RandomGenerator::Seed s = Math::instance.getRandomSeed();
-	lua_pushnumber(L, (lua_Number) s.b32.low);
-	lua_pushnumber(L, (lua_Number) s.b32.high);
-	return 2;
-}
-
-int w_setRandomState(lua_State *L)
-{
-	luax_catchexcept(L, [&](){ Math::instance.setRandomState(luax_checkstring(L, 1)); });
-	return 0;
-}
-
-int w_getRandomState(lua_State *L)
-{
-	luax_pushstring(L, Math::instance.getRandomState());
+	RandomGenerator *t = Math::instance.getRandomGenerator();
+	luax_pushtype(L, MATH_RANDOM_GENERATOR_ID, t);
 	return 1;
 	return 1;
 }
 }
 
 
@@ -463,12 +429,9 @@ static FFI_Math ffifuncs =
 // List of functions to wrap.
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 static const luaL_Reg functions[] =
 {
 {
-	{ "random", w_random },
-	{ "randomNormal", w_randomNormal },
-	{ "setRandomSeed", w_setRandomSeed },
-	{ "getRandomSeed", w_getRandomSeed },
-	{ "setRandomState", w_setRandomState },
-	{ "getRandomState", w_getRandomState },
+	// love.math.random etc. are defined in wrap_Math.lua.
+
+	{ "_getRandomGenerator", w__getRandomGenerator },
 	{ "newRandomGenerator", w_newRandomGenerator },
 	{ "newRandomGenerator", w_newRandomGenerator },
 	{ "newBezierCurve", w_newBezierCurve },
 	{ "newBezierCurve", w_newBezierCurve },
 	{ "triangulate", w_triangulate },
 	{ "triangulate", w_triangulate },
@@ -502,7 +465,7 @@ extern "C" int luaopen_love_math(lua_State *L)
 
 
 	int n = luax_register_module(L, w);
 	int n = luax_register_module(L, w);
 
 
-	// Execute wrap_Event.lua, sending the math table and ffifuncs pointer as args.
+	// Execute wrap_Math.lua, sending the math table and ffifuncs pointer as args.
 	luaL_loadbuffer(L, math_lua, sizeof(math_lua), "wrap_Math.lua");
 	luaL_loadbuffer(L, math_lua, sizeof(math_lua), "wrap_Math.lua");
 	lua_pushvalue(L, -2);
 	lua_pushvalue(L, -2);
 	lua_pushlightuserdata(L, &ffifuncs);
 	lua_pushlightuserdata(L, &ffifuncs);

+ 33 - 6
jni/love/src/modules/math/wrap_Math.lua

@@ -22,7 +22,36 @@ misrepresented as being the original software.
 3. This notice may not be removed or altered from any source distribution.
 3. This notice may not be removed or altered from any source distribution.
 --]]
 --]]
 
 
-local math, ffifuncspointer = ...
+local love_math, ffifuncspointer = ...
+
+local type, tonumber, error = type, tonumber, error
+local floor = math.floor
+
+local rng = love_math._getRandomGenerator()
+
+function love_math.random(l, u)
+	return rng:random(l, u)
+end
+
+function love_math.randomNormal(stddev, mean)
+	return rng:randomNormal(stddev, mean)
+end
+
+function love_math.setRandomSeed(low, high)
+	return rng:setSeed(low, high)
+end
+
+function love_math.getRandomSeed()
+	return rng:getSeed()
+end
+
+function love_math.setRandomState(state)
+	return rng:setState(state)
+end
+
+function love_math.getRandomState()
+	return rng:getState()
+end
 
 
 if type(jit) ~= "table" or not jit.status() then
 if type(jit) ~= "table" or not jit.status() then
 	-- LuaJIT's FFI is *much* slower than LOVE's regular methods when the JIT
 	-- LuaJIT's FFI is *much* slower than LOVE's regular methods when the JIT
@@ -33,8 +62,6 @@ end
 local status, ffi = pcall(require, "ffi")
 local status, ffi = pcall(require, "ffi")
 if not status then return end
 if not status then return end
 
 
-local type, tonumber = type, tonumber
-
 -- Matches the struct declaration in wrap_Math.cpp.
 -- Matches the struct declaration in wrap_Math.cpp.
 pcall(ffi.cdef, [[
 pcall(ffi.cdef, [[
 typedef struct FFI_Math
 typedef struct FFI_Math
@@ -54,7 +81,7 @@ local ffifuncs = ffi.cast("FFI_Math *", ffifuncspointer)
 
 
 -- Overwrite some regular love.math functions with FFI implementations.
 -- Overwrite some regular love.math functions with FFI implementations.
 
 
-function math.noise(x, y, z, w)
+function love_math.noise(x, y, z, w)
 	if w ~= nil then
 	if w ~= nil then
 		return tonumber(ffifuncs.noise4(x, y, z, w))
 		return tonumber(ffifuncs.noise4(x, y, z, w))
 	elseif z ~= nil then
 	elseif z ~= nil then
@@ -73,7 +100,7 @@ local function gammaToLinear(c)
 	return c
 	return c
 end
 end
 
 
-function math.gammaToLinear(r, g, b, a)
+function love_math.gammaToLinear(r, g, b, a)
 	if type(r) == "table" then
 	if type(r) == "table" then
 		local t = r
 		local t = r
 		return gammaToLinear(t[1]), gammaToLinear(t[2]), gammaToLinear(t[3]), t[4]
 		return gammaToLinear(t[1]), gammaToLinear(t[2]), gammaToLinear(t[3]), t[4]
@@ -88,7 +115,7 @@ local function linearToGamma(c)
 	return c
 	return c
 end
 end
 
 
-function math.linearToGamma(r, g, b, a)
+function love_math.linearToGamma(r, g, b, a)
 	if type(r) == "table" then
 	if type(r) == "table" then
 		local t = r
 		local t = r
 		return linearToGamma(t[1]), linearToGamma(t[2]), linearToGamma(t[3]), t[4]
 		return linearToGamma(t[1]), linearToGamma(t[2]), linearToGamma(t[3]), t[4]

+ 46 - 30
jni/love/src/modules/math/wrap_RandomGenerator.cpp

@@ -23,6 +23,11 @@
 #include <cmath>
 #include <cmath>
 #include <algorithm>
 #include <algorithm>
 
 
+// Put the Lua code directly into a raw string literal.
+static const char randomgenerator_lua[] =
+#include "wrap_RandomGenerator.lua"
+;
+
 namespace love
 namespace love
 {
 {
 namespace math
 namespace math
@@ -56,41 +61,16 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx)
 	return s;
 	return s;
 }
 }
 
 
-int luax_getrandom(lua_State *L, int startidx, double r)
-{
-	int l, u;
-	// from lua 5.1.4 source code: lmathlib.c:185 ff.
-	switch (lua_gettop(L) - (startidx - 1))
-	{
-	case 0:
-		lua_pushnumber(L, r);
-		break;
-	case 1:
-		u = (int) luaL_checknumber(L, startidx);
-		luaL_argcheck(L, 1 <= u, startidx, "interval is empty");
-		lua_pushnumber(L, floor(r * u) + 1);
-		break;
-	case 2:
-		l = (int) luaL_checknumber(L, startidx);
-		u = (int) luaL_checknumber(L, startidx + 1);
-		luaL_argcheck(L, l <= u, startidx + 1, "interval is empty");
-		lua_pushnumber(L, floor(r * (u - l + 1)) + l);
-		break;
-	default:
-		return luaL_error(L, "wrong number of arguments");
-	}
-	return 1;
-}
-
 RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx)
 RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx)
 {
 {
 	return luax_checktype<RandomGenerator>(L, idx, MATH_RANDOM_GENERATOR_ID);
 	return luax_checktype<RandomGenerator>(L, idx, MATH_RANDOM_GENERATOR_ID);
 }
 }
 
 
-int w_RandomGenerator_random(lua_State *L)
+int w_RandomGenerator__random(lua_State *L)
 {
 {
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
-	return luax_getrandom(L, 2, rng->random());
+	lua_pushnumber(L, rng->random());
+	return 1;
 }
 }
 
 
 int w_RandomGenerator_randomNormal(lua_State *L)
 int w_RandomGenerator_randomNormal(lua_State *L)
@@ -135,9 +115,28 @@ int w_RandomGenerator_getState(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
+// C functions in a struct, necessary for the FFI versions of RandomGenerator functions.
+struct FFI_RandomGenerator
+{
+	double (*random)(Proxy *p);
+};
+
+static FFI_RandomGenerator ffifuncs =
+{
+	[](Proxy *p) -> double // random()
+	{
+		// FIXME: We need better type-checking...
+		if (!typeFlags[p->type][MATH_RANDOM_GENERATOR_ID])
+			return 0.0;
+
+		RandomGenerator *rng = (RandomGenerator *) p->object;
+		return rng->random();
+	}
+};
+
 static const luaL_Reg w_RandomGenerator_functions[] =
 static const luaL_Reg w_RandomGenerator_functions[] =
 {
 {
-	{ "random", w_RandomGenerator_random },
+	{ "_random", w_RandomGenerator__random }, // random() is defined in wrap_RandomGenerator.lua.
 	{ "randomNormal", w_RandomGenerator_randomNormal },
 	{ "randomNormal", w_RandomGenerator_randomNormal },
 	{ "setSeed", w_RandomGenerator_setSeed },
 	{ "setSeed", w_RandomGenerator_setSeed },
 	{ "getSeed", w_RandomGenerator_getSeed },
 	{ "getSeed", w_RandomGenerator_getSeed },
@@ -148,7 +147,24 @@ static const luaL_Reg w_RandomGenerator_functions[] =
 
 
 extern "C" int luaopen_randomgenerator(lua_State *L)
 extern "C" int luaopen_randomgenerator(lua_State *L)
 {
 {
-	return luax_register_type(L, MATH_RANDOM_GENERATOR_ID, "RandomGenerator", w_RandomGenerator_functions, nullptr);
+	int n = luax_register_type(L, MATH_RANDOM_GENERATOR_ID, "RandomGenerator", w_RandomGenerator_functions, nullptr);
+
+	luax_gettypemetatable(L, MATH_RANDOM_GENERATOR_ID);
+
+	// Load and execute wrap_RandomGenerator.lua, sending the metatable and the
+	// ffi functions struct pointer as arguments.
+	if (lua_istable(L, -1))
+	{
+		luaL_loadbuffer(L, randomgenerator_lua, sizeof(randomgenerator_lua), "wrap_RandomGenerator.lua");
+		lua_pushvalue(L, -2);
+		lua_pushlightuserdata(L, &ffifuncs);
+		lua_call(L, 2, 0);
+	}
+
+	// Pop the metatable.
+	lua_pop(L, 1);
+
+	return n;
 }
 }
 
 
 } // math
 } // math

+ 0 - 1
jni/love/src/modules/math/wrap_RandomGenerator.h

@@ -33,7 +33,6 @@ namespace math
 
 
 // Helper functions.
 // Helper functions.
 RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx);
 RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx);
-int luax_getrandom(lua_State *L, int startidx, double r);
 
 
 RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx);
 RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx);
 extern "C" int luaopen_randomgenerator(lua_State *L);
 extern "C" int luaopen_randomgenerator(lua_State *L);

+ 80 - 0
jni/love/src/modules/math/wrap_RandomGenerator.lua

@@ -0,0 +1,80 @@
+R"luastring"--(
+-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
+-- There is a matching delimiter at the bottom of the file.
+
+--[[
+Copyright (c) 2006-2015 LOVE Development Team
+
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+--]]
+
+local RandomGenerator_mt, ffifuncspointer = ...
+local RandomGenerator = RandomGenerator_mt.__index
+
+local type, tonumber, error = type, tonumber, error
+local floor = math.floor
+
+local _random = RandomGenerator._random
+
+local function getrandom(r, l, u)
+	if u ~= nil then
+		if type(r) ~= "number" then error("bad argument #1 to 'random' (number expected)", 2) end
+		if type(l) ~= "number" then error("bad argument #2 to 'random' (number expected)", 2) end
+		return floor(r * (u - l + 1)) + l
+	elseif l ~= nil then
+		if type(l) ~= "number" then error("bad argument #1 to 'random' (number expected)", 2) end
+		return floor(r * l) + 1
+	else
+		return r
+	end
+end
+
+function RandomGenerator:random(l, u)
+	local r = _random(self)
+	return getrandom(r, l, u)
+end
+
+if type(jit) ~= "table" or not jit.status() then
+	-- LuaJIT's FFI is *much* slower than LOVE's regular methods when the JIT
+	-- compiler is disabled.
+	return
+end
+
+local status, ffi = pcall(require, "ffi")
+if not status then return end
+
+pcall(ffi.cdef, [[
+typedef struct Proxy Proxy;
+
+typedef struct FFI_RandomGenerator
+{
+	double (*random)(Proxy *p);
+} FFI_RandomGenerator;
+]])
+
+local ffifuncs = ffi.cast("FFI_RandomGenerator *", ffifuncspointer)
+
+
+-- Overwrite some regular love.math functions with FFI implementations.
+
+function RandomGenerator:random(l, u)
+	local r = tonumber(ffifuncs.random(self))
+	return getrandom(r, l, u)
+end
+
+-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
+--)luastring"--"

+ 1 - 1
jni/love/src/modules/sound/lullaby/ModPlugDecoder.h

@@ -30,7 +30,7 @@
 #include "Decoder.h"
 #include "Decoder.h"
 
 
 // libmodplug
 // libmodplug
-#ifdef LOVE_ANDROID
+#if defined(LOVE_ANDROID) || defined(LOVE_IOS)
 #include <modplug.h>
 #include <modplug.h>
 #else
 #else
 #include <libmodplug/modplug.h>
 #include <libmodplug/modplug.h>

+ 113 - 122
jni/love/src/modules/window/sdl/Window.cpp

@@ -90,7 +90,7 @@ void Window::setGLFramebufferAttributes(int msaa, bool sRGB)
 	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
 	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
 	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
 	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
 	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
+	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
 	SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
 	SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
 
 
 	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (msaa > 0) ? 1 : 0);
 	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (msaa > 0) ? 1 : 0);
@@ -122,9 +122,9 @@ void Window::setGLContextAttributes(const ContextAttribs &attribs)
 	int contextflags = 0;
 	int contextflags = 0;
 
 
 	if (attribs.gles)
 	if (attribs.gles)
-		profilemask |= SDL_GL_CONTEXT_PROFILE_ES;
+		profilemask = SDL_GL_CONTEXT_PROFILE_ES;
 	else if (attribs.debug)
 	else if (attribs.debug)
-		profilemask |= SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
+		profilemask = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
 
 
 	if (attribs.debug)
 	if (attribs.debug)
 		contextflags |= SDL_GL_CONTEXT_DEBUG_FLAG;
 		contextflags |= SDL_GL_CONTEXT_DEBUG_FLAG;
@@ -237,21 +237,24 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 	if (preferGLES)
 	if (preferGLES)
 		std::rotate(attribslist.begin(), attribslist.begin() + 1, attribslist.end());
 		std::rotate(attribslist.begin(), attribslist.begin() + 1, attribslist.end());
 
 
-	if (context)
-	{
-		SDL_GL_DeleteContext(context);
-		context = nullptr;
-	}
-
 	std::string windowerror;
 	std::string windowerror;
+	std::string contexterror;
 	std::string glversion;
 	std::string glversion;
 
 
-	// Try each context profile in order.
-	for (ContextAttribs attribs : attribslist)
+	// Unfortunately some OpenGL context settings are part of the internal
+	// window state in the Windows and Linux SDL backends, so we have to
+	// recreate the window when we want to change those settings...
+	// Also, apparently some Intel drivers on Windows give back a Microsoft
+	// OpenGL 1.1 software renderer context when high MSAA values are requested!
+
+	const auto create = [&](ContextAttribs attribs) -> bool
 	{
 	{
-		// Unfortunately some OpenGL context settings are part of the internal
-		// window state in the Windows and Linux SDL backends, so we have to
-		// recreate the window when we want to change those settings...
+		if (context)
+		{
+			SDL_GL_DeleteContext(context);
+			context = nullptr;
+		}
+
 		if (window)
 		if (window)
 		{
 		{
 			SDL_DestroyWindow(window);
 			SDL_DestroyWindow(window);
@@ -259,20 +262,55 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 			window = nullptr;
 			window = nullptr;
 		}
 		}
 
 
+		window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags);
+
+		if (!window)
+		{
+			windowerror = std::string(SDL_GetError());
+			return false;
+		}
+
+		context = SDL_GL_CreateContext(window);
+
+		if (!context)
+			contexterror = std::string(SDL_GetError());
+
+		// Make sure the context's version is at least what we requested.
+		if (context && !checkGLVersion(attribs, glversion))
+		{
+			SDL_GL_DeleteContext(context);
+			context = nullptr;
+		}
+
+		if (!context)
+		{
+			SDL_DestroyWindow(window);
+			window = nullptr;
+			return false;
+		}
+
+		return true;
+	};
+
+	// Try each context profile in order.
+	for (ContextAttribs attribs : attribslist)
+	{
 		int curMSAA  = msaa;
 		int curMSAA  = msaa;
 		bool curSRGB = love::graphics::isGammaCorrect();
 		bool curSRGB = love::graphics::isGammaCorrect();
 
 
 		setGLFramebufferAttributes(curMSAA, curSRGB);
 		setGLFramebufferAttributes(curMSAA, curSRGB);
 		setGLContextAttributes(attribs);
 		setGLContextAttributes(attribs);
 
 
-		window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags);
+		windowerror.clear();
+		contexterror.clear();
+
+		create(attribs);
 
 
 		if (!window && curMSAA > 0)
 		if (!window && curMSAA > 0)
 		{
 		{
 			// The MSAA setting could have caused the failure.
 			// The MSAA setting could have caused the failure.
 			setGLFramebufferAttributes(0, curSRGB);
 			setGLFramebufferAttributes(0, curSRGB);
-			window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags);
-			if (window)
+			if (create(attribs))
 				curMSAA = 0;
 				curMSAA = 0;
 		}
 		}
 
 
@@ -280,8 +318,7 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 		{
 		{
 			// same with sRGB.
 			// same with sRGB.
 			setGLFramebufferAttributes(curMSAA, false);
 			setGLFramebufferAttributes(curMSAA, false);
-			window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags);
-			if (window)
+			if (create(attribs))
 				curSRGB = false;
 				curSRGB = false;
 		}
 		}
 
 
@@ -289,60 +326,14 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 		{
 		{
 			// Or both!
 			// Or both!
 			setGLFramebufferAttributes(0, false);
 			setGLFramebufferAttributes(0, false);
-			window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags);
-			if (window)
+			if (create(attribs))
 			{
 			{
 				curMSAA = 0;
 				curMSAA = 0;
 				curSRGB = false;
 				curSRGB = false;
 			}
 			}
 		}
 		}
 
 
-		// Immediately try the next context profile if window creation failed.
-		if (!window)
-		{
-			windowerror = std::string(SDL_GetError());
-			continue;
-		}
-
-		windowerror.clear();
-
-		context = SDL_GL_CreateContext(window);
-
-		if (!context && curMSAA > 0)
-		{
-			// MSAA and sRGB settings can also cause CreateContext to fail, on
-			// certain SDL backends.
-			setGLFramebufferAttributes(0, curSRGB);
-			context = SDL_GL_CreateContext(window);
-		}
-
-		if (!context && curSRGB)
-		{
-			setGLFramebufferAttributes(curMSAA, false);
-			context = SDL_GL_CreateContext(window);
-		}
-
-		if (!context && curMSAA > 0 && curSRGB)
-		{
-			setGLFramebufferAttributes(0, false);
-			context = SDL_GL_CreateContext(window);
-		}
-
-		if (!context && attribs.debug)
-		{
-			attribs.debug = false;
-			setGLContextAttributes(attribs);
-			context = SDL_GL_CreateContext(window);
-		}
-
-		// Make sure the context's version is at least what we requested.
-		if (context && !checkGLVersion(attribs, glversion))
-		{
-			SDL_GL_DeleteContext(context);
-			context = nullptr;
-		}
-
-		if (context)
+		if (window && context)
 		{
 		{
 			love::graphics::setGammaCorrect(curSRGB);
 			love::graphics::setGammaCorrect(curSRGB);
 			break;
 			break;
@@ -370,6 +361,8 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 			std::string title = "Unable to initialize OpenGL";
 			std::string title = "Unable to initialize OpenGL";
 			std::string message = "This program requires a graphics card and video drivers which support OpenGL 2.1 or OpenGL ES 2.";
 			std::string message = "This program requires a graphics card and video drivers which support OpenGL 2.1 or OpenGL ES 2.";
 
 
+			if (!contexterror.empty())
+				message += "\n\nOpenGL context creation error: " + contexterror;
 			if (!glversion.empty())
 			if (!glversion.empty())
 				message += " \n\nDetected OpenGL version: " + glversion;
 				message += " \n\nDetected OpenGL version: " + glversion;
 
 
@@ -478,7 +471,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 		return false;
 		return false;
 
 
 	// Make sure the window keeps any previously set icon.
 	// Make sure the window keeps any previously set icon.
-	setIcon(curMode.icon.get());
+	setIcon(icon.get());
 
 
 	// Make sure the mouse keeps its previous grab setting.
 	// Make sure the mouse keeps its previous grab setting.
 	setMouseGrab(mouseGrabbed);
 	setMouseGrab(mouseGrabbed);
@@ -497,7 +490,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 
 
 	auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 	auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 	if (gfx != nullptr)
 	if (gfx != nullptr)
-		gfx->setMode(curMode.pixelwidth, curMode.pixelheight);
+		gfx->setMode(pixelWidth, pixelHeight);
 
 
 #ifdef LOVE_ANDROID
 #ifdef LOVE_ANDROID
 		love::android::setImmersive(f.fullscreen);
 		love::android::setImmersive(f.fullscreen);
@@ -511,14 +504,14 @@ bool Window::onSizeChanged(int width, int height)
 	if (!window)
 	if (!window)
 		return false;
 		return false;
 
 
-	curMode.width = width;
-	curMode.height = height;
+	windowWidth = width;
+	windowHeight = height;
 
 
-	SDL_GL_GetDrawableSize(window, &curMode.pixelwidth, &curMode.pixelheight);
+	SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 
 
 	auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 	auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 	if (gfx != nullptr)
 	if (gfx != nullptr)
-		gfx->setViewportSize(curMode.pixelwidth, curMode.pixelheight);
+		gfx->setViewportSize(pixelWidth, pixelHeight);
 
 
 	return true;
 	return true;
 }
 }
@@ -528,49 +521,43 @@ void Window::updateSettings(const WindowSettings &newsettings)
 	Uint32 wflags = SDL_GetWindowFlags(window);
 	Uint32 wflags = SDL_GetWindowFlags(window);
 
 
 	// Set the new display mode as the current display mode.
 	// Set the new display mode as the current display mode.
-	SDL_GetWindowSize(window, &curMode.width, &curMode.height);
-	SDL_GL_GetDrawableSize(window, &curMode.pixelwidth, &curMode.pixelheight);
+	SDL_GetWindowSize(window, &windowWidth, &windowHeight);
+	SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
 
 
 	if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
 	if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
 	{
 	{
-		curMode.settings.fullscreen = true;
-		curMode.settings.fstype = FULLSCREEN_DESKTOP;
+		settings.fullscreen = true;
+		settings.fstype = FULLSCREEN_DESKTOP;
 	}
 	}
 	else if ((wflags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)
 	else if ((wflags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)
 	{
 	{
-		curMode.settings.fullscreen = true;
-		curMode.settings.fstype = FULLSCREEN_EXCLUSIVE;
+		settings.fullscreen = true;
+		settings.fstype = FULLSCREEN_EXCLUSIVE;
 	}
 	}
 	else
 	else
 	{
 	{
-		curMode.settings.fullscreen = false;
-		curMode.settings.fstype = newsettings.fstype;
+		settings.fullscreen = false;
+		settings.fstype = newsettings.fstype;
 	}
 	}
 
 
 #ifdef LOVE_ANDROID
 #ifdef LOVE_ANDROID
-	curMode.settings.fullscreen = love::android::getImmersive();
+	settings.fullscreen = love::android::getImmersive();
 #endif
 #endif
 
 
-	// The min width/height is set to 0 internally in SDL when in fullscreen.
-	if (curMode.settings.fullscreen)
-	{
-		curMode.settings.minwidth = newsettings.minwidth;
-		curMode.settings.minheight = newsettings.minheight;
-	}
-	else
-		SDL_GetWindowMinimumSize(window, &curMode.settings.minwidth, &curMode.settings.minheight);
+	// SDL_GetWindowMinimumSize gives back 0,0 sometimes...
+	settings.minwidth = newsettings.minwidth;
+	settings.minheight = newsettings.minheight;
 
 
-	curMode.settings.resizable = (wflags & SDL_WINDOW_RESIZABLE) != 0;
-	curMode.settings.borderless = (wflags & SDL_WINDOW_BORDERLESS) != 0;
-	curMode.settings.centered = newsettings.centered;
+	settings.resizable = (wflags & SDL_WINDOW_RESIZABLE) != 0;
+	settings.borderless = (wflags & SDL_WINDOW_BORDERLESS) != 0;
+	settings.centered = newsettings.centered;
 
 
-	getPosition(curMode.settings.x, curMode.settings.y, curMode.settings.display);
+	getPosition(settings.x, settings.y, settings.display);
 
 
-	curMode.settings.highdpi = (wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
+	settings.highdpi = (wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
 
 
-	// Only minimize on focus loss if the window is in exclusive-fullscreen
-	// mode.
-	if (curMode.settings.fullscreen && curMode.settings.fstype == FULLSCREEN_EXCLUSIVE)
+	// Only minimize on focus loss if the window is in exclusive-fullscreen mode
+	if (settings.fullscreen && settings.fstype == FULLSCREEN_EXCLUSIVE)
 		SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1");
 		SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1");
 	else
 	else
 		SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
 		SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
@@ -581,25 +568,25 @@ void Window::updateSettings(const WindowSettings &newsettings)
 	SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
 	SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
 	SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
 	SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
 
 
-	curMode.settings.msaa = (buffers > 0 ? samples : 0);
-	curMode.settings.vsync = SDL_GL_GetSwapInterval() != 0;
+	settings.msaa = (buffers > 0 ? samples : 0);
+	settings.vsync = SDL_GL_GetSwapInterval() != 0;
 
 
 	SDL_DisplayMode dmode = {};
 	SDL_DisplayMode dmode = {};
-	SDL_GetCurrentDisplayMode(curMode.settings.display, &dmode);
+	SDL_GetCurrentDisplayMode(settings.display, &dmode);
 
 
 	// May be 0 if the refresh rate can't be determined.
 	// May be 0 if the refresh rate can't be determined.
-	curMode.settings.refreshrate = (double) dmode.refresh_rate;
+	settings.refreshrate = (double) dmode.refresh_rate;
 }
 }
 
 
-void Window::getWindow(int &width, int &height, WindowSettings &settings)
+void Window::getWindow(int &width, int &height, WindowSettings &newsettings)
 {
 {
 	// The window might have been modified (moved, resized, etc.) by the user.
 	// The window might have been modified (moved, resized, etc.) by the user.
 	if (window)
 	if (window)
-		updateSettings(curMode.settings);
+		updateSettings(settings);
 
 
-	width = curMode.width;
-	height = curMode.height;
-	settings = curMode.settings;
+	width = windowWidth;
+	height = windowHeight;
+	newsettings = settings;
 }
 }
 
 
 void Window::close()
 void Window::close()
@@ -632,7 +619,7 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
 	if (!window)
 	if (!window)
 		return false;
 		return false;
 
 
-	WindowSettings newsettings = curMode.settings;
+	WindowSettings newsettings = settings;
 	newsettings.fullscreen = fullscreen;
 	newsettings.fullscreen = fullscreen;
 	newsettings.fstype = fstype;
 	newsettings.fstype = fstype;
 
 
@@ -647,8 +634,8 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
 			sdlflags = SDL_WINDOW_FULLSCREEN;
 			sdlflags = SDL_WINDOW_FULLSCREEN;
 
 
 			SDL_DisplayMode mode = {};
 			SDL_DisplayMode mode = {};
-			mode.w = curMode.width;
-			mode.h = curMode.height;
+			mode.w = windowWidth;
+			mode.h = windowHeight;
 
 
 			SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(window), &mode, &mode);
 			SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(window), &mode, &mode);
 			SDL_SetWindowDisplayMode(window, &mode);
 			SDL_SetWindowDisplayMode(window, &mode);
@@ -664,10 +651,14 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
 		SDL_GL_MakeCurrent(window, context);
 		SDL_GL_MakeCurrent(window, context);
 		updateSettings(newsettings);
 		updateSettings(newsettings);
 
 
+		// Apparently this gets un-set when we exit fullscreen (at least in OS X).
+		if (!fullscreen)
+			SDL_SetWindowMinimumSize(window, settings.minwidth, settings.minheight);
+
 		// Update the viewport size now instead of waiting for event polling.
 		// Update the viewport size now instead of waiting for event polling.
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 		if (gfx != nullptr)
 		if (gfx != nullptr)
-			gfx->setViewportSize(curMode.pixelwidth, curMode.pixelheight);
+			gfx->setViewportSize(pixelWidth, pixelHeight);
 
 
 		return true;
 		return true;
 	}
 	}
@@ -677,7 +668,7 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
 
 
 bool Window::setFullscreen(bool fullscreen)
 bool Window::setFullscreen(bool fullscreen)
 {
 {
-	return setFullscreen(fullscreen, curMode.settings.fstype);
+	return setFullscreen(fullscreen, settings.fstype);
 }
 }
 
 
 int Window::getDisplayCount() const
 int Window::getDisplayCount() const
@@ -749,7 +740,7 @@ void Window::setPosition(int x, int y, int displayindex)
 
 
 	SDL_SetWindowPosition(window, x, y);
 	SDL_SetWindowPosition(window, x, y);
 
 
-	curMode.settings.useposition = true;
+	settings.useposition = true;
 }
 }
 
 
 void Window::getPosition(int &x, int &y, int &displayindex)
 void Window::getPosition(int &x, int &y, int &displayindex)
@@ -801,7 +792,7 @@ bool Window::setIcon(love::image::ImageData *imgd)
 	if (!imgd)
 	if (!imgd)
 		return false;
 		return false;
 
 
-	curMode.icon.set(imgd);
+	icon.set(imgd);
 
 
 	if (!window)
 	if (!window)
 		return false;
 		return false;
@@ -842,7 +833,7 @@ bool Window::setIcon(love::image::ImageData *imgd)
 
 
 love::image::ImageData *Window::getIcon()
 love::image::ImageData *Window::getIcon()
 {
 {
-	return curMode.icon.get();
+	return icon.get();
 }
 }
 
 
 void Window::setDisplaySleepEnabled(bool enable)
 void Window::setDisplaySleepEnabled(bool enable)
@@ -917,24 +908,24 @@ bool Window::isMouseGrabbed() const
 
 
 void Window::getPixelDimensions(int &w, int &h) const
 void Window::getPixelDimensions(int &w, int &h) const
 {
 {
-	w = curMode.pixelwidth;
-	h = curMode.pixelheight;
+	w = pixelWidth;
+	h = pixelHeight;
 }
 }
 
 
 void Window::windowToPixelCoords(double *x, double *y) const
 void Window::windowToPixelCoords(double *x, double *y) const
 {
 {
 	if (x != nullptr)
 	if (x != nullptr)
-		*x = (*x) * ((double) curMode.pixelwidth / (double) curMode.width);
+		*x = (*x) * ((double) pixelWidth / (double) windowWidth);
 	if (y != nullptr)
 	if (y != nullptr)
-		*y = (*y) * ((double) curMode.pixelheight / (double) curMode.height);
+		*y = (*y) * ((double) pixelHeight / (double) windowHeight);
 }
 }
 
 
 void Window::pixelToWindowCoords(double *x, double *y) const
 void Window::pixelToWindowCoords(double *x, double *y) const
 {
 {
 	if (x != nullptr)
 	if (x != nullptr)
-		*x = (*x) * ((double) curMode.width / (double) curMode.pixelwidth);
+		*x = (*x) * ((double) windowWidth / (double) pixelWidth);
 	if (y != nullptr)
 	if (y != nullptr)
-		*y = (*y) * ((double) curMode.height / (double) curMode.pixelheight);
+		*y = (*y) * ((double) windowHeight / (double) pixelHeight);
 }
 }
 
 
 double Window::getPixelScale() const
 double Window::getPixelScale() const
@@ -942,7 +933,7 @@ double Window::getPixelScale() const
 #ifdef LOVE_ANDROID
 #ifdef LOVE_ANDROID
 	return love::android::getScreenScale();
 	return love::android::getScreenScale();
 #else
 #else
-	return (double) curMode.pixelheight / (double) curMode.height;
+	return (double) pixelHeight / (double) windowHeight;
 #endif
 #endif
 }
 }
 
 

+ 6 - 10
jni/love/src/modules/window/sdl/Window.h

@@ -131,16 +131,12 @@ private:
 
 
 	std::string title;
 	std::string title;
 
 
-	struct _currentMode
-	{
-		int width  = 800;
-		int height = 600;
-		int pixelwidth = 800;
-		int pixelheight = 600;
-		WindowSettings settings;
-		StrongRef<love::image::ImageData> icon;
-
-	} curMode;
+	int windowWidth  = 800;
+	int windowHeight = 600;
+	int pixelWidth   = 800;
+	int pixelHeight  = 600;
+	WindowSettings settings;
+	StrongRef<love::image::ImageData> icon;
 
 
 	bool open;
 	bool open;
 
 

+ 4 - 4
jni/love/src/scripts/boot.lua

@@ -386,13 +386,13 @@ function love.init()
 	end
 	end
 
 
 	-- If config file exists, load it and allow it to update config table.
 	-- If config file exists, load it and allow it to update config table.
-	if not love.conf and love.filesystem and love.filesystem.isFile("conf.lua") then
-		require("conf")
+	local confok, conferr
+	if (not love.conf) and love.filesystem and love.filesystem.isFile("conf.lua") then
+		confok, conferr = pcall(require, "conf")
 	end
 	end
 
 
 	-- Yes, conf.lua might not exist, but there are other ways of making
 	-- Yes, conf.lua might not exist, but there are other ways of making
 	-- love.conf appear, so we should check for it anyway.
 	-- love.conf appear, so we should check for it anyway.
-	local confok, conferr
 	if love.conf then
 	if love.conf then
 		confok, conferr = pcall(love.conf, c)
 		confok, conferr = pcall(love.conf, c)
 		-- If love.conf errors, we'll trigger the error after loading modules so
 		-- If love.conf errors, we'll trigger the error after loading modules so
@@ -635,7 +635,7 @@ function love.errhand(msg)
 				return
 				return
 			elseif e == "keypressed" and a == "escape" then
 			elseif e == "keypressed" and a == "escape" then
 				return
 				return
-			elseif e == "touchreleased" then
+			elseif e == "touchpressed" then
 				local name = love.window.getTitle()
 				local name = love.window.getTitle()
 				if #name == 0 or name == "Untitled" then name = "Game" end
 				if #name == 0 or name == "Untitled" then name = "Game" end
 				local buttons = {"OK", "Cancel"}
 				local buttons = {"OK", "Cancel"}

+ 11 - 9
jni/love/src/scripts/boot.lua.h

@@ -708,12 +708,16 @@ const unsigned char boot_lua[] =
 	0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x2c, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x69, 0x74, 0x20, 0x61, 
 	0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x2c, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x69, 0x74, 0x20, 0x61, 
 	0x6e, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, 
 	0x6e, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, 
 	0x61, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a,
 	0x61, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a,
-	0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20, 
-	0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 
-	0x6d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 
-	0x74, 0x65, 0x6d, 0x2e, 0x69, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x28, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x6c, 
-	0x75, 0x61, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
-	0x09, 0x09, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0x29, 0x0a,
+	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 
+	0x66, 0x65, 0x72, 0x72, 0x0a,
+	0x09, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 
+	0x29, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 
+	0x74, 0x65, 0x6d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 
+	0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x69, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x28, 0x22, 0x63, 0x6f, 0x6e, 0x66, 
+	0x2e, 0x6c, 0x75, 0x61, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x65, 0x72, 0x72, 0x20, 
+	0x3d, 0x20, 0x70, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x2c, 0x20, 0x22, 
+	0x63, 0x6f, 0x6e, 0x66, 0x22, 0x29, 0x0a,
 	0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x2d, 0x2d, 0x20, 0x59, 0x65, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x6c, 0x75, 0x61, 0x20, 
 	0x09, 0x2d, 0x2d, 0x20, 0x59, 0x65, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x6c, 0x75, 0x61, 0x20, 
 	0x6d, 0x69, 0x67, 0x68, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x62, 
 	0x6d, 0x69, 0x67, 0x68, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x62, 
@@ -723,8 +727,6 @@ const unsigned char boot_lua[] =
 	0x61, 0x72, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x77, 0x65, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x63, 
 	0x61, 0x72, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x77, 0x65, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x63, 
 	0x68, 0x65, 0x63, 0x6b, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x77, 0x61, 0x79, 
 	0x68, 0x65, 0x63, 0x6b, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x77, 0x61, 0x79, 
 	0x2e, 0x0a,
 	0x2e, 0x0a,
-	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 
-	0x66, 0x65, 0x72, 0x72, 0x0a,
 	0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x09, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x65, 0x72, 0x72, 0x20, 
 	0x09, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x65, 0x72, 0x72, 0x20, 
 	0x3d, 0x20, 0x70, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x2c, 
 	0x3d, 0x20, 0x70, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x2c, 
@@ -1154,7 +1156,7 @@ const unsigned char boot_lua[] =
 	0x20, 0x22, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x20, 0x22, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x0a,
 	0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x0a,
 	0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x74, 0x6f, 
 	0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x74, 0x6f, 
-	0x75, 0x63, 0x68, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+	0x75, 0x63, 0x68, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x09, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x6c, 
 	0x09, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x6c, 
 	0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x67, 0x65, 0x74, 0x54, 0x69, 0x74, 0x6c, 
 	0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x67, 0x65, 0x74, 0x54, 0x69, 0x74, 0x6c, 
 	0x65, 0x28, 0x29, 0x0a,
 	0x65, 0x28, 0x29, 0x0a,

+ 7 - 1
jni/love/src/scripts/nogame.lua

@@ -887,7 +887,7 @@ function love.nogame()
 	function Mosaic:addGeneration()
 	function Mosaic:addGeneration()
 		self.generation = self.generation + 1
 		self.generation = self.generation + 1
 		if self.generation % 5 == 0 then
 		if self.generation % 5 == 0 then
-			if love.math.random(0, 100) < 30 then
+			if love.math.random(0, 100) < 60 then
 				self.generator = self.generators[love.math.random(2, #self.generators)]
 				self.generator = self.generators[love.math.random(2, #self.generators)]
 			else
 			else
 				self.generator = self.generators[1]
 				self.generator = self.generators[1]
@@ -975,6 +975,12 @@ function love.nogame()
 		g_entities.toast:center()
 		g_entities.toast:center()
 	end
 	end
 
 
+	function love.keypressed(key)
+		if key == "escape" then
+			love.event.quit()
+		end
+	end
+
 	function love.keyreleased(key)
 	function love.keyreleased(key)
 		if key == "f" then
 		if key == "f" then
 			local is_fs = love.window.getFullscreen()
 			local is_fs = love.window.getFullscreen()

+ 9 - 1
jni/love/src/scripts/nogame.lua.h

@@ -3471,7 +3471,7 @@ const unsigned char nogame_lua[] =
 	0x09, 0x09, 0x69, 0x66, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 
 	0x09, 0x09, 0x69, 0x66, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 
 	0x6f, 0x6e, 0x20, 0x25, 0x20, 0x35, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x6f, 0x6e, 0x20, 0x25, 0x20, 0x35, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x61, 
 	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x61, 
-	0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x30, 0x2c, 0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x3c, 0x20, 0x33, 0x30, 0x20, 
+	0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x30, 0x2c, 0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x3c, 0x20, 0x36, 0x30, 0x20, 
 	0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x74, 0x68, 0x65, 0x6e, 0x0a,
 	0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 
 	0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 
 	0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 
 	0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 
@@ -3649,6 +3649,14 @@ const unsigned char nogame_lua[] =
 	0x3a, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x28, 0x29, 0x0a,
 	0x3a, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x28, 0x29, 0x0a,
 	0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6b, 0x65, 0x79, 
 	0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6b, 0x65, 0x79, 
+	0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x28, 0x6b, 0x65, 0x79, 0x29, 0x0a,
+	0x09, 0x09, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x65, 0x73, 0x63, 0x61, 0x70, 
+	0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x71, 0x75, 0x69, 0x74, 
+	0x28, 0x29, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6b, 0x65, 0x79, 
 	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x28, 0x6b, 0x65, 0x79, 0x29, 0x0a,
 	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x28, 0x6b, 0x65, 0x79, 0x29, 0x0a,
 	0x09, 0x09, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x66, 0x22, 0x20, 0x74, 0x68, 
 	0x09, 0x09, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x66, 0x22, 0x20, 0x74, 0x68, 
 	0x65, 0x6e, 0x0a,
 	0x65, 0x6e, 0x0a,