os_android.go raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  package wm
   4  
   5  /*
   6  #cgo CFLAGS: -Werror
   7  #cgo LDFLAGS: -landroid
   8  
   9  #include <android/native_window_jni.h>
  10  #include <android/configuration.h>
  11  #include <android/keycodes.h>
  12  #include <android/input.h>
  13  #include <stdlib.h>
  14  
  15  __attribute__ ((visibility ("hidden"))) jint gio_jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version);
  16  __attribute__ ((visibility ("hidden"))) jint gio_jni_GetJavaVM(JNIEnv *env, JavaVM **jvm);
  17  __attribute__ ((visibility ("hidden"))) jint gio_jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args);
  18  __attribute__ ((visibility ("hidden"))) jint gio_jni_DetachCurrentThread(JavaVM *vm);
  19  
  20  __attribute__ ((visibility ("hidden"))) jobject gio_jni_NewGlobalRef(JNIEnv *env, jobject obj);
  21  __attribute__ ((visibility ("hidden"))) void gio_jni_DeleteGlobalRef(JNIEnv *env, jobject obj);
  22  __attribute__ ((visibility ("hidden"))) jclass gio_jni_GetObjectClass(JNIEnv *env, jobject obj);
  23  __attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
  24  __attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
  25  __attribute__ ((visibility ("hidden"))) jfloat gio_jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID);
  26  __attribute__ ((visibility ("hidden"))) jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID);
  27  __attribute__ ((visibility ("hidden"))) void gio_jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args);
  28  __attribute__ ((visibility ("hidden"))) void gio_jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
  29  __attribute__ ((visibility ("hidden"))) jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr);
  30  __attribute__ ((visibility ("hidden"))) void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes);
  31  __attribute__ ((visibility ("hidden"))) jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr);
  32  __attribute__ ((visibility ("hidden"))) jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
  33  __attribute__ ((visibility ("hidden"))) jsize gio_jni_GetStringLength(JNIEnv *env, jstring str);
  34  __attribute__ ((visibility ("hidden"))) const jchar *gio_jni_GetStringChars(JNIEnv *env, jstring str);
  35  __attribute__ ((visibility ("hidden"))) jthrowable gio_jni_ExceptionOccurred(JNIEnv *env);
  36  __attribute__ ((visibility ("hidden"))) void gio_jni_ExceptionClear(JNIEnv *env);
  37  __attribute__ ((visibility ("hidden"))) jobject gio_jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args);
  38  __attribute__ ((visibility ("hidden"))) jobject gio_jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args);
  39  */
  40  import "C"
  41  
  42  import (
  43  	"errors"
  44  	"fmt"
  45  	"image"
  46  	"reflect"
  47  	"runtime"
  48  	"runtime/debug"
  49  	"sync"
  50  	"time"
  51  	"unicode/utf16"
  52  	"unsafe"
  53  
  54  	"github.com/p9c/p9/pkg/gel/gio/f32"
  55  	"github.com/p9c/p9/pkg/gel/gio/io/clipboard"
  56  	"github.com/p9c/p9/pkg/gel/gio/io/key"
  57  	"github.com/p9c/p9/pkg/gel/gio/io/pointer"
  58  	"github.com/p9c/p9/pkg/gel/gio/io/system"
  59  	"github.com/p9c/p9/pkg/gel/gio/unit"
  60  )
  61  
  62  type window struct {
  63  	callbacks Callbacks
  64  
  65  	view C.jobject
  66  
  67  	dpi       int
  68  	fontScale float32
  69  	insets    system.Insets
  70  
  71  	stage   system.Stage
  72  	started bool
  73  
  74  	state, newState windowState
  75  
  76  	// mu protects the fields following it.
  77  	mu        sync.Mutex
  78  	win       *C.ANativeWindow
  79  	animating bool
  80  }
  81  
  82  // windowState tracks the View or Activity specific state lost when Android
  83  // re-creates our Activity.
  84  type windowState struct {
  85  	cursor *pointer.CursorName
  86  }
  87  
  88  // gioView hold cached JNI methods for GioView.
  89  var gioView struct {
  90  	once              sync.Once
  91  	getDensity        C.jmethodID
  92  	getFontScale      C.jmethodID
  93  	showTextInput     C.jmethodID
  94  	hideTextInput     C.jmethodID
  95  	postFrameCallback C.jmethodID
  96  	setCursor         C.jmethodID
  97  }
  98  
  99  // ViewEvent is sent whenever the Window's underlying Android view
 100  // changes.
 101  type ViewEvent struct {
 102  	// View is a JNI global reference to the android.view.View
 103  	// instance backing the Window. The reference is valid until
 104  	// the next ViewEvent is received.
 105  	// A zero View means that there is currently no view attached.
 106  	View uintptr
 107  }
 108  
 109  type jvalue uint64 // The largest JNI type fits in 64 bits.
 110  
 111  var dataDirChan = make(chan string, 1)
 112  
 113  var android struct {
 114  	// mu protects all fields of this structure. However, once a
 115  	// non-nil jvm is returned from javaVM, all the other fields may
 116  	// be accessed unlocked.
 117  	mu  sync.Mutex
 118  	jvm *C.JavaVM
 119  
 120  	// appCtx is the global Android App context.
 121  	appCtx C.jobject
 122  	// gioCls is the class of the Gio class.
 123  	gioCls C.jclass
 124  
 125  	mwriteClipboard   C.jmethodID
 126  	mreadClipboard    C.jmethodID
 127  	mwakeupMainThread C.jmethodID
 128  }
 129  
 130  // view maps from GioView JNI refenreces to windows.
 131  var views = make(map[C.jlong]*window)
 132  
 133  // windows maps from Callbacks to windows
 134  var windows = make(map[Callbacks]*window)
 135  
 136  var mainWindow = newWindowRendezvous()
 137  
 138  var mainFuncs = make(chan func(env *C.JNIEnv), 1)
 139  
 140  func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
 141  	m := C.CString(method)
 142  	defer C.free(unsafe.Pointer(m))
 143  	s := C.CString(sig)
 144  	defer C.free(unsafe.Pointer(s))
 145  	jm := C.gio_jni_GetMethodID(env, class, m, s)
 146  	if err := exception(env); err != nil {
 147  		panic(err)
 148  	}
 149  	return jm
 150  }
 151  
 152  func getStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
 153  	m := C.CString(method)
 154  	defer C.free(unsafe.Pointer(m))
 155  	s := C.CString(sig)
 156  	defer C.free(unsafe.Pointer(s))
 157  	jm := C.gio_jni_GetStaticMethodID(env, class, m, s)
 158  	if err := exception(env); err != nil {
 159  		panic(err)
 160  	}
 161  	return jm
 162  }
 163  
 164  //export Java_org_gioui_Gio_runGoMain
 165  func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) {
 166  	initJVM(env, class, context)
 167  	dirBytes := C.gio_jni_GetByteArrayElements(env, jdataDir)
 168  	if dirBytes == nil {
 169  		panic("runGoMain: GetByteArrayElements failed")
 170  	}
 171  	n := C.gio_jni_GetArrayLength(env, jdataDir)
 172  	dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n)
 173  	dataDirChan <- dataDir
 174  	C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
 175  
 176  	runMain()
 177  }
 178  
 179  func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) {
 180  	android.mu.Lock()
 181  	defer android.mu.Unlock()
 182  	if res := C.gio_jni_GetJavaVM(env, &android.jvm); res != 0 {
 183  		panic("gio: GetJavaVM failed")
 184  	}
 185  	android.appCtx = C.gio_jni_NewGlobalRef(env, ctx)
 186  	android.gioCls = C.jclass(C.gio_jni_NewGlobalRef(env, C.jobject(gio)))
 187  	android.mwriteClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V")
 188  	android.mreadClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;")
 189  	android.mwakeupMainThread = getStaticMethodID(env, gio, "wakeupMainThread", "()V")
 190  }
 191  
 192  func JavaVM() uintptr {
 193  	jvm := javaVM()
 194  	return uintptr(unsafe.Pointer(jvm))
 195  }
 196  
 197  func javaVM() *C.JavaVM {
 198  	android.mu.Lock()
 199  	defer android.mu.Unlock()
 200  	return android.jvm
 201  }
 202  
 203  func AppContext() uintptr {
 204  	android.mu.Lock()
 205  	defer android.mu.Unlock()
 206  	return uintptr(android.appCtx)
 207  }
 208  
 209  func GetDataDir() string {
 210  	return <-dataDirChan
 211  }
 212  
 213  //export Java_org_gioui_GioView_onCreateView
 214  func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
 215  	gioView.once.Do(func() {
 216  		m := &gioView
 217  		m.getDensity = getMethodID(env, class, "getDensity", "()I")
 218  		m.getFontScale = getMethodID(env, class, "getFontScale", "()F")
 219  		m.showTextInput = getMethodID(env, class, "showTextInput", "()V")
 220  		m.hideTextInput = getMethodID(env, class, "hideTextInput", "()V")
 221  		m.postFrameCallback = getMethodID(env, class, "postFrameCallback", "()V")
 222  		m.setCursor = getMethodID(env, class, "setCursor", "(I)V")
 223  	})
 224  	view = C.gio_jni_NewGlobalRef(env, view)
 225  	wopts := <-mainWindow.out
 226  	w, ok := windows[wopts.window]
 227  	if !ok {
 228  		w = &window{
 229  			callbacks: wopts.window,
 230  		}
 231  		windows[wopts.window] = w
 232  	}
 233  	w.callbacks.SetDriver(w)
 234  	w.view = view
 235  	handle := C.jlong(view)
 236  	views[handle] = w
 237  	w.loadConfig(env, class)
 238  	applyStateDiff(env, view, windowState{}, w.state)
 239  	w.setStage(system.StagePaused)
 240  	w.callbacks.Event(ViewEvent{View: uintptr(view)})
 241  	return handle
 242  }
 243  
 244  //export Java_org_gioui_GioView_onDestroyView
 245  func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
 246  	w := views[handle]
 247  	w.callbacks.Event(ViewEvent{View: 0})
 248  	w.callbacks.SetDriver(nil)
 249  	delete(views, handle)
 250  	C.gio_jni_DeleteGlobalRef(env, w.view)
 251  	w.view = 0
 252  }
 253  
 254  //export Java_org_gioui_GioView_onStopView
 255  func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
 256  	w := views[handle]
 257  	w.started = false
 258  	w.setStage(system.StagePaused)
 259  }
 260  
 261  //export Java_org_gioui_GioView_onStartView
 262  func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
 263  	w := views[handle]
 264  	w.started = true
 265  	if w.aNativeWindow() != nil {
 266  		w.setVisible()
 267  	}
 268  }
 269  
 270  //export Java_org_gioui_GioView_onSurfaceDestroyed
 271  func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) {
 272  	w := views[handle]
 273  	w.mu.Lock()
 274  	w.win = nil
 275  	w.mu.Unlock()
 276  	w.setStage(system.StagePaused)
 277  }
 278  
 279  //export Java_org_gioui_GioView_onSurfaceChanged
 280  func Java_org_gioui_GioView_onSurfaceChanged(env *C.JNIEnv, class C.jclass, handle C.jlong, surf C.jobject) {
 281  	w := views[handle]
 282  	w.mu.Lock()
 283  	w.win = C.ANativeWindow_fromSurface(env, surf)
 284  	w.mu.Unlock()
 285  	if w.started {
 286  		w.setVisible()
 287  	}
 288  }
 289  
 290  //export Java_org_gioui_GioView_onLowMemory
 291  func Java_org_gioui_GioView_onLowMemory() {
 292  	runtime.GC()
 293  	debug.FreeOSMemory()
 294  }
 295  
 296  //export Java_org_gioui_GioView_onConfigurationChanged
 297  func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) {
 298  	w := views[view]
 299  	w.loadConfig(env, class)
 300  	if w.stage >= system.StageRunning {
 301  		w.draw(true)
 302  	}
 303  }
 304  
 305  //export Java_org_gioui_GioView_onFrameCallback
 306  func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong, nanos C.jlong) {
 307  	w, exist := views[view]
 308  	if !exist {
 309  		return
 310  	}
 311  	if w.stage < system.StageRunning {
 312  		return
 313  	}
 314  	w.mu.Lock()
 315  	anim := w.animating
 316  	w.mu.Unlock()
 317  	if anim {
 318  		runInJVM(javaVM(), func(env *C.JNIEnv) {
 319  			callVoidMethod(env, w.view, gioView.postFrameCallback)
 320  		})
 321  		w.draw(false)
 322  	}
 323  }
 324  
 325  //export Java_org_gioui_GioView_onBack
 326  func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean {
 327  	w := views[view]
 328  	ev := &system.CommandEvent{Type: system.CommandBack}
 329  	w.callbacks.Event(ev)
 330  	if ev.Cancel {
 331  		return C.JNI_TRUE
 332  	}
 333  	return C.JNI_FALSE
 334  }
 335  
 336  //export Java_org_gioui_GioView_onFocusChange
 337  func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
 338  	w := views[view]
 339  	w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE})
 340  }
 341  
 342  //export Java_org_gioui_GioView_onWindowInsets
 343  func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) {
 344  	w := views[view]
 345  	w.insets = system.Insets{
 346  		Top:    unit.Px(float32(top)),
 347  		Right:  unit.Px(float32(right)),
 348  		Bottom: unit.Px(float32(bottom)),
 349  		Left:   unit.Px(float32(left)),
 350  	}
 351  	if w.stage >= system.StageRunning {
 352  		w.draw(true)
 353  	}
 354  }
 355  
 356  func (w *window) setVisible() {
 357  	win := w.aNativeWindow()
 358  	width, height := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win)
 359  	if width == 0 || height == 0 {
 360  		return
 361  	}
 362  	w.setStage(system.StageRunning)
 363  	w.draw(true)
 364  }
 365  
 366  func (w *window) setStage(stage system.Stage) {
 367  	if stage == w.stage {
 368  		return
 369  	}
 370  	w.stage = stage
 371  	w.callbacks.Event(system.StageEvent{stage})
 372  }
 373  
 374  func (w *window) nativeWindow(visID int) (*C.ANativeWindow, int, int) {
 375  	win := w.aNativeWindow()
 376  	var width, height int
 377  	if win != nil {
 378  		if C.ANativeWindow_setBuffersGeometry(win, 0, 0, C.int32_t(visID)) != 0 {
 379  			panic(errors.New("ANativeWindow_setBuffersGeometry failed"))
 380  		}
 381  		w, h := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win)
 382  		width, height = int(w), int(h)
 383  	}
 384  	return win, width, height
 385  }
 386  
 387  func (w *window) aNativeWindow() *C.ANativeWindow {
 388  	w.mu.Lock()
 389  	defer w.mu.Unlock()
 390  	return w.win
 391  }
 392  
 393  func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) {
 394  	dpi := int(C.gio_jni_CallIntMethod(env, w.view, gioView.getDensity))
 395  	w.fontScale = float32(C.gio_jni_CallFloatMethod(env, w.view, gioView.getFontScale))
 396  	switch dpi {
 397  	case C.ACONFIGURATION_DENSITY_NONE,
 398  		C.ACONFIGURATION_DENSITY_DEFAULT,
 399  		C.ACONFIGURATION_DENSITY_ANY:
 400  		// Assume standard density.
 401  		w.dpi = C.ACONFIGURATION_DENSITY_MEDIUM
 402  	default:
 403  		w.dpi = int(dpi)
 404  	}
 405  }
 406  
 407  func (w *window) SetAnimating(anim bool) {
 408  	w.mu.Lock()
 409  	w.animating = anim
 410  	w.mu.Unlock()
 411  	if anim {
 412  		runOnMain(func(env *C.JNIEnv) {
 413  			if w.view == 0 {
 414  				// View was destroyed while switching to main thread.
 415  				return
 416  			}
 417  			callVoidMethod(env, w.view, gioView.postFrameCallback)
 418  		})
 419  	}
 420  }
 421  
 422  func (w *window) draw(sync bool) {
 423  	win := w.aNativeWindow()
 424  	width, height := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win)
 425  	if width == 0 || height == 0 {
 426  		return
 427  	}
 428  	const inchPrDp = 1.0 / 160
 429  	ppdp := float32(w.dpi) * inchPrDp
 430  	w.callbacks.Event(FrameEvent{
 431  		FrameEvent: system.FrameEvent{
 432  			Now: time.Now(),
 433  			Size: image.Point{
 434  				X: int(width),
 435  				Y: int(height),
 436  			},
 437  			Insets: w.insets,
 438  			Metric: unit.Metric{
 439  				PxPerDp: ppdp,
 440  				PxPerSp: w.fontScale * ppdp,
 441  			},
 442  		},
 443  		Sync: sync,
 444  	})
 445  }
 446  
 447  type keyMapper func(devId, keyCode C.int32_t) rune
 448  
 449  func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
 450  	if jvm == nil {
 451  		panic("nil JVM")
 452  	}
 453  	runtime.LockOSThread()
 454  	defer runtime.UnlockOSThread()
 455  	var env *C.JNIEnv
 456  	if res := C.gio_jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
 457  		if res != C.JNI_EDETACHED {
 458  			panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
 459  		}
 460  		if C.gio_jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK {
 461  			panic(errors.New("runInJVM: AttachCurrentThread failed"))
 462  		}
 463  		defer C.gio_jni_DetachCurrentThread(jvm)
 464  	}
 465  
 466  	f(env)
 467  }
 468  
 469  func convertKeyCode(code C.jint) (string, bool) {
 470  	var n string
 471  	switch code {
 472  	case C.AKEYCODE_DPAD_UP:
 473  		n = key.NameUpArrow
 474  	case C.AKEYCODE_DPAD_DOWN:
 475  		n = key.NameDownArrow
 476  	case C.AKEYCODE_DPAD_LEFT:
 477  		n = key.NameLeftArrow
 478  	case C.AKEYCODE_DPAD_RIGHT:
 479  		n = key.NameRightArrow
 480  	case C.AKEYCODE_FORWARD_DEL:
 481  		n = key.NameDeleteForward
 482  	case C.AKEYCODE_DEL:
 483  		n = key.NameDeleteBackward
 484  	case C.AKEYCODE_NUMPAD_ENTER:
 485  		n = key.NameEnter
 486  	case C.AKEYCODE_ENTER:
 487  		n = key.NameEnter
 488  	default:
 489  		return "", false
 490  	}
 491  	return n, true
 492  }
 493  
 494  //export Java_org_gioui_GioView_onKeyEvent
 495  func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, t C.jlong) {
 496  	w := views[handle]
 497  	if n, ok := convertKeyCode(keyCode); ok {
 498  		w.callbacks.Event(key.Event{Name: n})
 499  	}
 500  	if r != 0 {
 501  		w.callbacks.Event(key.EditEvent{Text: string(rune(r))})
 502  	}
 503  }
 504  
 505  //export Java_org_gioui_GioView_onTouchEvent
 506  func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y, scrollX, scrollY C.jfloat, jbtns C.jint, t C.jlong) {
 507  	w := views[handle]
 508  	var typ pointer.Type
 509  	switch action {
 510  	case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN:
 511  		typ = pointer.Press
 512  	case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP:
 513  		typ = pointer.Release
 514  	case C.AMOTION_EVENT_ACTION_CANCEL:
 515  		typ = pointer.Cancel
 516  	case C.AMOTION_EVENT_ACTION_MOVE:
 517  		typ = pointer.Move
 518  	case C.AMOTION_EVENT_ACTION_SCROLL:
 519  		typ = pointer.Scroll
 520  	default:
 521  		return
 522  	}
 523  	var src pointer.Source
 524  	var btns pointer.Buttons
 525  	if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 {
 526  		btns |= pointer.ButtonPrimary
 527  	}
 528  	if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 {
 529  		btns |= pointer.ButtonSecondary
 530  	}
 531  	if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 {
 532  		btns |= pointer.ButtonTertiary
 533  	}
 534  	switch tool {
 535  	case C.AMOTION_EVENT_TOOL_TYPE_FINGER:
 536  		src = pointer.Touch
 537  	case C.AMOTION_EVENT_TOOL_TYPE_MOUSE:
 538  		src = pointer.Mouse
 539  	case C.AMOTION_EVENT_TOOL_TYPE_UNKNOWN:
 540  		// For example, triggered via 'adb shell input tap'.
 541  		// Instead of discarding it, treat it as a touch event.
 542  		src = pointer.Touch
 543  	default:
 544  		return
 545  	}
 546  	w.callbacks.Event(pointer.Event{
 547  		Type:      typ,
 548  		Source:    src,
 549  		Buttons:   btns,
 550  		PointerID: pointer.ID(pointerID),
 551  		Time:      time.Duration(t) * time.Millisecond,
 552  		Position:  f32.Point{X: float32(x), Y: float32(y)},
 553  		Scroll:    f32.Pt(float32(scrollX), float32(scrollY)),
 554  	})
 555  }
 556  
 557  func (w *window) ShowTextInput(show bool) {
 558  	runOnMain(func(env *C.JNIEnv) {
 559  		if w.view == 0 {
 560  			return
 561  		}
 562  		if show {
 563  			callVoidMethod(env, w.view, gioView.showTextInput)
 564  		} else {
 565  			callVoidMethod(env, w.view, gioView.hideTextInput)
 566  		}
 567  	})
 568  }
 569  
 570  func javaString(env *C.JNIEnv, str string) C.jstring {
 571  	if str == "" {
 572  		return 0
 573  	}
 574  	utf16Chars := utf16.Encode([]rune(str))
 575  	return C.gio_jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars)))
 576  }
 577  
 578  func varArgs(args []jvalue) *C.jvalue {
 579  	if len(args) == 0 {
 580  		return nil
 581  	}
 582  	return (*C.jvalue)(unsafe.Pointer(&args[0]))
 583  }
 584  
 585  func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error {
 586  	C.gio_jni_CallStaticVoidMethodA(env, cls, method, varArgs(args))
 587  	return exception(env)
 588  }
 589  
 590  func callStaticObjectMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) {
 591  	res := C.gio_jni_CallStaticObjectMethodA(env, cls, method, varArgs(args))
 592  	return res, exception(env)
 593  }
 594  
 595  func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) error {
 596  	C.gio_jni_CallVoidMethodA(env, obj, method, varArgs(args))
 597  	return exception(env)
 598  }
 599  
 600  func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) {
 601  	res := C.gio_jni_CallObjectMethodA(env, obj, method, varArgs(args))
 602  	return res, exception(env)
 603  }
 604  
 605  // exception returns an error corresponding to the pending
 606  // exception, or nil if no exception is pending. The pending
 607  // exception is cleared.
 608  func exception(env *C.JNIEnv) error {
 609  	thr := C.gio_jni_ExceptionOccurred(env)
 610  	if thr == 0 {
 611  		return nil
 612  	}
 613  	C.gio_jni_ExceptionClear(env)
 614  	cls := getObjectClass(env, C.jobject(thr))
 615  	toString := getMethodID(env, cls, "toString", "()Ljava/lang/String;")
 616  	msg, err := callObjectMethod(env, C.jobject(thr), toString)
 617  	if err != nil {
 618  		return err
 619  	}
 620  	return errors.New(goString(env, C.jstring(msg)))
 621  }
 622  
 623  func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass {
 624  	if obj == 0 {
 625  		panic("null object")
 626  	}
 627  	cls := C.gio_jni_GetObjectClass(env, C.jobject(obj))
 628  	if err := exception(env); err != nil {
 629  		// GetObjectClass should never fail.
 630  		panic(err)
 631  	}
 632  	return cls
 633  }
 634  
 635  // goString converts the JVM jstring to a Go string.
 636  func goString(env *C.JNIEnv, str C.jstring) string {
 637  	if str == 0 {
 638  		return ""
 639  	}
 640  	strlen := C.gio_jni_GetStringLength(env, C.jstring(str))
 641  	chars := C.gio_jni_GetStringChars(env, C.jstring(str))
 642  	var utf16Chars []uint16
 643  	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&utf16Chars))
 644  	hdr.Data = uintptr(unsafe.Pointer(chars))
 645  	hdr.Cap = int(strlen)
 646  	hdr.Len = int(strlen)
 647  	utf8 := utf16.Decode(utf16Chars)
 648  	return string(utf8)
 649  }
 650  
 651  func Main() {
 652  }
 653  
 654  func NewWindow(window Callbacks, opts *Options) error {
 655  	mainWindow.in <- windowAndOptions{window, opts}
 656  	return <-mainWindow.errs
 657  }
 658  
 659  func (w *window) WriteClipboard(s string) {
 660  	runOnMain(func(env *C.JNIEnv) {
 661  		jstr := javaString(env, s)
 662  		callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard,
 663  			jvalue(android.appCtx), jvalue(jstr))
 664  	})
 665  }
 666  
 667  func (w *window) ReadClipboard() {
 668  	runOnMain(func(env *C.JNIEnv) {
 669  		c, err := callStaticObjectMethod(env, android.gioCls, android.mreadClipboard,
 670  			jvalue(android.appCtx))
 671  		if err != nil {
 672  			return
 673  		}
 674  		content := goString(env, C.jstring(c))
 675  		w.callbacks.Event(clipboard.Event{Text: content})
 676  	})
 677  }
 678  
 679  func (w *window) Option(opts *Options) {}
 680  
 681  func (w *window) SetCursor(name pointer.CursorName) {
 682  	w.setState(func(state *windowState) {
 683  		state.cursor = &name
 684  	})
 685  }
 686  
 687  // setState adjust the window state on the main thread.
 688  func (w *window) setState(f func(state *windowState)) {
 689  	runOnMain(func(env *C.JNIEnv) {
 690  		f(&w.newState)
 691  		if w.view == 0 {
 692  			// No View attached. The state will be applied at next onCreateView.
 693  			return
 694  		}
 695  		old := w.state
 696  		state := w.newState
 697  		applyStateDiff(env, w.view, old, state)
 698  		w.state = state
 699  	})
 700  }
 701  
 702  func applyStateDiff(env *C.JNIEnv, view C.jobject, old, state windowState) {
 703  	if state.cursor != nil && old.cursor != state.cursor {
 704  		setCursor(env, view, *state.cursor)
 705  	}
 706  }
 707  
 708  func setCursor(env *C.JNIEnv, view C.jobject, name pointer.CursorName) {
 709  	var curID int
 710  	switch name {
 711  	default:
 712  		fallthrough
 713  	case pointer.CursorDefault:
 714  		curID = 1000 // TYPE_ARROW
 715  	case pointer.CursorText:
 716  		curID = 1008 // TYPE_TEXT
 717  	case pointer.CursorPointer:
 718  		curID = 1002 // TYPE_HAND
 719  	case pointer.CursorCrossHair:
 720  		curID = 1007 // TYPE_CROSSHAIR
 721  	case pointer.CursorColResize:
 722  		curID = 1014 // TYPE_HORIZONTAL_DOUBLE_ARROW
 723  	case pointer.CursorRowResize:
 724  		curID = 1015 // TYPE_VERTICAL_DOUBLE_ARROW
 725  	case pointer.CursorNone:
 726  		curID = 0 // TYPE_NULL
 727  	}
 728  	callVoidMethod(env, view, gioView.setCursor, jvalue(curID))
 729  }
 730  
 731  // Close the window. Not implemented for Android.
 732  func (w *window) Close() {}
 733  
 734  // runOnMain runs a function on the Java main thread.
 735  func runOnMain(f func(env *C.JNIEnv)) {
 736  	go func() {
 737  		mainFuncs <- f
 738  		runInJVM(javaVM(), func(env *C.JNIEnv) {
 739  			callStaticVoidMethod(env, android.gioCls, android.mwakeupMainThread)
 740  		})
 741  	}()
 742  }
 743  
 744  //export Java_org_gioui_Gio_scheduleMainFuncs
 745  func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) {
 746  	for {
 747  		select {
 748  		case f := <-mainFuncs:
 749  			f(env)
 750  		default:
 751  			return
 752  		}
 753  	}
 754  }
 755  
 756  func (_ ViewEvent) ImplementsEvent() {}
 757