os_android.go 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package app
  3. /*
  4. #cgo CFLAGS: -Werror
  5. #cgo LDFLAGS: -landroid
  6. #include <android/native_window_jni.h>
  7. #include <android/configuration.h>
  8. #include <android/keycodes.h>
  9. #include <android/input.h>
  10. #include <stdlib.h>
  11. static jint jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
  12. return (*vm)->GetEnv(vm, (void **)env, version);
  13. }
  14. static jint jni_GetJavaVM(JNIEnv *env, JavaVM **jvm) {
  15. return (*env)->GetJavaVM(env, jvm);
  16. }
  17. static jint jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
  18. return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
  19. }
  20. static jint jni_DetachCurrentThread(JavaVM *vm) {
  21. return (*vm)->DetachCurrentThread(vm);
  22. }
  23. static jobject jni_NewGlobalRef(JNIEnv *env, jobject obj) {
  24. return (*env)->NewGlobalRef(env, obj);
  25. }
  26. static void jni_DeleteGlobalRef(JNIEnv *env, jobject obj) {
  27. (*env)->DeleteGlobalRef(env, obj);
  28. }
  29. static jclass jni_GetObjectClass(JNIEnv *env, jobject obj) {
  30. return (*env)->GetObjectClass(env, obj);
  31. }
  32. static jmethodID jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
  33. return (*env)->GetMethodID(env, clazz, name, sig);
  34. }
  35. static jmethodID jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
  36. return (*env)->GetStaticMethodID(env, clazz, name, sig);
  37. }
  38. static jfloat jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
  39. return (*env)->CallFloatMethod(env, obj, methodID);
  40. }
  41. static jint jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
  42. return (*env)->CallIntMethod(env, obj, methodID);
  43. }
  44. static void jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) {
  45. (*env)->CallStaticVoidMethodA(env, cls, methodID, args);
  46. }
  47. static void jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) {
  48. (*env)->CallVoidMethodA(env, obj, methodID, args);
  49. }
  50. static jboolean jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) {
  51. return (*env)->CallBooleanMethodA(env, obj, methodID, args);
  52. }
  53. static jbyte *jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) {
  54. return (*env)->GetByteArrayElements(env, arr, NULL);
  55. }
  56. static void jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes) {
  57. (*env)->ReleaseByteArrayElements(env, arr, bytes, JNI_ABORT);
  58. }
  59. static jsize jni_GetArrayLength(JNIEnv *env, jbyteArray arr) {
  60. return (*env)->GetArrayLength(env, arr);
  61. }
  62. static jstring jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) {
  63. return (*env)->NewString(env, unicodeChars, len);
  64. }
  65. static jsize jni_GetStringLength(JNIEnv *env, jstring str) {
  66. return (*env)->GetStringLength(env, str);
  67. }
  68. static const jchar *jni_GetStringChars(JNIEnv *env, jstring str) {
  69. return (*env)->GetStringChars(env, str, NULL);
  70. }
  71. static jthrowable jni_ExceptionOccurred(JNIEnv *env) {
  72. return (*env)->ExceptionOccurred(env);
  73. }
  74. static void jni_ExceptionClear(JNIEnv *env) {
  75. (*env)->ExceptionClear(env);
  76. }
  77. static jobject jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
  78. return (*env)->CallObjectMethodA(env, obj, method, args);
  79. }
  80. static jobject jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) {
  81. return (*env)->CallStaticObjectMethodA(env, cls, method, args);
  82. }
  83. static jclass jni_FindClass(JNIEnv *env, char *name) {
  84. return (*env)->FindClass(env, name);
  85. }
  86. static jobject jni_NewObjectA(JNIEnv *env, jclass cls, jmethodID cons, jvalue *args) {
  87. return (*env)->NewObjectA(env, cls, cons, args);
  88. }
  89. */
  90. import "C"
  91. import (
  92. "errors"
  93. "fmt"
  94. "image"
  95. "image/color"
  96. "io"
  97. "math"
  98. "os"
  99. "path/filepath"
  100. "runtime"
  101. "runtime/cgo"
  102. "runtime/debug"
  103. "strings"
  104. "sync"
  105. "time"
  106. "unicode/utf16"
  107. "unsafe"
  108. "gioui.org/internal/f32color"
  109. "gioui.org/op"
  110. "gioui.org/f32"
  111. "gioui.org/io/event"
  112. "gioui.org/io/input"
  113. "gioui.org/io/key"
  114. "gioui.org/io/pointer"
  115. "gioui.org/io/semantic"
  116. "gioui.org/io/system"
  117. "gioui.org/io/transfer"
  118. "gioui.org/unit"
  119. )
  120. type window struct {
  121. callbacks *callbacks
  122. loop *eventLoop
  123. view C.jobject
  124. handle cgo.Handle
  125. dpi int
  126. fontScale float32
  127. insets pixelInsets
  128. visible bool
  129. started bool
  130. animating bool
  131. win *C.ANativeWindow
  132. config Config
  133. inputHint key.InputHint
  134. semantic struct {
  135. hoverID input.SemanticID
  136. rootID input.SemanticID
  137. focusID input.SemanticID
  138. diffs []input.SemanticID
  139. }
  140. }
  141. // gioView hold cached JNI methods for GioView.
  142. var gioView struct {
  143. once sync.Once
  144. getDensity C.jmethodID
  145. getFontScale C.jmethodID
  146. showTextInput C.jmethodID
  147. hideTextInput C.jmethodID
  148. setInputHint C.jmethodID
  149. postFrameCallback C.jmethodID
  150. invalidate C.jmethodID // requests draw, called from UI thread
  151. setCursor C.jmethodID
  152. setOrientation C.jmethodID
  153. setNavigationColor C.jmethodID
  154. setStatusColor C.jmethodID
  155. setFullscreen C.jmethodID
  156. unregister C.jmethodID
  157. sendA11yEvent C.jmethodID
  158. sendA11yChange C.jmethodID
  159. isA11yActive C.jmethodID
  160. restartInput C.jmethodID
  161. updateSelection C.jmethodID
  162. updateCaret C.jmethodID
  163. }
  164. type pixelInsets struct {
  165. top, bottom, left, right int
  166. }
  167. // AndroidViewEvent is sent whenever the Window's underlying Android view
  168. // changes.
  169. type AndroidViewEvent struct {
  170. // View is a JNI global reference to the android.view.View
  171. // instance backing the Window. The reference is valid until
  172. // the next ViewEvent is received.
  173. // A zero View means that there is currently no view attached.
  174. View uintptr
  175. }
  176. type jvalue uint64 // The largest JNI type fits in 64 bits.
  177. var dataDirChan = make(chan string, 1)
  178. var android struct {
  179. // mu protects all fields of this structure. However, once a
  180. // non-nil jvm is returned from javaVM, all the other fields may
  181. // be accessed unlocked.
  182. mu sync.Mutex
  183. jvm *C.JavaVM
  184. // appCtx is the global Android App context.
  185. appCtx C.jobject
  186. // gioCls is the class of the Gio class.
  187. gioCls C.jclass
  188. mwriteClipboard C.jmethodID
  189. mreadClipboard C.jmethodID
  190. mwakeupMainThread C.jmethodID
  191. // android.view.accessibility.AccessibilityNodeInfo class.
  192. accessibilityNodeInfo struct {
  193. cls C.jclass
  194. // addChild(View, int)
  195. addChild C.jmethodID
  196. // setBoundsInScreen(Rect)
  197. setBoundsInScreen C.jmethodID
  198. // setText(CharSequence)
  199. setText C.jmethodID
  200. // setContentDescription(CharSequence)
  201. setContentDescription C.jmethodID
  202. // setParent(View, int)
  203. setParent C.jmethodID
  204. // addAction(int)
  205. addAction C.jmethodID
  206. // setClassName(CharSequence)
  207. setClassName C.jmethodID
  208. // setCheckable(boolean)
  209. setCheckable C.jmethodID
  210. // setSelected(boolean)
  211. setSelected C.jmethodID
  212. // setChecked(boolean)
  213. setChecked C.jmethodID
  214. // setEnabled(boolean)
  215. setEnabled C.jmethodID
  216. // setAccessibilityFocused(boolean)
  217. setAccessibilityFocused C.jmethodID
  218. }
  219. // android.graphics.Rect class.
  220. rect struct {
  221. cls C.jclass
  222. // (int, int, int, int) constructor.
  223. cons C.jmethodID
  224. }
  225. strings struct {
  226. // "android.view.View"
  227. androidViewView C.jstring
  228. // "android.widget.Button"
  229. androidWidgetButton C.jstring
  230. // "android.widget.CheckBox"
  231. androidWidgetCheckBox C.jstring
  232. // "android.widget.EditText"
  233. androidWidgetEditText C.jstring
  234. // "android.widget.RadioButton"
  235. androidWidgetRadioButton C.jstring
  236. // "android.widget.Switch"
  237. androidWidgetSwitch C.jstring
  238. }
  239. }
  240. var windows = make(map[*callbacks]*window)
  241. var mainWindow = newWindowRendezvous()
  242. var mainFuncs = make(chan func(env *C.JNIEnv), 1)
  243. var (
  244. dataDirOnce sync.Once
  245. dataPath string
  246. )
  247. var (
  248. newAndroidVulkanContext func(w *window) (context, error)
  249. newAndroidGLESContext func(w *window) (context, error)
  250. )
  251. // AccessibilityNodeProvider.HOST_VIEW_ID.
  252. const HOST_VIEW_ID = -1
  253. const (
  254. // AccessibilityEvent constants.
  255. TYPE_VIEW_HOVER_ENTER = 128
  256. TYPE_VIEW_HOVER_EXIT = 256
  257. )
  258. const (
  259. // AccessibilityNodeInfo constants.
  260. ACTION_ACCESSIBILITY_FOCUS = 64
  261. ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128
  262. ACTION_CLICK = 16
  263. )
  264. func (w *window) NewContext() (context, error) {
  265. funcs := []func(w *window) (context, error){newAndroidGLESContext, newAndroidVulkanContext}
  266. var firstErr error
  267. for _, f := range funcs {
  268. if f == nil {
  269. continue
  270. }
  271. c, err := f(w)
  272. if err != nil {
  273. if firstErr == nil {
  274. firstErr = err
  275. }
  276. continue
  277. }
  278. return c, nil
  279. }
  280. if firstErr != nil {
  281. return nil, firstErr
  282. }
  283. return nil, errors.New("x11: no available GPU backends")
  284. }
  285. func dataDir() (string, error) {
  286. dataDirOnce.Do(func() {
  287. dataPath = <-dataDirChan
  288. })
  289. return dataPath, nil
  290. }
  291. func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
  292. m := C.CString(method)
  293. defer C.free(unsafe.Pointer(m))
  294. s := C.CString(sig)
  295. defer C.free(unsafe.Pointer(s))
  296. jm := C.jni_GetMethodID(env, class, m, s)
  297. if err := exception(env); err != nil {
  298. panic(err)
  299. }
  300. return jm
  301. }
  302. func getStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
  303. m := C.CString(method)
  304. defer C.free(unsafe.Pointer(m))
  305. s := C.CString(sig)
  306. defer C.free(unsafe.Pointer(s))
  307. jm := C.jni_GetStaticMethodID(env, class, m, s)
  308. if err := exception(env); err != nil {
  309. panic(err)
  310. }
  311. return jm
  312. }
  313. //export Java_org_gioui_Gio_runGoMain
  314. func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) {
  315. initJVM(env, class, context)
  316. dirBytes := C.jni_GetByteArrayElements(env, jdataDir)
  317. if dirBytes == nil {
  318. panic("runGoMain: GetByteArrayElements failed")
  319. }
  320. n := C.jni_GetArrayLength(env, jdataDir)
  321. dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n)
  322. // Set XDG_CACHE_HOME to make os.UserCacheDir work.
  323. if _, exists := os.LookupEnv("XDG_CACHE_HOME"); !exists {
  324. cachePath := filepath.Join(dataDir, "cache")
  325. os.Setenv("XDG_CACHE_HOME", cachePath)
  326. }
  327. // Set XDG_CONFIG_HOME to make os.UserConfigDir work.
  328. if _, exists := os.LookupEnv("XDG_CONFIG_HOME"); !exists {
  329. cfgPath := filepath.Join(dataDir, "config")
  330. os.Setenv("XDG_CONFIG_HOME", cfgPath)
  331. }
  332. // Set HOME to make os.UserHomeDir work.
  333. if _, exists := os.LookupEnv("HOME"); !exists {
  334. os.Setenv("HOME", dataDir)
  335. }
  336. dataDirChan <- dataDir
  337. C.jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
  338. runMain()
  339. }
  340. func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) {
  341. android.mu.Lock()
  342. defer android.mu.Unlock()
  343. if res := C.jni_GetJavaVM(env, &android.jvm); res != 0 {
  344. panic("gio: GetJavaVM failed")
  345. }
  346. android.appCtx = C.jni_NewGlobalRef(env, ctx)
  347. android.gioCls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(gio)))
  348. cls := findClass(env, "android/view/accessibility/AccessibilityNodeInfo")
  349. android.accessibilityNodeInfo.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls)))
  350. android.accessibilityNodeInfo.addChild = getMethodID(env, cls, "addChild", "(Landroid/view/View;I)V")
  351. android.accessibilityNodeInfo.setBoundsInScreen = getMethodID(env, cls, "setBoundsInScreen", "(Landroid/graphics/Rect;)V")
  352. android.accessibilityNodeInfo.setText = getMethodID(env, cls, "setText", "(Ljava/lang/CharSequence;)V")
  353. android.accessibilityNodeInfo.setContentDescription = getMethodID(env, cls, "setContentDescription", "(Ljava/lang/CharSequence;)V")
  354. android.accessibilityNodeInfo.setParent = getMethodID(env, cls, "setParent", "(Landroid/view/View;I)V")
  355. android.accessibilityNodeInfo.addAction = getMethodID(env, cls, "addAction", "(I)V")
  356. android.accessibilityNodeInfo.setClassName = getMethodID(env, cls, "setClassName", "(Ljava/lang/CharSequence;)V")
  357. android.accessibilityNodeInfo.setCheckable = getMethodID(env, cls, "setCheckable", "(Z)V")
  358. android.accessibilityNodeInfo.setSelected = getMethodID(env, cls, "setSelected", "(Z)V")
  359. android.accessibilityNodeInfo.setChecked = getMethodID(env, cls, "setChecked", "(Z)V")
  360. android.accessibilityNodeInfo.setEnabled = getMethodID(env, cls, "setEnabled", "(Z)V")
  361. android.accessibilityNodeInfo.setAccessibilityFocused = getMethodID(env, cls, "setAccessibilityFocused", "(Z)V")
  362. cls = findClass(env, "android/graphics/Rect")
  363. android.rect.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls)))
  364. android.rect.cons = getMethodID(env, cls, "<init>", "(IIII)V")
  365. android.mwriteClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V")
  366. android.mreadClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;")
  367. android.mwakeupMainThread = getStaticMethodID(env, gio, "wakeupMainThread", "()V")
  368. intern := func(s string) C.jstring {
  369. ref := C.jni_NewGlobalRef(env, C.jobject(javaString(env, s)))
  370. return C.jstring(ref)
  371. }
  372. android.strings.androidViewView = intern("android.view.View")
  373. android.strings.androidWidgetButton = intern("android.widget.Button")
  374. android.strings.androidWidgetCheckBox = intern("android.widget.CheckBox")
  375. android.strings.androidWidgetEditText = intern("android.widget.EditText")
  376. android.strings.androidWidgetRadioButton = intern("android.widget.RadioButton")
  377. android.strings.androidWidgetSwitch = intern("android.widget.Switch")
  378. }
  379. // JavaVM returns the global JNI JavaVM.
  380. func JavaVM() uintptr {
  381. jvm := javaVM()
  382. return uintptr(unsafe.Pointer(jvm))
  383. }
  384. func javaVM() *C.JavaVM {
  385. android.mu.Lock()
  386. defer android.mu.Unlock()
  387. return android.jvm
  388. }
  389. // AppContext returns the global Application context as a JNI jobject.
  390. func AppContext() uintptr {
  391. android.mu.Lock()
  392. defer android.mu.Unlock()
  393. return uintptr(android.appCtx)
  394. }
  395. //export Java_org_gioui_GioView_onCreateView
  396. func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
  397. gioView.once.Do(func() {
  398. m := &gioView
  399. m.getDensity = getMethodID(env, class, "getDensity", "()I")
  400. m.getFontScale = getMethodID(env, class, "getFontScale", "()F")
  401. m.showTextInput = getMethodID(env, class, "showTextInput", "()V")
  402. m.hideTextInput = getMethodID(env, class, "hideTextInput", "()V")
  403. m.setInputHint = getMethodID(env, class, "setInputHint", "(I)V")
  404. m.postFrameCallback = getMethodID(env, class, "postFrameCallback", "()V")
  405. m.invalidate = getMethodID(env, class, "invalidate", "()V")
  406. m.setCursor = getMethodID(env, class, "setCursor", "(I)V")
  407. m.setOrientation = getMethodID(env, class, "setOrientation", "(II)V")
  408. m.setNavigationColor = getMethodID(env, class, "setNavigationColor", "(II)V")
  409. m.setStatusColor = getMethodID(env, class, "setStatusColor", "(II)V")
  410. m.setFullscreen = getMethodID(env, class, "setFullscreen", "(Z)V")
  411. m.unregister = getMethodID(env, class, "unregister", "()V")
  412. m.sendA11yEvent = getMethodID(env, class, "sendA11yEvent", "(II)V")
  413. m.sendA11yChange = getMethodID(env, class, "sendA11yChange", "(I)V")
  414. m.isA11yActive = getMethodID(env, class, "isA11yActive", "()Z")
  415. m.restartInput = getMethodID(env, class, "restartInput", "()V")
  416. m.updateSelection = getMethodID(env, class, "updateSelection", "()V")
  417. m.updateCaret = getMethodID(env, class, "updateCaret", "(FFFFFFFFFF)V")
  418. })
  419. view = C.jni_NewGlobalRef(env, view)
  420. wopts := <-mainWindow.out
  421. var cnf Config
  422. w, ok := windows[wopts.window]
  423. if !ok {
  424. w = &window{
  425. callbacks: wopts.window,
  426. }
  427. w.loop = newEventLoop(w.callbacks, w.wakeup)
  428. w.callbacks.SetDriver(w)
  429. cnf.apply(unit.Metric{}, wopts.options)
  430. windows[wopts.window] = w
  431. } else {
  432. cnf = w.config
  433. }
  434. mainWindow.windows <- struct{}{}
  435. if w.view != 0 {
  436. w.detach(env)
  437. }
  438. w.view = view
  439. w.visible = false
  440. w.handle = cgo.NewHandle(w)
  441. w.loadConfig(env, class)
  442. w.setConfig(env, cnf)
  443. w.SetInputHint(w.inputHint)
  444. w.processEvent(AndroidViewEvent{View: uintptr(view)})
  445. return C.jlong(w.handle)
  446. }
  447. //export Java_org_gioui_GioView_onDestroyView
  448. func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
  449. w := cgo.Handle(handle).Value().(*window)
  450. w.detach(env)
  451. }
  452. //export Java_org_gioui_GioView_onStopView
  453. func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
  454. w := cgo.Handle(handle).Value().(*window)
  455. w.started = false
  456. w.visible = false
  457. }
  458. //export Java_org_gioui_GioView_onStartView
  459. func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
  460. w := cgo.Handle(handle).Value().(*window)
  461. w.started = true
  462. if w.win != nil {
  463. w.setVisible(env)
  464. }
  465. }
  466. //export Java_org_gioui_GioView_onSurfaceDestroyed
  467. func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) {
  468. w := cgo.Handle(handle).Value().(*window)
  469. w.win = nil
  470. w.visible = false
  471. }
  472. //export Java_org_gioui_GioView_onSurfaceChanged
  473. func Java_org_gioui_GioView_onSurfaceChanged(env *C.JNIEnv, class C.jclass, handle C.jlong, surf C.jobject) {
  474. w := cgo.Handle(handle).Value().(*window)
  475. w.win = C.ANativeWindow_fromSurface(env, surf)
  476. if w.started {
  477. w.setVisible(env)
  478. }
  479. }
  480. //export Java_org_gioui_GioView_onLowMemory
  481. func Java_org_gioui_GioView_onLowMemory(env *C.JNIEnv, class C.jclass) {
  482. runtime.GC()
  483. debug.FreeOSMemory()
  484. }
  485. //export Java_org_gioui_GioView_onConfigurationChanged
  486. func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) {
  487. w := cgo.Handle(view).Value().(*window)
  488. w.loadConfig(env, class)
  489. w.draw(env, true)
  490. }
  491. //export Java_org_gioui_GioView_onFrameCallback
  492. func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong) {
  493. w, exist := cgo.Handle(view).Value().(*window)
  494. if !exist {
  495. return
  496. }
  497. w.draw(env, false)
  498. }
  499. //export Java_org_gioui_GioView_onBack
  500. func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean {
  501. w := cgo.Handle(view).Value().(*window)
  502. if w.processEvent(key.Event{Name: key.NameBack}) {
  503. return C.JNI_TRUE
  504. }
  505. return C.JNI_FALSE
  506. }
  507. //export Java_org_gioui_GioView_onFocusChange
  508. func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
  509. w := cgo.Handle(view).Value().(*window)
  510. w.config.Focused = focus == C.JNI_TRUE
  511. w.processEvent(ConfigEvent{Config: w.config})
  512. }
  513. //export Java_org_gioui_GioView_onWindowInsets
  514. func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) {
  515. w := cgo.Handle(view).Value().(*window)
  516. w.insets = pixelInsets{
  517. top: int(top),
  518. bottom: int(bottom),
  519. left: int(left),
  520. right: int(right),
  521. }
  522. w.draw(env, true)
  523. }
  524. //export Java_org_gioui_GioView_initializeAccessibilityNodeInfo
  525. func Java_org_gioui_GioView_initializeAccessibilityNodeInfo(env *C.JNIEnv, class C.jclass, view C.jlong, virtID, screenX, screenY C.jint, info C.jobject) C.jobject {
  526. w := cgo.Handle(view).Value().(*window)
  527. semID := w.semIDFor(virtID)
  528. sem, found := w.callbacks.LookupSemantic(semID)
  529. if found {
  530. off := image.Pt(int(screenX), int(screenY))
  531. if err := w.initAccessibilityNodeInfo(env, sem, off, info); err != nil {
  532. panic(err)
  533. }
  534. }
  535. return info
  536. }
  537. //export Java_org_gioui_GioView_onTouchExploration
  538. func Java_org_gioui_GioView_onTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong, x, y C.jfloat) {
  539. w := cgo.Handle(view).Value().(*window)
  540. semID, _ := w.callbacks.SemanticAt(f32.Pt(float32(x), float32(y)))
  541. if w.semantic.hoverID == semID {
  542. return
  543. }
  544. // Android expects ENTER before EXIT.
  545. if semID != 0 {
  546. callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_ENTER, jvalue(w.virtualIDFor(semID)))
  547. }
  548. if prevID := w.semantic.hoverID; prevID != 0 {
  549. callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(prevID)))
  550. }
  551. w.semantic.hoverID = semID
  552. }
  553. //export Java_org_gioui_GioView_onExitTouchExploration
  554. func Java_org_gioui_GioView_onExitTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong) {
  555. w := cgo.Handle(view).Value().(*window)
  556. if w.semantic.hoverID != 0 {
  557. callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(w.semantic.hoverID)))
  558. w.semantic.hoverID = 0
  559. }
  560. }
  561. //export Java_org_gioui_GioView_onA11yFocus
  562. func Java_org_gioui_GioView_onA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) {
  563. w := cgo.Handle(view).Value().(*window)
  564. if semID := w.semIDFor(virtID); semID != w.semantic.focusID {
  565. w.semantic.focusID = semID
  566. // Android needs invalidate to refresh the TalkBack focus indicator.
  567. callVoidMethod(env, w.view, gioView.invalidate)
  568. }
  569. }
  570. //export Java_org_gioui_GioView_onClearA11yFocus
  571. func Java_org_gioui_GioView_onClearA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) {
  572. w := cgo.Handle(view).Value().(*window)
  573. if w.semantic.focusID == w.semIDFor(virtID) {
  574. w.semantic.focusID = 0
  575. }
  576. }
  577. func (w *window) ProcessEvent(e event.Event) {
  578. w.processEvent(e)
  579. }
  580. func (w *window) processEvent(e event.Event) bool {
  581. if !w.callbacks.ProcessEvent(e) {
  582. return false
  583. }
  584. w.loop.FlushEvents()
  585. return true
  586. }
  587. func (w *window) Event() event.Event {
  588. return w.loop.Event()
  589. }
  590. func (w *window) Invalidate() {
  591. w.loop.Invalidate()
  592. }
  593. func (w *window) Run(f func()) {
  594. w.loop.Run(f)
  595. }
  596. func (w *window) Frame(frame *op.Ops) {
  597. w.loop.Frame(frame)
  598. }
  599. func (w *window) initAccessibilityNodeInfo(env *C.JNIEnv, sem input.SemanticNode, off image.Point, info C.jobject) error {
  600. for _, ch := range sem.Children {
  601. err := callVoidMethod(env, info, android.accessibilityNodeInfo.addChild, jvalue(w.view), jvalue(w.virtualIDFor(ch.ID)))
  602. if err != nil {
  603. return err
  604. }
  605. }
  606. if sem.ParentID != 0 {
  607. if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setParent, jvalue(w.view), jvalue(w.virtualIDFor(sem.ParentID))); err != nil {
  608. return err
  609. }
  610. b := sem.Desc.Bounds.Add(off)
  611. rect, err := newObject(env, android.rect.cls, android.rect.cons,
  612. jvalue(b.Min.X),
  613. jvalue(b.Min.Y),
  614. jvalue(b.Max.X),
  615. jvalue(b.Max.Y),
  616. )
  617. if err != nil {
  618. return err
  619. }
  620. if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setBoundsInScreen, jvalue(rect)); err != nil {
  621. return err
  622. }
  623. }
  624. d := sem.Desc
  625. if l := d.Label; l != "" {
  626. jlbl := javaString(env, l)
  627. if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setText, jvalue(jlbl)); err != nil {
  628. return err
  629. }
  630. }
  631. if d.Description != "" {
  632. jd := javaString(env, d.Description)
  633. if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setContentDescription, jvalue(jd)); err != nil {
  634. return err
  635. }
  636. }
  637. addAction := func(act C.jint) {
  638. if err := callVoidMethod(env, info, android.accessibilityNodeInfo.addAction, jvalue(act)); err != nil {
  639. panic(err)
  640. }
  641. }
  642. if d.Gestures&input.ClickGesture != 0 {
  643. addAction(ACTION_CLICK)
  644. }
  645. clsName := android.strings.androidViewView
  646. selectMethod := android.accessibilityNodeInfo.setChecked
  647. checkable := false
  648. switch d.Class {
  649. case semantic.Button:
  650. clsName = android.strings.androidWidgetButton
  651. case semantic.CheckBox:
  652. checkable = true
  653. clsName = android.strings.androidWidgetCheckBox
  654. case semantic.Editor:
  655. clsName = android.strings.androidWidgetEditText
  656. case semantic.RadioButton:
  657. selectMethod = android.accessibilityNodeInfo.setSelected
  658. clsName = android.strings.androidWidgetRadioButton
  659. case semantic.Switch:
  660. checkable = true
  661. clsName = android.strings.androidWidgetSwitch
  662. }
  663. if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setClassName, jvalue(clsName)); err != nil {
  664. panic(err)
  665. }
  666. if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setCheckable, jvalue(javaBool(checkable))); err != nil {
  667. panic(err)
  668. }
  669. if err := callVoidMethod(env, info, selectMethod, jvalue(javaBool(d.Selected))); err != nil {
  670. panic(err)
  671. }
  672. if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setEnabled, jvalue(javaBool(!d.Disabled))); err != nil {
  673. panic(err)
  674. }
  675. isFocus := w.semantic.focusID == sem.ID
  676. if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setAccessibilityFocused, jvalue(javaBool(isFocus))); err != nil {
  677. panic(err)
  678. }
  679. if isFocus {
  680. addAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS)
  681. } else {
  682. addAction(ACTION_ACCESSIBILITY_FOCUS)
  683. }
  684. return nil
  685. }
  686. func (w *window) virtualIDFor(id input.SemanticID) C.jint {
  687. if id == w.semantic.rootID {
  688. return HOST_VIEW_ID
  689. }
  690. return C.jint(id)
  691. }
  692. func (w *window) semIDFor(virtID C.jint) input.SemanticID {
  693. if virtID == HOST_VIEW_ID {
  694. return w.semantic.rootID
  695. }
  696. return input.SemanticID(virtID)
  697. }
  698. func (w *window) detach(env *C.JNIEnv) {
  699. callVoidMethod(env, w.view, gioView.unregister)
  700. w.processEvent(AndroidViewEvent{})
  701. w.handle.Delete()
  702. C.jni_DeleteGlobalRef(env, w.view)
  703. w.view = 0
  704. }
  705. func (w *window) setVisible(env *C.JNIEnv) {
  706. width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win)
  707. if width == 0 || height == 0 {
  708. return
  709. }
  710. w.visible = true
  711. w.draw(env, true)
  712. }
  713. func (w *window) setVisual(visID int) error {
  714. if C.ANativeWindow_setBuffersGeometry(w.win, 0, 0, C.int32_t(visID)) != 0 {
  715. return errors.New("ANativeWindow_setBuffersGeometry failed")
  716. }
  717. return nil
  718. }
  719. func (w *window) nativeWindow() (*C.ANativeWindow, int, int) {
  720. width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win)
  721. return w.win, int(width), int(height)
  722. }
  723. func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) {
  724. dpi := int(C.jni_CallIntMethod(env, w.view, gioView.getDensity))
  725. w.fontScale = float32(C.jni_CallFloatMethod(env, w.view, gioView.getFontScale))
  726. switch dpi {
  727. case C.ACONFIGURATION_DENSITY_NONE,
  728. C.ACONFIGURATION_DENSITY_DEFAULT,
  729. C.ACONFIGURATION_DENSITY_ANY:
  730. // Assume standard density.
  731. w.dpi = C.ACONFIGURATION_DENSITY_MEDIUM
  732. default:
  733. w.dpi = int(dpi)
  734. }
  735. }
  736. func (w *window) SetAnimating(anim bool) {
  737. w.animating = anim
  738. if anim {
  739. runInJVM(javaVM(), func(env *C.JNIEnv) {
  740. callVoidMethod(env, w.view, gioView.postFrameCallback)
  741. })
  742. }
  743. }
  744. func (w *window) draw(env *C.JNIEnv, sync bool) {
  745. if !w.visible {
  746. return
  747. }
  748. size := image.Pt(int(C.ANativeWindow_getWidth(w.win)), int(C.ANativeWindow_getHeight(w.win)))
  749. if size != w.config.Size {
  750. w.config.Size = size
  751. w.processEvent(ConfigEvent{Config: w.config})
  752. }
  753. if size.X == 0 || size.Y == 0 {
  754. return
  755. }
  756. const inchPrDp = 1.0 / 160
  757. ppdp := float32(w.dpi) * inchPrDp
  758. dppp := unit.Dp(1.0 / ppdp)
  759. insets := Insets{
  760. Top: unit.Dp(w.insets.top) * dppp,
  761. Bottom: unit.Dp(w.insets.bottom) * dppp,
  762. Left: unit.Dp(w.insets.left) * dppp,
  763. Right: unit.Dp(w.insets.right) * dppp,
  764. }
  765. w.processEvent(frameEvent{
  766. FrameEvent: FrameEvent{
  767. Now: time.Now(),
  768. Size: w.config.Size,
  769. Insets: insets,
  770. Metric: unit.Metric{
  771. PxPerDp: ppdp,
  772. PxPerSp: w.fontScale * ppdp,
  773. },
  774. },
  775. Sync: sync,
  776. })
  777. if w.animating {
  778. callVoidMethod(env, w.view, gioView.postFrameCallback)
  779. }
  780. a11yActive, err := callBooleanMethod(env, w.view, gioView.isA11yActive)
  781. if err != nil {
  782. panic(err)
  783. }
  784. if a11yActive {
  785. if newR, oldR := w.callbacks.SemanticRoot(), w.semantic.rootID; newR != oldR {
  786. // Remap focus and hover.
  787. if oldR == w.semantic.hoverID {
  788. w.semantic.hoverID = newR
  789. }
  790. if oldR == w.semantic.focusID {
  791. w.semantic.focusID = newR
  792. }
  793. w.semantic.rootID = newR
  794. callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(newR)))
  795. }
  796. w.semantic.diffs = w.callbacks.AppendSemanticDiffs(w.semantic.diffs[:0])
  797. for _, id := range w.semantic.diffs {
  798. callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(id)))
  799. }
  800. }
  801. }
  802. func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
  803. if jvm == nil {
  804. panic("nil JVM")
  805. }
  806. runtime.LockOSThread()
  807. defer runtime.UnlockOSThread()
  808. var env *C.JNIEnv
  809. if res := C.jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
  810. if res != C.JNI_EDETACHED {
  811. panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
  812. }
  813. if C.jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK {
  814. panic(errors.New("runInJVM: AttachCurrentThread failed"))
  815. }
  816. defer C.jni_DetachCurrentThread(jvm)
  817. }
  818. f(env)
  819. }
  820. func convertKeyCode(code C.jint) (key.Name, bool) {
  821. var n key.Name
  822. switch code {
  823. case C.AKEYCODE_FORWARD_DEL:
  824. n = key.NameDeleteForward
  825. case C.AKEYCODE_DEL:
  826. n = key.NameDeleteBackward
  827. case C.AKEYCODE_NUMPAD_ENTER:
  828. n = key.NameEnter
  829. case C.AKEYCODE_ENTER:
  830. n = key.NameReturn
  831. case C.AKEYCODE_CTRL_LEFT, C.AKEYCODE_CTRL_RIGHT:
  832. n = key.NameCtrl
  833. case C.AKEYCODE_SHIFT_LEFT, C.AKEYCODE_SHIFT_RIGHT:
  834. n = key.NameShift
  835. case C.AKEYCODE_ALT_LEFT, C.AKEYCODE_ALT_RIGHT:
  836. n = key.NameAlt
  837. case C.AKEYCODE_META_LEFT, C.AKEYCODE_META_RIGHT:
  838. n = key.NameSuper
  839. case C.AKEYCODE_DPAD_UP:
  840. n = key.NameUpArrow
  841. case C.AKEYCODE_DPAD_DOWN:
  842. n = key.NameDownArrow
  843. case C.AKEYCODE_DPAD_LEFT:
  844. n = key.NameLeftArrow
  845. case C.AKEYCODE_DPAD_RIGHT:
  846. n = key.NameRightArrow
  847. default:
  848. return "", false
  849. }
  850. return n, true
  851. }
  852. //export Java_org_gioui_GioView_onKeyEvent
  853. func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, pressed C.jboolean, t C.jlong) {
  854. w := cgo.Handle(handle).Value().(*window)
  855. if pressed == C.JNI_TRUE && keyCode == C.AKEYCODE_DPAD_CENTER {
  856. w.callbacks.ClickFocus()
  857. return
  858. }
  859. if n, ok := convertKeyCode(keyCode); ok {
  860. state := key.Release
  861. if pressed == C.JNI_TRUE {
  862. state = key.Press
  863. }
  864. w.processEvent(key.Event{Name: n, State: state})
  865. }
  866. if pressed == C.JNI_TRUE && r != 0 && r != '\n' { // Checking for "\n" to prevent duplication with key.NameEnter (gio#224).
  867. w.callbacks.EditorInsert(string(rune(r)))
  868. }
  869. }
  870. //export Java_org_gioui_GioView_onTouchEvent
  871. 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) {
  872. w := cgo.Handle(handle).Value().(*window)
  873. var kind pointer.Kind
  874. switch action {
  875. case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN:
  876. kind = pointer.Press
  877. case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP:
  878. kind = pointer.Release
  879. case C.AMOTION_EVENT_ACTION_CANCEL:
  880. kind = pointer.Cancel
  881. case C.AMOTION_EVENT_ACTION_MOVE:
  882. kind = pointer.Move
  883. case C.AMOTION_EVENT_ACTION_SCROLL:
  884. kind = pointer.Scroll
  885. default:
  886. return
  887. }
  888. var src pointer.Source
  889. var btns pointer.Buttons
  890. if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 {
  891. btns |= pointer.ButtonPrimary
  892. }
  893. if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 {
  894. btns |= pointer.ButtonSecondary
  895. }
  896. if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 {
  897. btns |= pointer.ButtonTertiary
  898. }
  899. switch tool {
  900. case C.AMOTION_EVENT_TOOL_TYPE_FINGER:
  901. src = pointer.Touch
  902. case C.AMOTION_EVENT_TOOL_TYPE_STYLUS:
  903. src = pointer.Touch
  904. case C.AMOTION_EVENT_TOOL_TYPE_MOUSE:
  905. src = pointer.Mouse
  906. case C.AMOTION_EVENT_TOOL_TYPE_UNKNOWN:
  907. // For example, triggered via 'adb shell input tap'.
  908. // Instead of discarding it, treat it as a touch event.
  909. src = pointer.Touch
  910. default:
  911. return
  912. }
  913. w.processEvent(pointer.Event{
  914. Kind: kind,
  915. Source: src,
  916. Buttons: btns,
  917. PointerID: pointer.ID(pointerID),
  918. Time: time.Duration(t) * time.Millisecond,
  919. Position: f32.Point{X: float32(x), Y: float32(y)},
  920. Scroll: f32.Pt(float32(scrollX), float32(scrollY)),
  921. })
  922. }
  923. //export Java_org_gioui_GioView_imeSelectionStart
  924. func Java_org_gioui_GioView_imeSelectionStart(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
  925. w := cgo.Handle(handle).Value().(*window)
  926. sel := w.callbacks.EditorState().Selection
  927. start := sel.Start
  928. if sel.End < sel.Start {
  929. start = sel.End
  930. }
  931. return C.jint(start)
  932. }
  933. //export Java_org_gioui_GioView_imeSelectionEnd
  934. func Java_org_gioui_GioView_imeSelectionEnd(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
  935. w := cgo.Handle(handle).Value().(*window)
  936. sel := w.callbacks.EditorState().Selection
  937. end := sel.End
  938. if sel.End < sel.Start {
  939. end = sel.Start
  940. }
  941. return C.jint(end)
  942. }
  943. //export Java_org_gioui_GioView_imeComposingStart
  944. func Java_org_gioui_GioView_imeComposingStart(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
  945. w := cgo.Handle(handle).Value().(*window)
  946. comp := w.callbacks.EditorState().compose
  947. start := comp.Start
  948. if e := comp.End; e < start {
  949. start = e
  950. }
  951. return C.jint(start)
  952. }
  953. //export Java_org_gioui_GioView_imeComposingEnd
  954. func Java_org_gioui_GioView_imeComposingEnd(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
  955. w := cgo.Handle(handle).Value().(*window)
  956. comp := w.callbacks.EditorState().compose
  957. end := comp.End
  958. if s := comp.Start; s > end {
  959. end = s
  960. }
  961. return C.jint(end)
  962. }
  963. //export Java_org_gioui_GioView_imeSnippet
  964. func Java_org_gioui_GioView_imeSnippet(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jstring {
  965. w := cgo.Handle(handle).Value().(*window)
  966. snip := w.callbacks.EditorState().Snippet.Text
  967. return javaString(env, snip)
  968. }
  969. //export Java_org_gioui_GioView_imeSnippetStart
  970. func Java_org_gioui_GioView_imeSnippetStart(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
  971. w := cgo.Handle(handle).Value().(*window)
  972. return C.jint(w.callbacks.EditorState().Snippet.Start)
  973. }
  974. //export Java_org_gioui_GioView_imeSetSnippet
  975. func Java_org_gioui_GioView_imeSetSnippet(env *C.JNIEnv, class C.jclass, handle C.jlong, start, end C.jint) {
  976. w := cgo.Handle(handle).Value().(*window)
  977. if start < 0 {
  978. start = 0
  979. }
  980. if end < start {
  981. end = start
  982. }
  983. r := key.Range{Start: int(start), End: int(end)}
  984. w.callbacks.SetEditorSnippet(r)
  985. }
  986. //export Java_org_gioui_GioView_imeSetSelection
  987. func Java_org_gioui_GioView_imeSetSelection(env *C.JNIEnv, class C.jclass, handle C.jlong, start, end C.jint) {
  988. w := cgo.Handle(handle).Value().(*window)
  989. r := key.Range{Start: int(start), End: int(end)}
  990. w.callbacks.SetEditorSelection(r)
  991. }
  992. //export Java_org_gioui_GioView_imeSetComposingRegion
  993. func Java_org_gioui_GioView_imeSetComposingRegion(env *C.JNIEnv, class C.jclass, handle C.jlong, start, end C.jint) {
  994. w := cgo.Handle(handle).Value().(*window)
  995. w.callbacks.SetComposingRegion(key.Range{
  996. Start: int(start),
  997. End: int(end),
  998. })
  999. }
  1000. //export Java_org_gioui_GioView_imeReplace
  1001. func Java_org_gioui_GioView_imeReplace(env *C.JNIEnv, class C.jclass, handle C.jlong, start, end C.jint, jtext C.jstring) {
  1002. w := cgo.Handle(handle).Value().(*window)
  1003. r := key.Range{Start: int(start), End: int(end)}
  1004. text := goString(env, jtext)
  1005. w.callbacks.EditorReplace(r, text)
  1006. }
  1007. //export Java_org_gioui_GioView_imeToRunes
  1008. func Java_org_gioui_GioView_imeToRunes(env *C.JNIEnv, class C.jclass, handle C.jlong, chars C.jint) C.jint {
  1009. w := cgo.Handle(handle).Value().(*window)
  1010. state := w.callbacks.EditorState()
  1011. return C.jint(state.RunesIndex(int(chars)))
  1012. }
  1013. //export Java_org_gioui_GioView_imeToUTF16
  1014. func Java_org_gioui_GioView_imeToUTF16(env *C.JNIEnv, class C.jclass, handle C.jlong, runes C.jint) C.jint {
  1015. w := cgo.Handle(handle).Value().(*window)
  1016. state := w.callbacks.EditorState()
  1017. return C.jint(state.UTF16Index(int(runes)))
  1018. }
  1019. func (w *window) EditorStateChanged(old, new editorState) {
  1020. runInJVM(javaVM(), func(env *C.JNIEnv) {
  1021. if old.Snippet != new.Snippet {
  1022. callVoidMethod(env, w.view, gioView.restartInput)
  1023. return
  1024. }
  1025. if old.Selection.Range != new.Selection.Range {
  1026. w.callbacks.SetComposingRegion(key.Range{Start: -1, End: -1})
  1027. callVoidMethod(env, w.view, gioView.updateSelection)
  1028. }
  1029. if old.Selection.Transform != new.Selection.Transform || old.Selection.Caret != new.Selection.Caret {
  1030. sel := new.Selection
  1031. m00, m01, m02, m10, m11, m12 := sel.Transform.Elems()
  1032. f := func(v float32) jvalue {
  1033. return jvalue(math.Float32bits(v))
  1034. }
  1035. c := sel.Caret
  1036. callVoidMethod(env, w.view, gioView.updateCaret, f(m00), f(m01), f(m02), f(m10), f(m11), f(m12), f(c.Pos.X), f(c.Pos.Y-c.Ascent), f(c.Pos.Y), f(c.Pos.Y+c.Descent))
  1037. }
  1038. })
  1039. }
  1040. func (w *window) ShowTextInput(show bool) {
  1041. runInJVM(javaVM(), func(env *C.JNIEnv) {
  1042. if show {
  1043. callVoidMethod(env, w.view, gioView.showTextInput)
  1044. } else {
  1045. callVoidMethod(env, w.view, gioView.hideTextInput)
  1046. }
  1047. })
  1048. }
  1049. func (w *window) SetInputHint(mode key.InputHint) {
  1050. w.inputHint = mode
  1051. // Constants defined at https://developer.android.com/reference/android/text/InputType.
  1052. const (
  1053. TYPE_NULL = 0
  1054. TYPE_CLASS_TEXT = 1
  1055. TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 32
  1056. TYPE_TEXT_VARIATION_URI = 16
  1057. TYPE_TEXT_VARIATION_PASSWORD = 128
  1058. TYPE_TEXT_FLAG_CAP_SENTENCES = 16384
  1059. TYPE_TEXT_FLAG_AUTO_CORRECT = 32768
  1060. TYPE_CLASS_NUMBER = 2
  1061. TYPE_NUMBER_FLAG_DECIMAL = 8192
  1062. TYPE_NUMBER_FLAG_SIGNED = 4096
  1063. TYPE_CLASS_PHONE = 3
  1064. )
  1065. runInJVM(javaVM(), func(env *C.JNIEnv) {
  1066. var m jvalue
  1067. switch mode {
  1068. case key.HintText:
  1069. m = TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_AUTO_CORRECT | TYPE_TEXT_FLAG_CAP_SENTENCES
  1070. case key.HintNumeric:
  1071. m = TYPE_CLASS_NUMBER | TYPE_NUMBER_FLAG_DECIMAL | TYPE_NUMBER_FLAG_SIGNED
  1072. case key.HintEmail:
  1073. m = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS
  1074. case key.HintURL:
  1075. m = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_URI
  1076. case key.HintTelephone:
  1077. m = TYPE_CLASS_PHONE
  1078. case key.HintPassword:
  1079. m = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD
  1080. default:
  1081. m = TYPE_CLASS_TEXT
  1082. }
  1083. callVoidMethod(env, w.view, gioView.setInputHint, m)
  1084. })
  1085. }
  1086. func javaBool(b bool) C.jboolean {
  1087. if b {
  1088. return C.JNI_TRUE
  1089. } else {
  1090. return C.JNI_FALSE
  1091. }
  1092. }
  1093. func javaString(env *C.JNIEnv, str string) C.jstring {
  1094. utf16Chars := utf16.Encode([]rune(str))
  1095. var ptr *C.jchar
  1096. if len(utf16Chars) > 0 {
  1097. ptr = (*C.jchar)(unsafe.Pointer(&utf16Chars[0]))
  1098. }
  1099. return C.jni_NewString(env, ptr, C.int(len(utf16Chars)))
  1100. }
  1101. func varArgs(args []jvalue) *C.jvalue {
  1102. if len(args) == 0 {
  1103. return nil
  1104. }
  1105. return (*C.jvalue)(unsafe.Pointer(&args[0]))
  1106. }
  1107. func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error {
  1108. C.jni_CallStaticVoidMethodA(env, cls, method, varArgs(args))
  1109. return exception(env)
  1110. }
  1111. func callStaticObjectMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) {
  1112. res := C.jni_CallStaticObjectMethodA(env, cls, method, varArgs(args))
  1113. return res, exception(env)
  1114. }
  1115. func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) error {
  1116. C.jni_CallVoidMethodA(env, obj, method, varArgs(args))
  1117. return exception(env)
  1118. }
  1119. func callBooleanMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (bool, error) {
  1120. res := C.jni_CallBooleanMethodA(env, obj, method, varArgs(args))
  1121. return res == C.JNI_TRUE, exception(env)
  1122. }
  1123. func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) {
  1124. res := C.jni_CallObjectMethodA(env, obj, method, varArgs(args))
  1125. return res, exception(env)
  1126. }
  1127. func newObject(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) {
  1128. res := C.jni_NewObjectA(env, cls, method, varArgs(args))
  1129. return res, exception(env)
  1130. }
  1131. // exception returns an error corresponding to the pending
  1132. // exception, or nil if no exception is pending. The pending
  1133. // exception is cleared.
  1134. func exception(env *C.JNIEnv) error {
  1135. thr := C.jni_ExceptionOccurred(env)
  1136. if thr == 0 {
  1137. return nil
  1138. }
  1139. C.jni_ExceptionClear(env)
  1140. cls := getObjectClass(env, C.jobject(thr))
  1141. toString := getMethodID(env, cls, "toString", "()Ljava/lang/String;")
  1142. msg, err := callObjectMethod(env, C.jobject(thr), toString)
  1143. if err != nil {
  1144. return err
  1145. }
  1146. return errors.New(goString(env, C.jstring(msg)))
  1147. }
  1148. func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass {
  1149. if obj == 0 {
  1150. panic("null object")
  1151. }
  1152. cls := C.jni_GetObjectClass(env, C.jobject(obj))
  1153. if err := exception(env); err != nil {
  1154. // GetObjectClass should never fail.
  1155. panic(err)
  1156. }
  1157. return cls
  1158. }
  1159. // goString converts the JVM jstring to a Go string.
  1160. func goString(env *C.JNIEnv, str C.jstring) string {
  1161. if str == 0 {
  1162. return ""
  1163. }
  1164. strlen := C.jni_GetStringLength(env, C.jstring(str))
  1165. chars := C.jni_GetStringChars(env, C.jstring(str))
  1166. utf16Chars := unsafe.Slice((*uint16)(unsafe.Pointer(chars)), strlen)
  1167. utf8 := utf16.Decode(utf16Chars)
  1168. return string(utf8)
  1169. }
  1170. func findClass(env *C.JNIEnv, name string) C.jclass {
  1171. cn := C.CString(name)
  1172. defer C.free(unsafe.Pointer(cn))
  1173. return C.jni_FindClass(env, cn)
  1174. }
  1175. func osMain() {
  1176. }
  1177. func newWindow(window *callbacks, options []Option) {
  1178. mainWindow.in <- windowAndConfig{window, options}
  1179. <-mainWindow.windows
  1180. }
  1181. func (w *window) WriteClipboard(mime string, s []byte) {
  1182. runInJVM(javaVM(), func(env *C.JNIEnv) {
  1183. jstr := javaString(env, string(s))
  1184. callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard,
  1185. jvalue(android.appCtx), jvalue(jstr))
  1186. })
  1187. }
  1188. func (w *window) ReadClipboard() {
  1189. runInJVM(javaVM(), func(env *C.JNIEnv) {
  1190. c, err := callStaticObjectMethod(env, android.gioCls, android.mreadClipboard,
  1191. jvalue(android.appCtx))
  1192. if err != nil {
  1193. return
  1194. }
  1195. content := goString(env, C.jstring(c))
  1196. w.processEvent(transfer.DataEvent{
  1197. Type: "application/text",
  1198. Open: func() io.ReadCloser {
  1199. return io.NopCloser(strings.NewReader(content))
  1200. },
  1201. })
  1202. })
  1203. }
  1204. func (w *window) Configure(options []Option) {
  1205. cnf := w.config
  1206. cnf.apply(unit.Metric{}, options)
  1207. runInJVM(javaVM(), func(env *C.JNIEnv) {
  1208. w.setConfig(env, cnf)
  1209. })
  1210. }
  1211. func (w *window) setConfig(env *C.JNIEnv, cnf Config) {
  1212. prev := w.config
  1213. // Decorations are never disabled.
  1214. cnf.Decorated = true
  1215. if prev.Orientation != cnf.Orientation {
  1216. w.config.Orientation = cnf.Orientation
  1217. setOrientation(env, w.view, cnf.Orientation)
  1218. }
  1219. if prev.NavigationColor != cnf.NavigationColor {
  1220. w.config.NavigationColor = cnf.NavigationColor
  1221. setNavigationColor(env, w.view, cnf.NavigationColor)
  1222. }
  1223. if prev.StatusColor != cnf.StatusColor {
  1224. w.config.StatusColor = cnf.StatusColor
  1225. setStatusColor(env, w.view, cnf.StatusColor)
  1226. }
  1227. if prev.Mode != cnf.Mode {
  1228. switch cnf.Mode {
  1229. case Fullscreen:
  1230. callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_TRUE)
  1231. w.config.Mode = Fullscreen
  1232. case Windowed:
  1233. callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_FALSE)
  1234. w.config.Mode = Windowed
  1235. }
  1236. }
  1237. if cnf.Decorated != prev.Decorated {
  1238. w.config.Decorated = cnf.Decorated
  1239. }
  1240. w.processEvent(ConfigEvent{Config: w.config})
  1241. }
  1242. func (w *window) Perform(system.Action) {}
  1243. func (w *window) SetCursor(cursor pointer.Cursor) {
  1244. runInJVM(javaVM(), func(env *C.JNIEnv) {
  1245. setCursor(env, w.view, cursor)
  1246. })
  1247. }
  1248. func (w *window) wakeup() {
  1249. runOnMain(func(env *C.JNIEnv) {
  1250. w.loop.Wakeup()
  1251. w.loop.FlushEvents()
  1252. })
  1253. }
  1254. var androidCursor = [...]uint16{
  1255. pointer.CursorDefault: 1000, // TYPE_ARROW
  1256. pointer.CursorNone: 0,
  1257. pointer.CursorText: 1008, // TYPE_TEXT
  1258. pointer.CursorVerticalText: 1009, // TYPE_VERTICAL_TEXT
  1259. pointer.CursorPointer: 1002, // TYPE_HAND
  1260. pointer.CursorCrosshair: 1007, // TYPE_CROSSHAIR
  1261. pointer.CursorAllScroll: 1013, // TYPE_ALL_SCROLL
  1262. pointer.CursorColResize: 1014, // TYPE_HORIZONTAL_DOUBLE_ARROW
  1263. pointer.CursorRowResize: 1015, // TYPE_VERTICAL_DOUBLE_ARROW
  1264. pointer.CursorGrab: 1020, // TYPE_GRAB
  1265. pointer.CursorGrabbing: 1021, // TYPE_GRABBING
  1266. pointer.CursorNotAllowed: 1012, // TYPE_NO_DROP
  1267. pointer.CursorWait: 1004, // TYPE_WAIT
  1268. pointer.CursorProgress: 1000, // TYPE_ARROW
  1269. pointer.CursorNorthWestResize: 1017, // TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW
  1270. pointer.CursorNorthEastResize: 1016, // TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW
  1271. pointer.CursorSouthWestResize: 1016, // TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW
  1272. pointer.CursorSouthEastResize: 1017, // TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW
  1273. pointer.CursorNorthSouthResize: 1015, // TYPE_VERTICAL_DOUBLE_ARROW
  1274. pointer.CursorEastWestResize: 1014, // TYPE_HORIZONTAL_DOUBLE_ARROW
  1275. pointer.CursorWestResize: 1014, // TYPE_HORIZONTAL_DOUBLE_ARROW
  1276. pointer.CursorEastResize: 1014, // TYPE_HORIZONTAL_DOUBLE_ARROW
  1277. pointer.CursorNorthResize: 1015, // TYPE_VERTICAL_DOUBLE_ARROW
  1278. pointer.CursorSouthResize: 1015, // TYPE_VERTICAL_DOUBLE_ARROW
  1279. pointer.CursorNorthEastSouthWestResize: 1016, // TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW
  1280. pointer.CursorNorthWestSouthEastResize: 1017, // TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW
  1281. }
  1282. func setCursor(env *C.JNIEnv, view C.jobject, cursor pointer.Cursor) {
  1283. curID := androidCursor[cursor]
  1284. callVoidMethod(env, view, gioView.setCursor, jvalue(curID))
  1285. }
  1286. func setOrientation(env *C.JNIEnv, view C.jobject, mode Orientation) {
  1287. var (
  1288. id int
  1289. idFallback int // Used only for SDK 17 or older.
  1290. )
  1291. // Constants defined at https://developer.android.com/reference/android/content/pm/ActivityInfo.
  1292. switch mode {
  1293. case AnyOrientation:
  1294. id, idFallback = 2, 2 // SCREEN_ORIENTATION_USER
  1295. case LandscapeOrientation:
  1296. id, idFallback = 11, 0 // SCREEN_ORIENTATION_USER_LANDSCAPE (or SCREEN_ORIENTATION_LANDSCAPE)
  1297. case PortraitOrientation:
  1298. id, idFallback = 12, 1 // SCREEN_ORIENTATION_USER_PORTRAIT (or SCREEN_ORIENTATION_PORTRAIT)
  1299. }
  1300. callVoidMethod(env, view, gioView.setOrientation, jvalue(id), jvalue(idFallback))
  1301. }
  1302. func setStatusColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) {
  1303. callVoidMethod(env, view, gioView.setStatusColor,
  1304. jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)),
  1305. jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)),
  1306. )
  1307. }
  1308. func setNavigationColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) {
  1309. callVoidMethod(env, view, gioView.setNavigationColor,
  1310. jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)),
  1311. jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)),
  1312. )
  1313. }
  1314. // runOnMain runs a function on the Java main thread.
  1315. func runOnMain(f func(env *C.JNIEnv)) {
  1316. go func() {
  1317. mainFuncs <- f
  1318. runInJVM(javaVM(), func(env *C.JNIEnv) {
  1319. callStaticVoidMethod(env, android.gioCls, android.mwakeupMainThread)
  1320. })
  1321. }()
  1322. }
  1323. //export Java_org_gioui_Gio_scheduleMainFuncs
  1324. func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) {
  1325. for {
  1326. select {
  1327. case f := <-mainFuncs:
  1328. f(env)
  1329. default:
  1330. return
  1331. }
  1332. }
  1333. }
  1334. func (AndroidViewEvent) implementsViewEvent() {}
  1335. func (AndroidViewEvent) ImplementsEvent() {}
  1336. func (a AndroidViewEvent) Valid() bool {
  1337. return a != (AndroidViewEvent{})
  1338. }