Commits

Alex Szpakowski  committed f1d42e6

Added love.system.openURL (resolves issue #863)

  • Participants
  • Parent commits ead96c7

Comments (0)

Files changed (7)

File platform/macosx/love-framework.xcodeproj/project.pbxproj

 		FA9B4A0816E1578300074F42 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0716E1578300074F42 /* SDL2.framework */; };
 		FA9FC0B0173D6E3E005027FF /* wrap_Window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA9FC0AE173D6E3E005027FF /* wrap_Window.cpp */; };
 		FA9FC0B1173D6E3E005027FF /* wrap_Window.h in Headers */ = {isa = PBXBuildFile; fileRef = FA9FC0AF173D6E3E005027FF /* wrap_Window.h */; };
+		FAA627CE18E7E1560080752D /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAA627CD18E7E1560080752D /* CoreServices.framework */; };
 		FAAC6B02170A373B008A61C5 /* CompressedData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAAC6B00170A373A008A61C5 /* CompressedData.cpp */; };
 		FAAC6B03170A373B008A61C5 /* CompressedData.h in Headers */ = {isa = PBXBuildFile; fileRef = FAAC6B01170A373A008A61C5 /* CompressedData.h */; };
 		FAAFF04416CB11C700CCDE45 /* OpenAL-Soft.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */; };
 		FA9B4A0716E1578300074F42 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = /Library/Frameworks/SDL2.framework; sourceTree = "<absolute>"; };
 		FA9FC0AE173D6E3E005027FF /* wrap_Window.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Window.cpp; sourceTree = "<group>"; };
 		FA9FC0AF173D6E3E005027FF /* wrap_Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Window.h; sourceTree = "<group>"; };
+		FAA627CD18E7E1560080752D /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
 		FAAC6B00170A373A008A61C5 /* CompressedData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompressedData.cpp; sourceTree = "<group>"; };
 		FAAC6B01170A373A008A61C5 /* CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressedData.h; sourceTree = "<group>"; };
 		FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "OpenAL-Soft.framework"; path = "/Library/Frameworks/OpenAL-Soft.framework"; sourceTree = "<absolute>"; };
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				FAA627CE18E7E1560080752D /* CoreServices.framework in Frameworks */,
 				FA9B4A0816E1578300074F42 /* SDL2.framework in Frameworks */,
 				FAAFF04416CB11C700CCDE45 /* OpenAL-Soft.framework in Frameworks */,
 				FA577AB016C7507900860150 /* Cocoa.framework in Frameworks */,
 		FA577A6616C7199700860150 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
-				FA9B4A0716E1578300074F42 /* SDL2.framework */,
 				FA577A7916C71A1700860150 /* Cocoa.framework */,
+				FAA627CD18E7E1560080752D /* CoreServices.framework */,
 				FA577A6716C719D900860150 /* FreeType.framework */,
 				FA577A6916C719DE00860150 /* IL.framework */,
 				FA577A8216C71A5300860150 /* libmodplug.framework */,
 				FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */,
 				FA577A7C16C71A2600860150 /* OpenGL.framework */,
 				FA577A7316C719F900860150 /* physfs.framework */,
+				FA9B4A0716E1578300074F42 /* SDL2.framework */,
 				FA577A7716C71A0800860150 /* Vorbis.framework */,
 			);
 			name = Frameworks;

File src/common/utf8.cpp

 	return ret;
 }
 
+std::wstring to_widestr(const std::string &str)
+{
+	if (str.empty())
+		return std::wstring();
+
+	int wide_size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int) str.length(), nullptr, 0);
+
+	if (wide_size == 0)
+		return std::wstring();
+
+	std::wstring widestr;
+	widestr.resize(wide_size);
+
+	int ok = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int) str.length(), &widestr[0], widestr.length());
+
+	if (!ok)
+		return std::wstring();
+
+	return widestr;
+}
+
 void replace_char(std::string &str, char find, char replace)
 {
 	int length = str.length();

File src/common/utf8.h

 std::string to_utf8(LPCWSTR wstr);
 
 /**
+ * Convert a UTF-8 encoded string to a wide string.
+ * @param str The UTF-8 string.
+ * @return A wide string.
+**/
+std::wstring to_widestr(const std::string &str);
+
+/**
  * Replace all occurences of 'find' with 'replace' in a string.
  * @param str The string to modify.
  * @param find The character to match.

File src/modules/system/System.cpp

  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
+// LOVE
+#include "common/config.h"
 #include "System.h"
 
+#if defined(LOVE_MACOSX)
+#include <CoreServices/CoreServices.h>
+#elif defined(LOVE_LINUX)
+#include <stdlib.h>
+#include <unistd.h>
+#elif defined(LOVE_WINDOWS)
+#include "common/utf8.h"
+#include <shlobj.h>
+#include <shellapi.h>
+#pragma comment(lib, "shell32.lib")
+#endif
+
 namespace love
 {
 namespace system
 
 std::string System::getOS() const
 {
-#ifdef LOVE_MACOSX
+#if defined(LOVE_MACOSX)
 	return "OS X";
-#elif LOVE_WINDOWS
+#elif defined(LOVE_WINDOWS)
 	return "Windows";
-#elif LOVE_LINUX
+#elif defined(LOVE_LINUX)
 	return "Linux";
 #else
 	return "Unknown";
 #endif
 }
 
+bool System::openURL(const std::string &url) const
+{
+	bool success = false;
+
+#if defined(LOVE_MACOSX)
+
+	// We could be lazy and use system("open " + url), but this is safer.
+	CFURLRef cfurl = CFURLCreateWithBytes(nullptr,
+	                                      (const UInt8 *) url.c_str(),
+	                                      url.length(),
+	                                      kCFStringEncodingUTF8,
+	                                      nullptr);
+
+	success = LSOpenCFURLRef(cfurl, nullptr) == noErr;
+	CFRelease(cfurl);
+
+#elif defined(LOVE_LINUX)
+
+	// Spawn a child process, which we'll replace with xdg-open.
+	pid_t pid = vfork();
+
+	if (pid == 0) // Child process.
+	{
+		// Replace the child process with xdg-open and pass in the URL.
+		execlp("xdg-open", "xdg-open", url.c_str(), nullptr);
+
+		// exec will only return if it errored, so we should exit with non-zero.
+		_exit(1);
+	}
+	else if (pid > 0) // Parent process.
+	{
+		// Wait for xdg-open to complete (or fail.)
+		int status = 0;
+		if (waitpid(pid, &status, 0) == pid)
+			success = (status == 0);
+		else
+			success = false;
+	}
+	else
+	{
+		// vfork() failed.
+		success = false;
+	}
+
+#elif defined(LOVE_WINDOWS)
+
+	// Unicode-aware WinAPI functions don't accept UTF-8, so we need to convert.
+	std::wstring wurl = to_widestr(url);
+
+	HINSTANCE result = ShellExecuteW(nullptr,
+	                                 L"open",
+	                                 wurl.c_str(),
+	                                 nullptr,
+	                                 nullptr,
+	                                 SW_SHOW);
+
+	success = (int) result > 32;
+
+#endif
+
+	return success;
+}
+
 bool System::getConstant(const char *in, System::PowerState &out)
 {
 	return powerStates.find(in, out);

File src/modules/system/System.h

 	 **/
 	virtual PowerState getPowerInfo(int &seconds, int &percent) const = 0;
 
+	/**
+	 * Opens the specified URL with the user's default program to handle that
+	 * particular URL type.
+	 *
+	 * @param url The URL to open.
+	 *
+	 * @return Whether the URL was opened successfully.
+	 **/
+	virtual bool openURL(const std::string &url) const;
+
 	static bool getConstant(const char *in, PowerState &out);
 	static bool getConstant(PowerState in, const char *&out);
 

File src/modules/system/wrap_System.cpp

 	return 3;
 }
 
+int w_openURL(lua_State *L)
+{
+	std::string url = luax_checkstring(L, 1);
+	luax_pushboolean(L, instance->openURL(url));
+	return 1;
+}
+
 static const luaL_Reg functions[] =
 {
 	{ "getOS", w_getOS },
 	{ "setClipboardText", w_setClipboardText },
 	{ "getClipboardText", w_getClipboardText },
 	{ "getPowerInfo", w_getPowerInfo },
+	{ "openURL", w_openURL },
 	{ 0, 0 }
 };
 

File src/modules/system/wrap_System.h

 int w_setClipboardText(lua_State *L);
 int w_getClipboardText(lua_State *L);
 int w_getPowerInfo(lua_State *L);
+int w_openURL(lua_State *L);
 extern "C" LOVE_EXPORT int luaopen_love_system(lua_State *L);
 
 } // system