mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 23:55:39 +00:00
ui/app: call main from Android and iOS
Android can only run c-shared libraries which means that every Gio program must create its window and event loop from an init function. The same applies to iOS but for a more benign reason: the gio tool builds programs in c-archive mode for iOS and links the binary with a Objective-C driver. Allow Gio programs to run off its main function by linking to and invoking main even from Android libraries and iOS ditto. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
+6
-3
@@ -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
|
||||
|
||||
+9
-10
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//+build darwin,ios
|
||||
// +build darwin,ios
|
||||
|
||||
package app
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//+build darwin,ios
|
||||
// +build darwin,ios
|
||||
|
||||
@import UIKit;
|
||||
@import OpenGLES;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//+build darwin,!ios
|
||||
// +build darwin,!ios
|
||||
|
||||
@import AppKit;
|
||||
|
||||
|
||||
+19
-17
@@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
#include <jni.h>
|
||||
#include <dlfcn.h>
|
||||
#include <android/log.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
+13
-2
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// +build darwin,ios
|
||||
|
||||
package app
|
||||
|
||||
import "C"
|
||||
|
||||
//export gio_runMain
|
||||
func gio_runMain() {
|
||||
runMain()
|
||||
}
|
||||
Reference in New Issue
Block a user