ソースを参照

[WIP] Mobile support (#101)

HL mobile support
Romain TISSERAND 7 年 前
コミット
983f23f126
14 ファイル変更514 行追加26 行削除
  1. 2 0
      CMakeLists.txt
  2. 39 7
      libs/sdl/gl.c
  3. 52 3
      libs/sdl/sdl.c
  4. 4 0
      libs/sdl/sdl/Event.hx
  5. 21 7
      libs/sdl/sdl/Window.hx
  6. 24 2
      src/hl.h
  7. 4 0
      src/hlc_main.c
  8. 12 1
      src/std/process.c
  9. 5 3
      src/std/socket.c
  10. 20 2
      src/std/sys.c
  11. 242 0
      src/std/sys_android.c
  12. 73 0
      src/std/sys_ios.m
  13. 1 1
      src/std/thread.c
  14. 15 0
      src/std/ucs2.c

+ 2 - 0
CMakeLists.txt

@@ -60,6 +60,8 @@ file(GLOB std_srcs
 	src/std/socket.c
     src/std/string.c
     src/std/sys.c
+    src/std/sys_android.c
+    src/std/sys_ios.m
     src/std/types.c
     src/std/ucs2.c
 	src/std/thread.c

+ 39 - 7
libs/sdl/gl.c

@@ -1,7 +1,14 @@
 #define HL_NAME(n) sdl_##n
 #include <hl.h>
 
-#if defined(__APPLE__)
+#if defined(HL_IOS) || defined (HL_TVOS)
+#	include <SDL2/SDL.h>
+#	include <SDL2/SDL_syswm.h>
+#	include <OpenGLES/ES3/gl.h>
+#	define glBindFragDataLocation(...)
+#	define glGetQueryObjectiv glGetQueryObjectuiv
+#	define glClearDepth glClearDepthf
+#elif defined(HL_MAC)
 #	include <SDL2/SDL.h>
 #	include <OpenGL/gl3.h>
 #elif defined(_WIN32)
@@ -16,6 +23,13 @@
 #	define GL_IMPORT(fun, t)
 #	define glBindFragDataLocation(...)
 #	define glGetQueryObjectiv glGetQueryObjectuiv
+#elif defined(HL_ANDROID)
+#	include <SDL.h>
+#	include <GLES3/gl3.h>
+#	include <GLES3/gl3ext.h>
+#	define glBindFragDataLocation(...)
+#	define glGetQueryObjectiv glGetQueryObjectuiv
+#	define glClearDepth glClearDepthf
 #else
 #	include <SDL2/SDL.h>
 #	include <GL/glu.h>
@@ -380,8 +394,17 @@ HL_PRIM vdynamic *HL_NAME(gl_create_framebuffer)() {
 }
 
 HL_PRIM void HL_NAME(gl_bind_framebuffer)( int target, vdynamic *f ) {
-	GLOG("%d,%d",target,ZIDX(f));
-	glBindFramebuffer(target, ZIDX(f));
+	unsigned int id = ZIDX(f);
+	GLOG("%d,%d",target,id);
+#if	defined(HL_IOS) || defined(HL_TVOS)
+	if ( id==0 ) {
+		SDL_SysWMinfo info;
+		SDL_VERSION(&info.version);
+		SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
+		id = info.info.uikit.framebuffer;
+	}
+#endif
+	glBindFramebuffer(target, id);
 }
 
 HL_PRIM void HL_NAME(gl_framebuffer_texture2d)( int target, int attach, int texTarget, vdynamic *t, int level ) {
@@ -420,8 +443,17 @@ HL_PRIM vdynamic *HL_NAME(gl_create_renderbuffer)() {
 }
 
 HL_PRIM void HL_NAME(gl_bind_renderbuffer)( int target, vdynamic *r ) {
-	GLOG("%d,%d",target,ZIDX(r));
-	glBindRenderbuffer(target, ZIDX(r));
+	unsigned int id = ZIDX(r);
+	GLOG("%d,%d",target,id);
+#if	defined(HL_IOS) || defined(HL_TVOS)
+	if ( id==0 ) {
+		SDL_SysWMinfo info;
+		SDL_VERSION(&info.version);
+		SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
+		id = info.info.uikit.colorbuffer;
+	}
+#endif
+	glBindRenderbuffer(GL_RENDERBUFFER, id);
 }
 
 HL_PRIM void HL_NAME(gl_renderbuffer_storage)( int target, int format, int width, int height ) {
@@ -553,14 +585,14 @@ HL_PRIM bool HL_NAME(gl_query_result_available)( vdynamic *q ) {
 
 HL_PRIM double HL_NAME(gl_query_result)( vdynamic *q ) {
 	GLuint64 v = -1;
-#	ifndef HL_MESA
+#	if !defined(HL_MESA) && !defined(HL_MOBILE)
 	glGetQueryObjectui64v(q->v.i, GL_QUERY_RESULT, &v);
 #	endif
 	return (double)v;
 }
 
 HL_PRIM void HL_NAME(gl_query_counter)( vdynamic *q, int target ) {
-#	ifndef HL_MESA
+#	if !defined(HL_MESA) && !defined(HL_MOBILE)
 	glQueryCounter(q->v.i, target);
 #	endif
 }

+ 52 - 3
libs/sdl/sdl.c

@@ -2,13 +2,19 @@
 
 #include <hl.h>
 
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__ANDROID__)
 #	include <SDL.h>
 #	include <SDL_syswm.h>
 #else
 #	include <SDL2/SDL.h>
 #endif
 
+#if defined (HL_IOS) || defined(HL_TVOS)
+#	include <OpenGLES/ES3/gl.h>
+#	include <OpenGLES/ES3/glext.h>
+#	include <SDL2/SDL_syswm.h>
+#endif
+
 #ifndef SDL_MAJOR_VERSION
 #	error "SDL2 SDK not found in hl/include/sdl/"
 #endif
@@ -36,7 +42,10 @@ typedef enum {
 	GControllerRemoved,
 	GControllerDown,
 	GControllerUp,
-	GControllerAxis
+	GControllerAxis,
+	TouchDown = 200,
+	TouchUp,
+	TouchMove,
 } event_type;
 
 typedef enum {
@@ -67,6 +76,7 @@ typedef struct {
 	bool keyRepeat;
 	int controller;
 	int value;
+	int fingerId;
 } event_data;
 
 HL_PRIM bool HL_NAME(init_once)() {
@@ -80,9 +90,15 @@ HL_PRIM bool HL_NAME(init_once)() {
 	timeBeginPeriod(1);
 #	endif
 	// default GL parameters
+#ifdef HL_MOBILE
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+#else
 	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
 	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
 	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+#endif
 	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
 	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
@@ -142,6 +158,24 @@ HL_PRIM bool HL_NAME(event_loop)( event_data *event ) {
 			event->mouseX = e.button.x;
 			event->mouseY = e.motion.y;
 			break;
+		case SDL_FINGERDOWN:
+			event->type = TouchDown;
+			event->mouseX = e.tfinger.x*100;
+			event->mouseY = e.tfinger.y*100;
+			event->fingerId = e.tfinger.fingerId;
+			break;
+		case SDL_FINGERMOTION:
+			event->type = TouchMove;
+			event->mouseX = e.tfinger.x*100;
+			event->mouseY = e.tfinger.y*100;
+			event->fingerId = e.tfinger.fingerId;
+			break;
+		case SDL_FINGERUP:
+			event->type = TouchUp;
+			event->mouseX = e.tfinger.x*100;
+			event->mouseY = e.tfinger.y*100;
+			event->fingerId = e.tfinger.fingerId;
+			break;
 		case SDL_MOUSEWHEEL:
 			event->type = MouseWheel;
 			event->wheelDelta = e.wheel.y;
@@ -325,7 +359,14 @@ DEFINE_PRIM(_BYTES, detect_keyboard_layout, _NO_ARG);
 
 HL_PRIM SDL_Window *HL_NAME(win_create)(int width, int height) {
 	SDL_Window *w;
+	// force window to match device resolution on mobile
+#ifdef	HL_MOBILE
+	SDL_DisplayMode displayMode;
+	SDL_GetDesktopDisplayMode(0, &displayMode);
+	w = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS);
+#else
 	w = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
+#endif
 #	ifdef HL_WIN
 	// force window to show even if the debugger force process windows to be hidden
 	if( (SDL_GetWindowFlags(w) & SDL_WINDOW_INPUT_FOCUS) == 0 ) {
@@ -427,6 +468,14 @@ HL_PRIM void HL_NAME(win_resize)(SDL_Window *win, int mode) {
 
 
 HL_PRIM void HL_NAME(win_swap_window)(SDL_Window *win) {
+#if defined(HL_IOS) || defined(HL_TVOS)
+	SDL_SysWMinfo info;
+	SDL_VERSION(&info.version);
+	SDL_GetWindowWMInfo(win, &info);
+
+	glBindFramebuffer(GL_FRAMEBUFFER, info.info.uikit.framebuffer);
+	glBindRenderbuffer(GL_RENDERBUFFER,info.info.uikit.colorbuffer);
+#endif
 	SDL_GL_SwapWindow(win);
 }
 
@@ -573,4 +622,4 @@ DEFINE_PRIM(_CURSOR, cursor_create, _SURF _I32 _I32);
 DEFINE_PRIM(_CURSOR, cursor_create_system, _I32);
 DEFINE_PRIM(_VOID, free_cursor, _CURSOR);
 DEFINE_PRIM(_VOID, set_cursor, _CURSOR);
-DEFINE_PRIM(_ARR, get_devices, _NO_ARG);
+DEFINE_PRIM(_ARR, get_devices, _NO_ARG);

+ 4 - 0
libs/sdl/sdl/Event.hx

@@ -11,6 +11,7 @@ package sdl;
 	public var keyRepeat : Bool;
 	public var controller : Int;
 	public var value : Int;
+	public var fingerId : Int;
 	public function new() {
 	}
 }
@@ -31,6 +32,9 @@ package sdl;
 	var GControllerDown		= 102;
 	var GControllerUp		= 103;
 	var GControllerAxis 	= 104;
+	var TouchDown	= 200;
+	var TouchUp		= 201;
+	var TouchMove	= 202;
 }
 
 @:enum abstract WindowStateChange(Int) {

+ 21 - 7
libs/sdl/sdl/Window.hx

@@ -50,11 +50,22 @@ class Window {
 
 			var reg = ~/[0-9]+\.[0-9]+/;
 			var v : String = GL.getParameter(GL.SHADING_LANGUAGE_VERSION);
-			var shaderVersion = 130;
-			if( reg.match(v) ) {
-				var minVer = 150;
-				shaderVersion = Math.round( Std.parseFloat(reg.matched(0)) * 100 );
-				if( shaderVersion < minVer ) shaderVersion = minVer;
+
+			var glv : String = GL.getParameter(GL.VERSION);
+			var isOpenGLES : Bool = ((glv!=null) && (glv.indexOf("ES") >= 0));
+
+			var shaderVersion = 120;
+			if (isOpenGLES) {
+				if( reg.match(v) )
+					shaderVersion = hxd.Math.imin( 100, Math.round( Std.parseFloat(reg.matched(0)) * 100 ) );
+			}
+			else {
+				shaderVersion = 130;
+				if( reg.match(v) ) {
+					var minVer = 150;
+					shaderVersion = Math.round( Std.parseFloat(reg.matched(0)) * 100 );
+					if( shaderVersion < minVer ) shaderVersion = minVer;
+				}
 			}
 
 			var vertex = GL.createShader(GL.VERTEX_SHADER);
@@ -63,7 +74,10 @@ class Window {
 			if( GL.getShaderParameter(vertex, GL.COMPILE_STATUS) != 1 ) throw "Failed to compile VS ("+GL.getShaderInfoLog(vertex)+")";
 
 			var fragment = GL.createShader(GL.FRAGMENT_SHADER);
-			GL.shaderSource(fragment, ["#version " + shaderVersion, "out vec4 color; void main() { color = vec4(1.0); }"].join("\n"));
+			if (isOpenGLES)
+				GL.shaderSource(fragment, ["#version " + shaderVersion, "lowp vec4 color; void main() { color = vec4(1.0); }"].join("\n"));
+			else
+				GL.shaderSource(fragment, ["#version " + shaderVersion, "out vec4 color; void main() { color = vec4(1.0); }"].join("\n"));
 			GL.compileShader(fragment);
 			if( GL.getShaderParameter(fragment, GL.COMPILE_STATUS) != 1 ) throw "Failed to compile FS ("+GL.getShaderInfoLog(fragment)+")";
 
@@ -198,4 +212,4 @@ class Window {
 	static function setVsync( b : Bool ) {
 	}
 
-}
+}

+ 24 - 2
src/hl.h

@@ -34,7 +34,18 @@
 #endif
 
 #if defined(__APPLE__) || defined(__MACH__) || defined(macintosh)
-#	define HL_MAC
+#include <TargetConditionals.h>
+#if TARGET_OS_IOS
+#define HL_IOS
+#elif TARGET_OS_TV
+#define HL_TVOS
+#elif TARGET_OS_MAC
+#define HL_MAC
+#endif
+#endif
+
+#ifdef __ANDROID__
+#	define HL_ANDROID
 #endif
 
 #if defined(linux) || defined(__linux__)
@@ -42,6 +53,10 @@
 #	define _GNU_SOURCE
 #endif
 
+#if defined(HL_IOS) || defined(HL_ANDROID) || defined(HL_TVOS)
+#	define HL_MOBILE
+#endif
+
 #ifdef __ORBIS__
 #	define HL_PS
 #endif
@@ -188,7 +203,14 @@ typedef uint16_t uchar;
 #	define USTR(str)	u##str
 #else
 #	include <stdarg.h>
+#if defined(HL_IOS) || defined(HL_TVOS) || defined(HL_MAC)
+#include <stddef.h>
+#include <stdint.h>
+typedef uint16_t char16_t;
+typedef uint32_t char32_t;
+#else
 #	include <uchar.h>
+#endif
 typedef char16_t uchar;
 #	undef USTR
 #	define USTR(str)	u##str
@@ -212,7 +234,7 @@ C_FUNCTION_END
 #	define hl_debug_break()	if( IsDebuggerPresent() ) __debugbreak()
 #elif defined(HL_PS)
 #	define hl_debug_break()	__debugbreak()
-#elif defined(HL_LINUX)
+#elif defined(HL_LINUX) && defined(__i386__)
 #	ifdef HL_64
 #	define hl_debug_break() \
 		if( hl_detect_debugger() ) \

+ 4 - 0
src/hlc_main.c

@@ -21,6 +21,10 @@
  */
 #include <hlc.h>
 
+#if defined(HL_MOBILE) && defined(sdl__Sdl__val)
+#   include <SDL_main.h>
+#endif
+
 #ifdef _WIN32
 #	pragma warning(disable:4091)
 #	include <DbgHelp.h>

+ 12 - 1
src/std/process.c

@@ -28,7 +28,7 @@
 #	include <unistd.h>
 #	include <errno.h>
 #	if !defined(HL_MAC)
-#		if defined(HL_BSD)
+#		if defined(HL_BSD) || defined (HL_IOS) || defined (HL_TVOS)
 #			include <sys/wait.h>
 #		else
 #			include <wait.h>
@@ -136,7 +136,12 @@ HL_PRIM vprocess *hl_process_run( vbyte *cmd, varray *vargs, bool detached ) {
 	if( pipe(input) || pipe(output) || pipe(error) )
 		return NULL;
 	p = (vprocess*)hl_gc_alloc_finalizer(sizeof(vprocess));
+#ifdef HL_TVOS
+	hl_error("hl_process_run() not available for this platform");
+	p->pid = -1;
+#else
 	p->pid = fork();
+#endif
 	if( p->pid == -1 ) {
 		close(input[0]);
 		close(input[1]);
@@ -154,7 +159,11 @@ HL_PRIM vprocess *hl_process_run( vbyte *cmd, varray *vargs, bool detached ) {
 		dup2(input[0],0);
 		dup2(output[1],1);
 		dup2(error[1],2);
+#ifdef HL_TVOS
+		hl_error("hl_process_run() not available for this platform");
+#else
 		execvp(argv[0],argv);
+#endif
 		fprintf(stderr,"Command not found : %s\n",cmd);
 		exit(1);
 	}
@@ -273,6 +282,8 @@ HL_PRIM void hl_process_close( vprocess *p ) {
 HL_PRIM void hl_process_kill( vprocess *p ) {
 #	ifdef HL_WIN
 	TerminateProcess(p->pinf.hProcess,0xCDCDCDCD);
+#   elif defined(HL_IOS) || defined(HL_TVOS)
+	hl_error("hl_process_kill() not available on this platform");
 #	else
 	kill(p->pid,9);
 #	endif

+ 5 - 3
src/std/socket.c

@@ -77,7 +77,7 @@
 
 #include <hl.h>
 
-#if defined(HL_WIN) || defined(HL_MAC)
+#if defined(HL_WIN) || defined(HL_MAC) || defined(HL_IOS) || defined(HL_TVOS)
 #	define MSG_NOSIGNAL 0
 #endif
 
@@ -184,7 +184,7 @@ HL_PRIM int hl_host_resolve( vbyte *host ) {
 	ip = inet_addr((char*)host);
 	if( ip == INADDR_NONE ) {
 		struct hostent *h;
-#	if defined(HL_WIN) || defined(HL_MAC) || defined (HL_CYGWIN) || defined(HL_CONSOLE)
+#	if defined(HL_WIN) || defined(HL_MAC) || defined(HL_IOS) || defined(HL_TVOS) || defined (HL_CYGWIN) || defined(HL_CONSOLE)
 		h = gethostbyname((char*)host);
 #	else
 		struct hostent hbase;
@@ -207,8 +207,10 @@ HL_PRIM vbyte *hl_host_to_string( int ip ) {
 
 HL_PRIM vbyte *hl_host_reverse( int ip ) {
 	struct hostent *h;
-#	if defined(HL_WIN) || defined(HL_MAC) || defined(HL_CYGWIN) || defined(HL_CONSOLE)
+#	if defined(HL_WIN) || defined(HL_MAC) || defined(HL_IOS) || defined(HL_TVOS) || defined(HL_CYGWIN) || defined(HL_CONSOLE)
 	h = gethostbyaddr((char *)&ip,4,AF_INET);
+#	elif defined(__ANDROID__)
+	hl_error("hl_host_reverse() not available for this platform");
 #	else
 	struct hostent htmp;
 	int errcode;

+ 20 - 2
src/std/sys.c

@@ -113,8 +113,14 @@ HL_PRIM vbyte *hl_sys_string() {
 	return (vbyte*)USTR("Mac");
 #elif defined(HL_CONSOLE)
 	return (vbyte*)sys_platform_name();
+#elif defined(HL_IOS)
+	return (vbyte*)USTR("iOS");
+#elif defined(HL_TVOS)
+	return (vbyte*)USTR("tvOS");
+#elif defined(HL_ANDROID)
+	return (vbyte*)USTR("Android");
 #else
-#error Unknow system string
+#error Unknown system string
 #endif
 }
 
@@ -282,7 +288,12 @@ HL_PRIM int hl_sys_command( vbyte *cmd ) {
 #else
 	int status;
 	hl_blocking(true);
+#if defined(HL_IOS) || defined(HL_TVOS)
+	status = 0;
+	hl_error("hl_sys_command() not available on this platform");
+#else
 	status = system((pchar*)cmd);
+#endif
 	hl_blocking(false);
 	return WEXITSTATUS(status) | (WTERMSIG(status) << 8);
 #endif
@@ -528,7 +539,7 @@ HL_PRIM vbyte *hl_sys_exe_path() {
 		int length = readlink("/proc/self/exe", path, sizeof(path));
 		if( length < 0 )
 			return NULL;
-	    path[length] = '\0';
+		path[length] = '\0';
 		return (vbyte*)pstrdup(path,-1);
 	}
 #endif
@@ -579,6 +590,13 @@ HL_PRIM vbyte *hl_sys_hl_file() {
 	return hl_file;
 }
 
+#ifndef HL_MOBILE
+const char *hl_sys_special( const char *key ) {
+	 hl_error("Unknown sys_special key");
+	 return NULL;
+}
+DEFINE_PRIM(_BYTES, sys_special, _BYTES);
+#endif
 
 DEFINE_PRIM(_BYTES, sys_hl_file, _NO_ARG);
 DEFINE_PRIM(_BOOL, sys_utf8_path, _NO_ARG);

+ 242 - 0
src/std/sys_android.c

@@ -0,0 +1,242 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <hl.h>
+
+#if defined(HL_MOBILE) && defined(HL_ANDROID)
+
+#ifndef HL_ANDROID_ACTIVITY
+#	define HL_ANDROID_ACTIVITY "org/haxe/HashLinkActivity"
+#endif
+
+#include <jni.h>
+#include <android/log.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef HL_JNI_LOG_TAG
+#define HL_JNI_LOG_TAG "HL_JNI"
+#endif
+
+#define HL_ANDROID_EXTERNAL_STORAGE_NOT_MOUNTED	(0)
+#define HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RO	(1)
+#define HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RW	(2 | HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RO)
+
+/* #define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,HL_JNI_LOG_TAG,__VA_ARGS__) */
+/* #define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,HL_JNI_LOG_TAG,__VA_ARGS__) */
+#define LOGI(...) do {} while (0)
+#define LOGE(...) do {} while (0)
+
+/* Forward declaration for JNI thread management */
+static void hl_android_jni_thread_destructor(void*);
+
+/* pthread key for proper JVM thread handling */
+static pthread_key_t hl_java_thread_key;
+
+/* Java VM reference */
+static JavaVM* hl_java_vm;
+
+/* Main activity */
+static jclass hl_java_activity_class;
+
+/* Method signatures */
+static jmethodID hl_java_method_id_get_context;
+
+/* Paths */
+static char *hl_android_external_files_path = NULL;
+static char *hl_android_internal_files_path = NULL;
+
+/* Function to retrieve JNI environment, and dealing with threading */
+static JNIEnv* hl_android_jni_get_env(void)
+{
+	/* Always try to attach if calling from a non-attached thread */
+	JNIEnv *env;
+	if((*hl_java_vm)->AttachCurrentThread(hl_java_vm, &env, NULL) < 0) {
+		LOGE("failed to attach current thread");
+		return 0;
+	}
+	pthread_setspecific(hl_java_thread_key, (void*) env);
+
+	return env;
+}
+
+/* JNI_OnLoad is automatically called when loading shared library through System.loadLibrary() Java call */
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+	JNIEnv *env;
+	jclass cls;
+
+	hl_java_vm = vm;
+	if ((*hl_java_vm)->GetEnv(hl_java_vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+		__android_log_print(ANDROID_LOG_ERROR, HL_JNI_LOG_TAG, "Failed to get the environment using GetEnv()");
+		return -1;
+	}
+
+	/* Create pthread "destructor" pthread key to detach properly all threads */
+	if (pthread_key_create(&hl_java_thread_key, hl_android_jni_thread_destructor) != 0) {
+		__android_log_print(ANDROID_LOG_ERROR, HL_JNI_LOG_TAG, "Error initializing pthread key");
+	}
+
+	/* Make sure we are attached (we should) and setup pthread destructor */
+	env = hl_android_jni_get_env();
+
+	/* Try to retrieve local reference to our Activity class */
+	cls = (*env)->FindClass(env, hl_java_activity_class);
+	if (!cls) {
+		__android_log_print(ANDROID_LOG_ERROR, HL_JNI_LOG_TAG, "Error cannot find HashLink Activity class");
+	}
+
+	/* Create a global reference for our Activity class */
+	hl_java_activity_class = (jclass)((*env)->NewGlobalRef(env, cls));
+
+	/* Retrieve the getContext() method id */
+	hl_java_method_id_get_context = (*env)->GetStaticMethodID(env, hl_java_activity_class, "getContext","()Landroid/content/Context;");
+	if (!hl_java_method_id_get_context) {
+		__android_log_print(ANDROID_LOG_ERROR, HL_JNI_LOG_TAG, "Error cannot get getContext() method on specified Activity class (not an Activity ?)");
+	}
+	
+	return JNI_VERSION_1_4;
+}
+
+static void hl_android_jni_thread_destructor(void* value)
+{
+	/* The thread is being destroyed, detach it from the Java VM and set the hl_java_thread_key value to NULL as required */
+	JNIEnv *env = (JNIEnv*) value;
+	if (env != NULL) {
+		(*hl_java_vm)->DetachCurrentThread(hl_java_vm);
+		pthread_setspecific(hl_java_thread_key, NULL);
+	}
+}
+
+static int hl_sys_android_get_external_storage_state(void)
+{
+	jmethodID mid;
+	jclass cls;
+	jstring stateString;
+	const char *state;
+	int status = HL_ANDROID_EXTERNAL_STORAGE_NOT_MOUNTED;
+
+	JNIEnv *env = hl_android_jni_get_env();
+	if (!env) {
+		LOGE("Couldn't get Android JNIEnv !");
+		return status;
+	}
+
+	cls = (*env)->FindClass(env, "android/os/Environment");
+	mid = (*env)->GetStaticMethodID(env, cls, "getExternalStorageState", "()Ljava/lang/String;");
+	stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
+	if (!stateString) {
+		LOGE("Call to getExternalStorageState failed");
+		return status;
+	}
+
+	state = (*env)->GetStringUTFChars(env, stateString, NULL);
+	if (strcmp(state, "mounted") == 0) {
+		status = HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RW;
+	} else if (strcmp(state, "mounted_ro") == 0) {
+		status = HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RO;
+	}
+	(*env)->ReleaseStringUTFChars(env, stateString, state);
+
+	return status;
+}
+
+static char* hl_sys_android_get_absolute_path_from(const char* method, const char* signature)
+{
+		jmethodID mid;
+		jobject context;
+		jobject fileObject;
+		jstring pathString;
+		const char *path;
+		char* retrievedPath = NULL;
+
+		JNIEnv *env = hl_android_jni_get_env();
+		if (!env) {
+			LOGE("Couldn't get Android JNIEnv !");
+			return NULL;
+		}
+
+		context = (*env)->CallStaticObjectMethod(env, hl_java_activity_class, hl_java_method_id_get_context);
+		if (!context) {
+			LOGE("Couldn't get Android context!");
+			return NULL;
+		}
+
+		mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), method, signature);
+		fileObject = (*env)->CallObjectMethod(env, context, mid, NULL);
+		if (!fileObject) {
+			LOGE("Couldn't call %s%s on the specified context", method, signature);
+			return NULL;
+		}
+
+		mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject), "getAbsolutePath", "()Ljava/lang/String;");
+		pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
+		if (!pathString) {
+			LOGE("Couldn't retrieve absolute path");
+			return NULL;
+		}
+
+		/* Retrieve as C string */
+		path = (*env)->GetStringUTFChars(env, pathString, NULL);
+		retrievedPath = strdup(path);
+		(*env)->ReleaseStringUTFChars(env, pathString, path);
+
+		return retrievedPath;
+}
+
+static const char* hl_sys_android_get_external_storage_path(void)
+{
+	/* Make sure external storage is mounted (at least read-only) */
+	if (hl_sys_android_get_external_storage_state()==HL_ANDROID_EXTERNAL_STORAGE_NOT_MOUNTED)
+		return NULL;
+
+	if (!hl_android_external_files_path) {
+		hl_android_external_files_path = hl_sys_android_get_absolute_path_from("getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
+	}
+
+	return hl_android_external_files_path;
+}
+
+static const char* hl_sys_android_get_internal_storage_path(void)
+{
+	/* Internal storage is always available */
+	if (!hl_android_internal_files_path) {
+		hl_android_internal_files_path = hl_sys_android_get_absolute_path_from("getFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
+	}
+
+	return hl_android_internal_files_path;
+}
+
+const char *hl_sys_special( const char *key ) { 
+	if (strcmp(key, "android_external_storage_path")==0)
+		return hl_sys_android_get_external_storage_path();
+	else if (strcmp(key, "android_internal_storage_path")==0)
+		return hl_sys_android_get_internal_storage_path();
+	else
+		hl_error("Unknown sys_special key");
+	return NULL;
+}
+
+DEFINE_PRIM(_BYTES, sys_special, _BYTES);
+
+#endif

+ 73 - 0
src/std/sys_ios.m

@@ -0,0 +1,73 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <hl.h>
+
+#if defined(HL_MOBILE) && defined(HL_IOS)
+
+#include <sys/utsname.h>
+#include <Foundation/Foundation.h>
+#include <UIKit/UIKit.h>
+
+static const char* ios_get_document_path()
+{
+	NSString* string = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
+	const char* path = strdup([string fileSystemRepresentation]);
+	return path;
+}
+
+static const char* ios_get_resource_path()
+{
+	NSString* string = [[NSBundle mainBundle] resourcePath];
+	const char* path = strdup([string UTF8String]);
+	return path;
+}
+
+static const char* ios_get_device_name()
+{
+	struct utsname systemInfo;
+	uname(&systemInfo);
+	return strdup(systemInfo.machine);
+}
+
+static int ios_get_retina_scale_factor()
+{
+	return [[UIScreen mainScreen] scale];
+}
+
+const char *hl_sys_special( const char *key ) { 
+	if (strcmp(key, "ios_resource_path")==0)
+		return ios_get_resource_path();
+	else if (strcmp(key, "ios_document_path")==0)
+		return ios_get_document_path();
+	else if (strcmp(key, "ios_retina_scale_factor")==0)
+		return ios_get_retina_scale_factor();
+	else if (strcmp(key, "ios_device_name")==0)
+		return ios_get_device_name();
+	else
+		hl_error("Unknown sys_special key");
+	return NULL;
+}
+
+DEFINE_PRIM(_BYTES, sys_special, _BYTES);
+
+#endif

+ 1 - 1
src/std/thread.c

@@ -39,7 +39,7 @@ HL_PRIM int hl_thread_id() {
 #	ifdef HL_WIN
 	return (int)GetCurrentThreadId();
 #	else
-#	ifdef SYS_gettid
+#	if defined(SYS_gettid) && !defined(HL_TVOS)
 	return syscall(SYS_gettid);
 #	else
 	hl_error("hl_thread_id() not available for this platform");

+ 15 - 0
src/std/ucs2.c

@@ -23,6 +23,17 @@
 #ifndef HL_NATIVE_UCHAR_FUN
 #include <stdarg.h>
 
+#ifdef HL_ANDROID
+#	include <android/log.h>
+#	ifndef HL_ANDROID_LOG_TAG
+#		define HL_ANDROID_LOG_TAG "hl"
+#	endif
+#	ifndef HL_ANDROID_LOG_LEVEL
+#		define HL_ANDROID_LOG_LEVEL ANDROID_LOG_DEBUG
+#	endif
+#	define LOG_ANDROID(cfmt,cstr) __android_log_print(HL_ANDROID_LOG_LEVEL, HL_ANDROID_LOG_TAG, cfmt, cstr);
+#endif
+
 int ustrlen( const uchar *str ) {
 	const uchar *p = str;
 	while( *p ) p++;
@@ -224,7 +235,11 @@ static char *utos( const uchar *s ) {
 void uprintf( const uchar *fmt, const uchar *str ) {
 	char *cfmt = utos(fmt);
 	char *cstr = utos(str);
+#ifdef HL_ANDROID
+	LOG_ANDROID(cfmt,cstr);
+#else
 	printf(cfmt,cstr);
+#endif
 	free(cfmt);
 	free(cstr);
 }