diff --git a/apps/go.mod b/apps/go.mod index a637fea4..c14a67c9 100644 --- a/apps/go.mod +++ b/apps/go.mod @@ -3,7 +3,7 @@ module gioui.org/apps go 1.12 require ( - gioui.org/ui v0.0.0-20190721195854-dd081c78b96d + gioui.org/ui v0.0.0-20190721200230-1ee8c08f3bf9 github.com/google/go-github/v24 v24.0.1 golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9 diff --git a/cmd/gio/iosbuild.go b/cmd/gio/iosbuild.go index fce1e0ae..17ce31bb 100644 --- a/cmd/gio/iosbuild.go +++ b/cmd/gio/iosbuild.go @@ -139,10 +139,13 @@ func exeIOS(tmpDir, target, app string, bi *buildInfo) error { const mainmSrc = `@import UIKit; @import Gio; +void gio_runMain(void); + int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([GioAppDelegate class])); - } + @autoreleasepool { + gio_runMain(); + return UIApplicationMain(argc, argv, nil, NSStringFromClass([GioAppDelegate class])); + } }` if err := ioutil.WriteFile(mainm, []byte(mainmSrc), 0660); err != nil { return err diff --git a/ui/app/GioView.java b/ui/app/GioView.java index 2b24264d..4cb92603 100644 --- a/ui/app/GioView.java +++ b/ui/app/GioView.java @@ -27,7 +27,6 @@ import java.io.UnsupportedEncodingException; public class GioView extends SurfaceView implements Choreographer.FrameCallback { private final static Object initLock = new Object(); private static boolean jniLoaded; - private static String dataDir; private final SurfaceHolder.Callback callbacks; private final InputMethodManager imm; @@ -39,20 +38,19 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback if (jniLoaded) { return; } - dataDir = appCtx.getFilesDir().getAbsolutePath(); + String dataDir = appCtx.getFilesDir().getAbsolutePath(); + byte[] dataDirUTF8; + try { + dataDirUTF8 = dataDir.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } System.loadLibrary("gio"); + runGoMain(dataDirUTF8); jniLoaded = true; } } - static byte[] dataDir() { - try { - return dataDir.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - public GioView(Context context) { this(context, null); } @@ -221,6 +219,7 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback static private native void onFrameCallback(long handle, long nanos); static private native boolean onBack(long handle); static private native void onFocusChange(long handle, boolean focus); + static private native void runGoMain(byte[] dataDir); private static class InputConnection extends BaseInputConnection { private final Editable editable; diff --git a/ui/app/datadir_android.go b/ui/app/datadir_android.go index 0065ad1d..ad0da624 100644 --- a/ui/app/datadir_android.go +++ b/ui/app/datadir_android.go @@ -20,8 +20,6 @@ func dataDir() (string, error) { return dataPath, nil } -//export setDataDir -func setDataDir(cdir *C.char, len C.int) { - dir := C.GoStringN(cdir, len) +func setDataDir(dir string) { dataDirChan <- dir } diff --git a/ui/app/gl_ios.go b/ui/app/gl_ios.go index dc367321..ae406eeb 100644 --- a/ui/app/gl_ios.go +++ b/ui/app/gl_ios.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT -//+build darwin,ios +// +build darwin,ios package app diff --git a/ui/app/gl_ios.m b/ui/app/gl_ios.m index 410d699a..47819753 100644 --- a/ui/app/gl_ios.m +++ b/ui/app/gl_ios.m @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT -//+build darwin,ios +// +build darwin,ios @import UIKit; @import OpenGLES; diff --git a/ui/app/gl_macos.m b/ui/app/gl_macos.m index 9495f190..abdb406b 100644 --- a/ui/app/gl_macos.m +++ b/ui/app/gl_macos.m @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT -//+build darwin,!ios +// +build darwin,!ios @import AppKit; diff --git a/ui/app/os_android.c b/ui/app/os_android.c index 5e6d0094..30d8e728 100644 --- a/ui/app/os_android.c +++ b/ui/app/os_android.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Unlicense OR MIT #include +#include +#include #include "os_android.h" #include "_cgo_export.h" @@ -18,6 +20,11 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { } static const JNINativeMethod methods[] = { + { + .name = "runGoMain", + .signature = "([B)V", + .fnPtr = runGoMain + }, { .name = "onCreateView", .signature = "(Lorg/gioui/GioView;)J", @@ -93,23 +100,6 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { return -1; } - // Initialize data dir. - jmethodID dataDirMethod = (*env)->GetStaticMethodID(env, viewClass, "dataDir", "()[B"); - if (dataDirMethod == NULL) { - return -1; - } - jbyteArray dirArr = (*env)->CallStaticObjectMethod(env, viewClass, dataDirMethod); - if (dirArr == NULL) { - return -1; - } - jbyte *dir = (*env)->GetByteArrayElements(env, dirArr, NULL); - if (dir == NULL) { - return -1; - } - jint n = (*env)->GetArrayLength(env, dirArr); - setDataDir((char *)dir, n); - (*env)->ReleaseByteArrayElements(env, dirArr, dir, JNI_ABORT); - return JNI_VERSION_1_6; } @@ -164,3 +154,15 @@ void gio_jni_CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID) { void gio_jni_CallVoidMethod_J(JNIEnv *env, jobject obj, jmethodID methodID, jlong a1) { (*env)->CallVoidMethod(env, obj, methodID, a1); } + +jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) { + return (*env)->GetByteArrayElements(env, arr, NULL); +} + +void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes) { + (*env)->ReleaseByteArrayElements(env, arr, bytes, JNI_ABORT); +} + +jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr) { + return (*env)->GetArrayLength(env, arr); +} diff --git a/ui/app/os_android.go b/ui/app/os_android.go index 256f48c4..af70d181 100644 --- a/ui/app/os_android.go +++ b/ui/app/os_android.go @@ -76,6 +76,19 @@ func jniGetStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.j return C.gio_jni_GetStaticMethodID(env, class, m, s) } +//export runGoMain +func runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray) { + dirBytes := C.gio_jni_GetByteArrayElements(env, jdataDir) + if dirBytes == nil { + panic("runGoMain: GetByteArrayElements failed") + } + n := C.gio_jni_GetArrayLength(env, jdataDir) + dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n) + setDataDir(dataDir) + C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes) + runMain() +} + //export setJVM func setJVM(vm *C.JavaVM) { theJVM = vm @@ -407,8 +420,6 @@ func (w *window) showTextInput(show bool) { } func Main() { - // Android runs in c-shared mode where main is never reached. - panic("call to Main from outside main") } func createWindow(window *Window, opts *WindowOptions) error { diff --git a/ui/app/os_android.h b/ui/app/os_android.h index 51a5f97a..288d6335 100644 --- a/ui/app/os_android.h +++ b/ui/app/os_android.h @@ -14,3 +14,6 @@ __attribute__ ((visibility ("hidden"))) jfloat gio_jni_CallFloatMethod(JNIEnv *e __attribute__ ((visibility ("hidden"))) jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID); __attribute__ ((visibility ("hidden"))) void gio_jni_CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID); __attribute__ ((visibility ("hidden"))) void gio_jni_CallVoidMethod_J(JNIEnv *env, jobject obj, jmethodID methodID, jlong a1); +__attribute__ ((visibility ("hidden"))) jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr); +__attribute__ ((visibility ("hidden"))) void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes); +__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr); diff --git a/ui/app/os_ios.go b/ui/app/os_ios.go index a5088a46..9aa32f4d 100644 --- a/ui/app/os_ios.go +++ b/ui/app/os_ios.go @@ -252,6 +252,4 @@ func createWindow(win *Window, opts *WindowOptions) error { } func Main() { - // iOS runs in c-archive mode, so this is never reached. - panic("call to Main from outside main") } diff --git a/ui/app/runmain.go b/ui/app/runmain.go new file mode 100644 index 00000000..4a59727d --- /dev/null +++ b/ui/app/runmain.go @@ -0,0 +1,22 @@ +// +build android darwin,ios + +package app + +// Android only supports non-Java programs as c-shared libraries. +// Unfortunately, Go does not run a program's main function in +// library mode. To make Gio programs simpler and uniform, we'll +// link to the main function here and call it from Java. + +import _ "unsafe" // for go:linkname + +//go:linkname mainMain main.main +func mainMain() + +func runMain() { + go func() { + // Indirect call, since the linker does not know the address of main when + // laying down this package. + fn := mainMain + fn() + }() +} diff --git a/ui/app/runmain_ios.go b/ui/app/runmain_ios.go new file mode 100644 index 00000000..02b08df6 --- /dev/null +++ b/ui/app/runmain_ios.go @@ -0,0 +1,10 @@ +// +build darwin,ios + +package app + +import "C" + +//export gio_runMain +func gio_runMain() { + runMain() +}