Add dex example that dynamically loads a java class from a byte
stream.
This commit is contained in:
parent
dad071293c
commit
45f4e38ee3
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,3 +1,9 @@
|
|||
*.ast
|
||||
*.apk
|
||||
*.jar
|
||||
*.dex
|
||||
*.class
|
||||
examples/sensors/sensors
|
||||
examples/jni/jni
|
||||
examples/dex/dex
|
||||
examples/permissions/permissions
|
||||
|
|
237
examples/dex/bindata.go
Normal file
237
examples/dex/bindata.go
Normal file
|
@ -0,0 +1,237 @@
|
|||
// Code generated by go-bindata.
|
||||
// sources:
|
||||
// java/classes.dex
|
||||
// DO NOT EDIT!
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _javaClassesDex = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x91\xbf\x4e\xc2\x50\x14\xc6\x7f\xa7\xe5\x4f\x22\x06\x11\x4d\x5c\x88\xe9\xa0\xab\x57\x23\x4e\x18\x13\xe3\x22\x09\xd1\x8d\xa5\x53\x15\x24\x25\x88\xc4\x56\x61\xd4\x84\x91\x47\xd0\xcd\x47\xf0\x11\x1c\x1c\x1c\xf4\x21\xdc\x58\xdc\x1d\xcc\xed\xbd\x40\xe3\x49\xda\xef\xe4\xf4\x9e\xef\x9c\xfe\x6e\xab\x3d\x5a\xda\xdd\x3f\xa0\x5e\xfd\x2c\xbf\xff\x3e\x7f\x7f\xbd\x6c\xbe\xf9\xcd\x1f\xff\xd6\x5f\x9f\xf8\x8d\xca\xf6\xb2\x03\x03\x60\xd4\xac\x96\xb1\x31\x16\xc8\x63\xea\x19\x60\x02\x38\xc0\x13\x8b\x70\x81\x57\x40\x80\x0f\xc0\x13\x98\x02\x15\x81\x2d\x81\x3d\x81\xaa\xc0\xa9\x40\x20\x70\x25\xc6\xc3\xb5\x9e\x39\xeb\x39\x8b\x9c\xfd\x46\xe2\x29\x89\x3a\x73\x85\xac\x55\xb1\x0f\x29\x8d\x64\xd1\xa7\xd3\x50\xcc\x8c\x41\x49\x57\x8b\x73\x9f\xbe\x98\x19\x6b\xac\xb2\xa2\x67\x1e\x86\xfd\x30\x3e\xa2\x70\x7c\xd2\x0b\xa2\x68\xa7\x1b\xdc\x07\x48\x9d\x72\x43\x67\xaa\x17\xf4\x3b\xea\xfc\xa2\xdb\xbe\x8c\x6b\x6c\x34\xa2\x58\x0d\x6f\x86\xaa\x13\xc6\xaa\xd5\x1e\x29\xd3\x53\xc3\x3d\xbb\xbb\x46\x9a\xb8\xe4\x8b\x64\xf5\x0b\x44\xe4\xf1\x21\x33\x15\x47\xc6\x7a\xe7\xc2\xbf\x7d\xb5\xce\xf8\x3a\x29\xc6\x6e\x8a\x73\x36\xc5\x38\x97\xe2\x2c\x9e\x39\xa3\x59\x3b\x9e\xf1\xd1\xcc\x5d\x5b\xd7\xff\x8e\x67\xce\x27\x5c\x4a\x26\xd7\x77\xfa\x17\x00\x00\xff\xff\xe4\x6b\xad\x5e\x0c\x02\x00\x00")
|
||||
|
||||
func javaClassesDexBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_javaClassesDex,
|
||||
"java/classes.dex",
|
||||
)
|
||||
}
|
||||
|
||||
func javaClassesDex() (*asset, error) {
|
||||
bytes, err := javaClassesDexBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "java/classes.dex", size: 524, mode: os.FileMode(420), modTime: time.Unix(1572550715, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"java/classes.dex": javaClassesDex,
|
||||
}
|
||||
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
// For example if you run go-bindata on data/... and data contains the
|
||||
// following hierarchy:
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(cannonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"java": &bintree{nil, map[string]*bintree{
|
||||
"classes.dex": &bintree{javaClassesDex, map[string]*bintree{}},
|
||||
}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||
}
|
||||
|
206
examples/dex/dex_android.go
Normal file
206
examples/dex/dex_android.go
Normal file
|
@ -0,0 +1,206 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -landroid -llog
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
|
||||
static jobject gClassLoader;
|
||||
static jmethodID gFindClassMethod;
|
||||
|
||||
jobject
|
||||
CreateObject(JNIEnv* env, jclass cls) {
|
||||
jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "()V");
|
||||
return (*env)->NewObject(env, cls, init);
|
||||
}
|
||||
|
||||
jmethodID GetMethodID(JNIEnv *env, jclass cls, const char *name, const char *sig) {
|
||||
return (*env)->GetMethodID(env, cls, name, sig);
|
||||
}
|
||||
|
||||
jclass
|
||||
FindClass(JNIEnv* env, char* name) {
|
||||
jstring strClassName = (*env)->NewStringUTF(env, name);
|
||||
return (*env)->CallObjectMethod(env, gClassLoader, gFindClassMethod, strClassName);
|
||||
}
|
||||
|
||||
void
|
||||
SetupJVM(JNIEnv* env, jobject context, void* buf, int len) {
|
||||
jclass cls = (*env)->GetObjectClass(env, context);
|
||||
jmethodID mid = (*env)->GetMethodID(env, cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
jobject loader = (*env)->CallObjectMethod(env, context, mid);
|
||||
gClassLoader = (*env)->NewGlobalRef(env, loader);
|
||||
cls = (*env)->GetObjectClass(env, loader);
|
||||
gFindClassMethod = (*env)->GetMethodID(env, cls, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
|
||||
__android_log_write(ANDROID_LOG_DEBUG, "gio", "Looking for InMemoryDexClassLoader");
|
||||
cls = (*env)->FindClass(env, "dalvik/system/InMemoryDexClassLoader");
|
||||
if (cls == NULL) {
|
||||
__android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||
return;
|
||||
}
|
||||
__android_log_write(ANDROID_LOG_DEBUG, "gio", "NewDirectByteBuffer");
|
||||
jobject bbuf = (*env)->NewDirectByteBuffer(env, buf, (jlong)len);
|
||||
if (bbuf == NULL) {
|
||||
__android_log_write(ANDROID_LOG_DEBUG, "gio", "failed");
|
||||
return;
|
||||
}
|
||||
|
||||
__android_log_write(ANDROID_LOG_DEBUG, "gio", "looking for <init>");
|
||||
mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||
if (mid == NULL) {
|
||||
__android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||
return;
|
||||
}
|
||||
__android_log_write(ANDROID_LOG_DEBUG, "gio", "calling <init>");
|
||||
jobject dexLoader = (*env)->NewObject(env, cls, mid, bbuf, gClassLoader);
|
||||
// make the dexLoader our new class loader
|
||||
gClassLoader = (*env)->NewGlobalRef(env, dexLoader);
|
||||
}
|
||||
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "looking for AClass");
|
||||
// cls = FindClass(env, "st/wow/git/dex/AClass");
|
||||
// if (cls == NULL) {
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "calling CreateObject");
|
||||
// jobject obj = CreateObject(env, cls);
|
||||
// if (obj == NULL) {
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "failed");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "looking for <init>");
|
||||
// mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
|
||||
// if (mid == NULL) {
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||
// return;
|
||||
// }
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "creating object");
|
||||
// obj = (*env)->NewObject(env, cls, mid);
|
||||
// if (obj == NULL) {
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "failed");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "looking for Num");
|
||||
// mid = (*env)->GetMethodID(env, cls, "Num", "()I");
|
||||
// if (mid == NULL) {
|
||||
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||
// return;
|
||||
// }
|
||||
// jint val = (*env)->CallIntMethod(env, obj, mid);
|
||||
// __android_log_print(ANDROID_LOG_DEBUG, "gio", "value = %d", val);
|
||||
|
||||
|
||||
jint
|
||||
CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
|
||||
return (*env)->CallIntMethod(env, obj, methodID);
|
||||
}
|
||||
|
||||
jint
|
||||
GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
|
||||
return (*vm)->GetEnv(vm, (void **)env, version);
|
||||
}
|
||||
|
||||
jint
|
||||
AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
|
||||
return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
|
||||
}
|
||||
|
||||
jint
|
||||
DetachCurrentThread(JavaVM *vm) {
|
||||
return (*vm)->DetachCurrentThread(vm);
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/app"
|
||||
)
|
||||
|
||||
var theJVM *C.JavaVM
|
||||
|
||||
func SetJVM() {
|
||||
h := app.PlatformHandle()
|
||||
theJVM = (*C.JavaVM)(unsafe.Pointer(h.JVM))
|
||||
dex := MustAsset("java/classes.dex")
|
||||
RunInJVM(func(env *C.JNIEnv) {
|
||||
labchan <- "Set up JVM"
|
||||
C.SetupJVM(
|
||||
env,
|
||||
(C.jobject)(h.Context),
|
||||
unsafe.Pointer(&dex[0]),
|
||||
C.int(len(dex)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func CallJNI() {
|
||||
labchan <- "Call JNI"
|
||||
RunInJVM(func(env *C.JNIEnv) {
|
||||
labchan <- "FindClass"
|
||||
cls := JniFindClass(env, "st/wow/git/dex/AClass")
|
||||
labchan <- "GetMethodID"
|
||||
mid := JniGetMethodID(env, cls, "Num", "()I")
|
||||
labchan <- "CreateObject"
|
||||
obj := JniCreateObject(env, cls)
|
||||
val := JniCallIntMethod(env, obj, mid)
|
||||
labchan <- fmt.Sprintf("val = %d", val)
|
||||
})
|
||||
}
|
||||
|
||||
func JniFindClass(env *C.JNIEnv, name string) C.jclass {
|
||||
cname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
return C.FindClass((*C.JNIEnv)(env), cname)
|
||||
}
|
||||
|
||||
func JniCreateObject(env *C.JNIEnv, cls C.jclass) C.jobject {
|
||||
return C.CreateObject(env, (C.jclass)(cls))
|
||||
}
|
||||
|
||||
func JniCallIntMethod(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID) int {
|
||||
return (int)(C.CallIntMethod(env, obj, methodID))
|
||||
}
|
||||
|
||||
func JniGetMethodID(env *C.JNIEnv, cls C.jclass, name, sig string) C.jmethodID {
|
||||
cname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
csig := C.CString(sig)
|
||||
defer C.free(unsafe.Pointer(csig))
|
||||
return C.GetMethodID(env, cls, cname, csig)
|
||||
}
|
||||
|
||||
func RunInJVM(f func(env *C.JNIEnv)) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
var env *C.JNIEnv
|
||||
var detach bool
|
||||
if res := C.GetEnv(theJVM, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
|
||||
if res != C.JNI_EDETACHED {
|
||||
panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
|
||||
}
|
||||
if C.AttachCurrentThread(theJVM, &env, nil) != C.JNI_OK {
|
||||
panic(errors.New("runInJVM: AttachCurrentThread failed"))
|
||||
}
|
||||
detach = true
|
||||
}
|
||||
|
||||
if detach {
|
||||
defer func() {
|
||||
C.DetachCurrentThread(theJVM)
|
||||
}()
|
||||
}
|
||||
f(env)
|
||||
}
|
9
examples/dex/dex_other.go
Normal file
9
examples/dex/dex_other.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package main
|
||||
|
||||
func SetJVM() {
|
||||
dex := MustAsset("java/classes.dex")
|
||||
_ = dex
|
||||
}
|
||||
|
||||
func CallJNI() {
|
||||
}
|
9
examples/dex/java/AClass.java
Normal file
9
examples/dex/java/AClass.java
Normal file
|
@ -0,0 +1,9 @@
|
|||
package st.wow.git.dex;
|
||||
|
||||
public class AClass {
|
||||
public int Num() {
|
||||
return 17;
|
||||
}
|
||||
}
|
||||
|
||||
|
5
examples/dex/java/gen.go
Normal file
5
examples/dex/java/gen.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:generate javac -bootclasspath $ANDROID_HOME/platforms/android-29/android.jar AClass.java
|
||||
//go:generate $ANDROID_HOME/build-tools/28.0.3/dx --dex --output=classes.dex --no-strict AClass.class
|
||||
//go:generate rm AClass.class
|
||||
|
||||
package java
|
70
examples/dex/main.go
Normal file
70
examples/dex/main.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
//go:generate go generate ./java
|
||||
//go:generate go-bindata java/classes.dex
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"gioui.org/app"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/io/system"
|
||||
"gioui.org/unit"
|
||||
"gioui.org/widget/material"
|
||||
|
||||
"gioui.org/font/gofont"
|
||||
)
|
||||
|
||||
var (
|
||||
labchan chan string
|
||||
)
|
||||
|
||||
func main() {
|
||||
labchan = make(chan string)
|
||||
go eventloop()
|
||||
app.Main()
|
||||
}
|
||||
|
||||
func eventloop() {
|
||||
gofont.Register()
|
||||
w := app.NewWindow(app.Title("Dex"))
|
||||
th := material.NewTheme()
|
||||
gtx := &layout.Context{Queue: w.Queue()}
|
||||
|
||||
sysinset := &layout.Inset{}
|
||||
margin := layout.UniformInset(unit.Dp(10))
|
||||
labels := []material.Label{}
|
||||
list := &layout.List{Axis: layout.Vertical}
|
||||
|
||||
go func() {
|
||||
labchan <- "Starting"
|
||||
SetJVM()
|
||||
CallJNI()
|
||||
}()
|
||||
|
||||
resetSysinset := func(x system.Insets) {
|
||||
sysinset.Top = x.Top
|
||||
sysinset.Bottom = x.Bottom
|
||||
sysinset.Left = x.Left
|
||||
sysinset.Right = x.Right
|
||||
}
|
||||
|
||||
for { select {
|
||||
case x := <-labchan:
|
||||
labels = append(labels, th.Body1(x))
|
||||
case e := <-w.Events():
|
||||
switch e := e.(type) {
|
||||
case system.DestroyEvent:
|
||||
return
|
||||
case system.FrameEvent:
|
||||
gtx.Reset(e.Config, e.Size)
|
||||
resetSysinset(e.Insets)
|
||||
sysinset.Layout(gtx, func() {
|
||||
margin.Layout(gtx, func() {
|
||||
list.Layout(gtx, len(labels), func(i int) {
|
||||
labels[i].Layout(gtx)
|
||||
})
|
||||
})
|
||||
})
|
||||
e.Frame(gtx.Ops)
|
||||
}
|
||||
}}
|
||||
}
|
|
@ -26,13 +26,6 @@ func main() {
|
|||
log.Print("app.Main() returned")
|
||||
}
|
||||
|
||||
func diffInsets(x, y system.Insets) bool {
|
||||
return x.Top != y.Top ||
|
||||
x.Bottom != y.Bottom ||
|
||||
x.Left != y.Left ||
|
||||
x.Right != y.Right
|
||||
}
|
||||
|
||||
func eventloop() {
|
||||
w := app.NewWindow(
|
||||
app.Size(unit.Dp(400), unit.Dp(400)),
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"gioui.org/unit"
|
||||
"gioui.org/widget/material"
|
||||
|
||||
_ "gioui.org/font/gofont"
|
||||
"gioui.org/font/gofont"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -34,6 +34,7 @@ func diffInsets(x, y system.Insets) bool {
|
|||
}
|
||||
|
||||
func eventloop() {
|
||||
gofont.Register()
|
||||
w := app.NewWindow(
|
||||
app.Size(unit.Dp(400), unit.Dp(400)),
|
||||
app.Title("Hello"))
|
||||
|
|
Loading…
Reference in New Issue
Block a user