mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +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
|
go 1.12
|
||||||
|
|
||||||
require (
|
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
|
github.com/google/go-github/v24 v24.0.1
|
||||||
golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f
|
golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f
|
||||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9
|
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;
|
const mainmSrc = `@import UIKit;
|
||||||
@import Gio;
|
@import Gio;
|
||||||
|
|
||||||
|
void gio_runMain(void);
|
||||||
|
|
||||||
int main(int argc, char * argv[]) {
|
int main(int argc, char * argv[]) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([GioAppDelegate class]));
|
gio_runMain();
|
||||||
}
|
return UIApplicationMain(argc, argv, nil, NSStringFromClass([GioAppDelegate class]));
|
||||||
|
}
|
||||||
}`
|
}`
|
||||||
if err := ioutil.WriteFile(mainm, []byte(mainmSrc), 0660); err != nil {
|
if err := ioutil.WriteFile(mainm, []byte(mainmSrc), 0660); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+9
-10
@@ -27,7 +27,6 @@ import java.io.UnsupportedEncodingException;
|
|||||||
public class GioView extends SurfaceView implements Choreographer.FrameCallback {
|
public class GioView extends SurfaceView implements Choreographer.FrameCallback {
|
||||||
private final static Object initLock = new Object();
|
private final static Object initLock = new Object();
|
||||||
private static boolean jniLoaded;
|
private static boolean jniLoaded;
|
||||||
private static String dataDir;
|
|
||||||
|
|
||||||
private final SurfaceHolder.Callback callbacks;
|
private final SurfaceHolder.Callback callbacks;
|
||||||
private final InputMethodManager imm;
|
private final InputMethodManager imm;
|
||||||
@@ -39,20 +38,19 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback
|
|||||||
if (jniLoaded) {
|
if (jniLoaded) {
|
||||||
return;
|
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");
|
System.loadLibrary("gio");
|
||||||
|
runGoMain(dataDirUTF8);
|
||||||
jniLoaded = true;
|
jniLoaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] dataDir() {
|
|
||||||
try {
|
|
||||||
return dataDir.getBytes("UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public GioView(Context context) {
|
public GioView(Context context) {
|
||||||
this(context, null);
|
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 void onFrameCallback(long handle, long nanos);
|
||||||
static private native boolean onBack(long handle);
|
static private native boolean onBack(long handle);
|
||||||
static private native void onFocusChange(long handle, boolean focus);
|
static private native void onFocusChange(long handle, boolean focus);
|
||||||
|
static private native void runGoMain(byte[] dataDir);
|
||||||
|
|
||||||
private static class InputConnection extends BaseInputConnection {
|
private static class InputConnection extends BaseInputConnection {
|
||||||
private final Editable editable;
|
private final Editable editable;
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ func dataDir() (string, error) {
|
|||||||
return dataPath, nil
|
return dataPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//export setDataDir
|
func setDataDir(dir string) {
|
||||||
func setDataDir(cdir *C.char, len C.int) {
|
|
||||||
dir := C.GoStringN(cdir, len)
|
|
||||||
dataDirChan <- dir
|
dataDirChan <- dir
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: Unlicense OR MIT
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
//+build darwin,ios
|
// +build darwin,ios
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: Unlicense OR MIT
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
//+build darwin,ios
|
// +build darwin,ios
|
||||||
|
|
||||||
@import UIKit;
|
@import UIKit;
|
||||||
@import OpenGLES;
|
@import OpenGLES;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: Unlicense OR MIT
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
//+build darwin,!ios
|
// +build darwin,!ios
|
||||||
|
|
||||||
@import AppKit;
|
@import AppKit;
|
||||||
|
|
||||||
|
|||||||
+19
-17
@@ -1,6 +1,8 @@
|
|||||||
// SPDX-License-Identifier: Unlicense OR MIT
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <android/log.h>
|
||||||
#include "os_android.h"
|
#include "os_android.h"
|
||||||
#include "_cgo_export.h"
|
#include "_cgo_export.h"
|
||||||
|
|
||||||
@@ -18,6 +20,11 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const JNINativeMethod methods[] = {
|
static const JNINativeMethod methods[] = {
|
||||||
|
{
|
||||||
|
.name = "runGoMain",
|
||||||
|
.signature = "([B)V",
|
||||||
|
.fnPtr = runGoMain
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = "onCreateView",
|
.name = "onCreateView",
|
||||||
.signature = "(Lorg/gioui/GioView;)J",
|
.signature = "(Lorg/gioui/GioView;)J",
|
||||||
@@ -93,23 +100,6 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
|||||||
return -1;
|
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;
|
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) {
|
void gio_jni_CallVoidMethod_J(JNIEnv *env, jobject obj, jmethodID methodID, jlong a1) {
|
||||||
(*env)->CallVoidMethod(env, obj, methodID, 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)
|
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
|
//export setJVM
|
||||||
func setJVM(vm *C.JavaVM) {
|
func setJVM(vm *C.JavaVM) {
|
||||||
theJVM = vm
|
theJVM = vm
|
||||||
@@ -407,8 +420,6 @@ func (w *window) showTextInput(show bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Main() {
|
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 {
|
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"))) 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(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"))) 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() {
|
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