android.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build android
  5. // +build android
  6. /*
  7. Android Apps are built with -buildmode=c-shared. They are loaded by a
  8. running Java process.
  9. Before any entry point is reached, a global constructor initializes the
  10. Go runtime, calling all Go init functions. All cgo calls will block
  11. until this is complete. Next JNI_OnLoad is called. When that is
  12. complete, one of two entry points is called.
  13. All-Go apps built using NativeActivity enter at ANativeActivity_onCreate.
  14. Go libraries (for example, those built with gomobile bind) do not use
  15. the app package initialization.
  16. */
  17. package app
  18. /*
  19. #cgo LDFLAGS: -landroid -llog -lEGL -lGLESv2
  20. #include <android/configuration.h>
  21. #include <android/input.h>
  22. #include <android/keycodes.h>
  23. #include <android/looper.h>
  24. #include <android/native_activity.h>
  25. #include <android/native_window.h>
  26. #include <EGL/egl.h>
  27. #include <jni.h>
  28. #include <pthread.h>
  29. #include <stdlib.h>
  30. #include <stdbool.h>
  31. extern EGLDisplay display;
  32. extern EGLSurface surface;
  33. char* createEGLSurface(ANativeWindow* window);
  34. char* destroyEGLSurface();
  35. int32_t getKeyRune(JNIEnv* env, AInputEvent* e);
  36. void showKeyboard(JNIEnv* env, int keyboardType);
  37. void hideKeyboard(JNIEnv* env);
  38. void showFileOpen(JNIEnv* env, char* mimes);
  39. void showFileSave(JNIEnv* env, char* mimes, char* filename);
  40. void finish(JNIEnv* env, jobject ctx);
  41. void Java_org_golang_app_GoNativeActivity_filePickerReturned(JNIEnv *env, jclass clazz, jstring str);
  42. */
  43. import "C"
  44. import (
  45. "fmt"
  46. "log"
  47. "mime"
  48. "os"
  49. "runtime"
  50. "runtime/debug"
  51. "strings"
  52. "time"
  53. "unsafe"
  54. "fyne.io/fyne/v2/internal/driver/mobile/app/callfn"
  55. "fyne.io/fyne/v2/internal/driver/mobile/event/key"
  56. "fyne.io/fyne/v2/internal/driver/mobile/event/lifecycle"
  57. "fyne.io/fyne/v2/internal/driver/mobile/event/paint"
  58. "fyne.io/fyne/v2/internal/driver/mobile/event/size"
  59. "fyne.io/fyne/v2/internal/driver/mobile/event/touch"
  60. "fyne.io/fyne/v2/internal/driver/mobile/mobileinit"
  61. )
  62. // mimeMap contains standard mime entries that are missing on Android
  63. var mimeMap = map[string]string{
  64. ".txt": "text/plain",
  65. }
  66. // GoBack asks the OS to go to the previous app / activity
  67. func GoBack() {
  68. err := RunOnJVM(func(_, jniEnv, ctx uintptr) error {
  69. env := (*C.JNIEnv)(unsafe.Pointer(jniEnv))
  70. C.finish(env, C.jobject(ctx))
  71. return nil
  72. })
  73. if err != nil {
  74. log.Fatalf("app: %v", err)
  75. }
  76. }
  77. // RunOnJVM runs fn on a new goroutine locked to an OS thread with a JNIEnv.
  78. //
  79. // RunOnJVM blocks until the call to fn is complete. Any Java
  80. // exception or failure to attach to the JVM is returned as an error.
  81. //
  82. // The function fn takes vm, the current JavaVM*,
  83. // env, the current JNIEnv*, and
  84. // ctx, a jobject representing the global android.context.Context.
  85. func RunOnJVM(fn func(vm, jniEnv, ctx uintptr) error) error {
  86. return mobileinit.RunOnJVM(fn)
  87. }
  88. //export setCurrentContext
  89. func setCurrentContext(vm *C.JavaVM, ctx C.jobject) {
  90. mobileinit.SetCurrentContext(unsafe.Pointer(vm), uintptr(ctx))
  91. }
  92. //export callMain
  93. func callMain(mainPC uintptr) {
  94. for _, name := range []string{"FILESDIR", "TMPDIR", "PATH", "LD_LIBRARY_PATH"} {
  95. n := C.CString(name)
  96. os.Setenv(name, C.GoString(C.getenv(n)))
  97. C.free(unsafe.Pointer(n))
  98. }
  99. // Set timezone.
  100. //
  101. // Note that Android zoneinfo is stored in /system/usr/share/zoneinfo,
  102. // but it is in some kind of packed TZiff file that we do not support
  103. // yet. As a stopgap, we build a fixed zone using the tm_zone name.
  104. var curtime C.time_t
  105. var curtm C.struct_tm
  106. C.time(&curtime)
  107. C.localtime_r(&curtime, &curtm)
  108. tzOffset := int(curtm.tm_gmtoff)
  109. tz := C.GoString(curtm.tm_zone)
  110. time.Local = time.FixedZone(tz, tzOffset)
  111. go callfn.CallFn(mainPC)
  112. }
  113. //export onStart
  114. func onStart(activity *C.ANativeActivity) {
  115. }
  116. //export onResume
  117. func onResume(activity *C.ANativeActivity) {
  118. }
  119. //export onSaveInstanceState
  120. func onSaveInstanceState(activity *C.ANativeActivity, outSize *C.size_t) unsafe.Pointer {
  121. return nil
  122. }
  123. //export onPause
  124. func onPause(activity *C.ANativeActivity) {
  125. }
  126. //export onStop
  127. func onStop(activity *C.ANativeActivity) {
  128. }
  129. //export onBackPressed
  130. func onBackPressed() {
  131. k := key.Event{
  132. Code: key.CodeBackButton,
  133. Direction: key.DirPress,
  134. }
  135. log.Println("Logging key event back")
  136. theApp.events.In() <- k
  137. k.Direction = key.DirRelease
  138. theApp.events.In() <- k
  139. }
  140. //export onCreate
  141. func onCreate(activity *C.ANativeActivity) {
  142. // Set the initial configuration.
  143. //
  144. // Note we use unbuffered channels to talk to the activity loop, and
  145. // NativeActivity calls these callbacks sequentially, so configuration
  146. // will be set before <-windowRedrawNeeded is processed.
  147. windowConfigChange <- windowConfigRead(activity)
  148. }
  149. //export onDestroy
  150. func onDestroy(activity *C.ANativeActivity) {
  151. activityDestroyed <- struct{}{}
  152. }
  153. //export onWindowFocusChanged
  154. func onWindowFocusChanged(activity *C.ANativeActivity, hasFocus C.int) {
  155. }
  156. //export onNativeWindowCreated
  157. func onNativeWindowCreated(activity *C.ANativeActivity, window *C.ANativeWindow) {
  158. }
  159. //export onNativeWindowRedrawNeeded
  160. func onNativeWindowRedrawNeeded(activity *C.ANativeActivity, window *C.ANativeWindow) {
  161. // Called on orientation change and window resize.
  162. // Send a request for redraw, and block this function
  163. // until a complete draw and buffer swap is completed.
  164. // This is required by the redraw documentation to
  165. // avoid bad draws.
  166. windowRedrawNeeded <- window
  167. <-windowRedrawDone
  168. }
  169. //export onNativeWindowDestroyed
  170. func onNativeWindowDestroyed(activity *C.ANativeActivity, window *C.ANativeWindow) {
  171. windowDestroyed <- window
  172. }
  173. //export onInputQueueCreated
  174. func onInputQueueCreated(activity *C.ANativeActivity, q *C.AInputQueue) {
  175. inputQueue <- q
  176. <-inputQueueDone
  177. }
  178. //export onInputQueueDestroyed
  179. func onInputQueueDestroyed(activity *C.ANativeActivity, q *C.AInputQueue) {
  180. inputQueue <- nil
  181. <-inputQueueDone
  182. }
  183. //export onContentRectChanged
  184. func onContentRectChanged(activity *C.ANativeActivity, rect *C.ARect) {
  185. }
  186. //export setDarkMode
  187. func setDarkMode(dark C.bool) {
  188. darkMode = bool(dark)
  189. }
  190. type windowConfig struct {
  191. orientation size.Orientation
  192. pixelsPerPt float32
  193. }
  194. func windowConfigRead(activity *C.ANativeActivity) windowConfig {
  195. aconfig := C.AConfiguration_new()
  196. C.AConfiguration_fromAssetManager(aconfig, activity.assetManager)
  197. orient := C.AConfiguration_getOrientation(aconfig)
  198. density := C.AConfiguration_getDensity(aconfig)
  199. C.AConfiguration_delete(aconfig)
  200. // Calculate the screen resolution. This value is approximate. For example,
  201. // a physical resolution of 200 DPI may be quantized to one of the
  202. // ACONFIGURATION_DENSITY_XXX values such as 160 or 240.
  203. //
  204. // A more accurate DPI could possibly be calculated from
  205. // https://developer.android.com/reference/android/util/DisplayMetrics.html#xdpi
  206. // but this does not appear to be accessible via the NDK. In any case, the
  207. // hardware might not even provide a more accurate number, as the system
  208. // does not apparently use the reported value. See golang.org/issue/13366
  209. // for a discussion.
  210. var dpi int
  211. switch density {
  212. case C.ACONFIGURATION_DENSITY_DEFAULT:
  213. dpi = 160
  214. case C.ACONFIGURATION_DENSITY_LOW,
  215. C.ACONFIGURATION_DENSITY_MEDIUM,
  216. 213, // C.ACONFIGURATION_DENSITY_TV
  217. C.ACONFIGURATION_DENSITY_HIGH,
  218. 320, // ACONFIGURATION_DENSITY_XHIGH
  219. 480, // ACONFIGURATION_DENSITY_XXHIGH
  220. 640: // ACONFIGURATION_DENSITY_XXXHIGH
  221. dpi = int(density)
  222. case C.ACONFIGURATION_DENSITY_NONE:
  223. log.Print("android device reports no screen density")
  224. dpi = 72
  225. default:
  226. log.Printf("android device reports unknown density: %d", density)
  227. // All we can do is guess.
  228. if density > 0 {
  229. dpi = int(density)
  230. } else {
  231. dpi = 72
  232. }
  233. }
  234. o := size.OrientationUnknown
  235. switch orient {
  236. case C.ACONFIGURATION_ORIENTATION_PORT:
  237. o = size.OrientationPortrait
  238. case C.ACONFIGURATION_ORIENTATION_LAND:
  239. o = size.OrientationLandscape
  240. }
  241. return windowConfig{
  242. orientation: o,
  243. pixelsPerPt: float32(dpi) / 72,
  244. }
  245. }
  246. //export onConfigurationChanged
  247. func onConfigurationChanged(activity *C.ANativeActivity) {
  248. // A rotation event first triggers onConfigurationChanged, then
  249. // calls onNativeWindowRedrawNeeded. We extract the orientation
  250. // here and save it for the redraw event.
  251. windowConfigChange <- windowConfigRead(activity)
  252. }
  253. //export onLowMemory
  254. func onLowMemory(activity *C.ANativeActivity) {
  255. runtime.GC()
  256. debug.FreeOSMemory()
  257. }
  258. var (
  259. inputQueue = make(chan *C.AInputQueue)
  260. inputQueueDone = make(chan struct{})
  261. windowDestroyed = make(chan *C.ANativeWindow)
  262. windowRedrawNeeded = make(chan *C.ANativeWindow)
  263. windowRedrawDone = make(chan struct{})
  264. windowConfigChange = make(chan windowConfig)
  265. activityDestroyed = make(chan struct{})
  266. screenInsetTop, screenInsetBottom, screenInsetLeft, screenInsetRight int
  267. darkMode bool
  268. )
  269. func init() {
  270. theApp.registerGLViewportFilter()
  271. }
  272. func main(f func(App)) {
  273. mainUserFn = f
  274. // TODO: merge the runInputQueue and mainUI functions?
  275. go func() {
  276. if err := mobileinit.RunOnJVM(runInputQueue); err != nil {
  277. log.Fatalf("app: %v", err)
  278. }
  279. }()
  280. // Preserve this OS thread for:
  281. // 1. the attached JNI thread
  282. // 2. the GL context
  283. if err := mobileinit.RunOnJVM(mainUI); err != nil {
  284. log.Fatalf("app: %v", err)
  285. }
  286. }
  287. // driverShowVirtualKeyboard requests the driver to show a virtual keyboard for text input
  288. func driverShowVirtualKeyboard(keyboard KeyboardType) {
  289. err := mobileinit.RunOnJVM(func(vm, jniEnv, ctx uintptr) error {
  290. env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer
  291. C.showKeyboard(env, C.int(int32(keyboard)))
  292. return nil
  293. })
  294. if err != nil {
  295. log.Fatalf("app: %v", err)
  296. }
  297. }
  298. // driverHideVirtualKeyboard requests the driver to hide any visible virtual keyboard
  299. func driverHideVirtualKeyboard() {
  300. if err := mobileinit.RunOnJVM(hideSoftInput); err != nil {
  301. log.Fatalf("app: %v", err)
  302. }
  303. }
  304. func hideSoftInput(vm, jniEnv, ctx uintptr) error {
  305. env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer
  306. C.hideKeyboard(env)
  307. return nil
  308. }
  309. var fileCallback func(string, func())
  310. //export filePickerReturned
  311. func filePickerReturned(str *C.char) {
  312. if fileCallback == nil {
  313. return
  314. }
  315. fileCallback(C.GoString(str), nil)
  316. fileCallback = nil
  317. }
  318. //export insetsChanged
  319. func insetsChanged(top, bottom, left, right int) {
  320. screenInsetTop, screenInsetBottom, screenInsetLeft, screenInsetRight = top, bottom, left, right
  321. }
  322. func mimeStringFromFilter(filter *FileFilter) string {
  323. mimes := "*/*"
  324. if filter.MimeTypes != nil {
  325. mimes = strings.Join(filter.MimeTypes, "|")
  326. } else if filter.Extensions != nil {
  327. var mimeTypes []string
  328. for _, ext := range filter.Extensions {
  329. if mimeEntry, ok := mimeMap[ext]; ok {
  330. mimeTypes = append(mimeTypes, mimeEntry)
  331. continue
  332. }
  333. mimeType := mime.TypeByExtension(ext)
  334. if mimeType == "" {
  335. log.Println("Could not find mime for extension " + ext + ", allowing all")
  336. return "*/*" // could not find one, so allow all
  337. }
  338. mimeTypes = append(mimeTypes, mimeType)
  339. }
  340. mimes = strings.Join(mimeTypes, "|")
  341. }
  342. return mimes
  343. }
  344. func driverShowFileOpenPicker(callback func(string, func()), filter *FileFilter) {
  345. fileCallback = callback
  346. mimes := mimeStringFromFilter(filter)
  347. mimeStr := C.CString(mimes)
  348. defer C.free(unsafe.Pointer(mimeStr))
  349. open := func(vm, jniEnv, ctx uintptr) error {
  350. // TODO pass in filter...
  351. env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer
  352. C.showFileOpen(env, mimeStr)
  353. return nil
  354. }
  355. if err := mobileinit.RunOnJVM(open); err != nil {
  356. log.Fatalf("app: %v", err)
  357. }
  358. }
  359. func driverShowFileSavePicker(callback func(string, func()), filter *FileFilter, filename string) {
  360. fileCallback = callback
  361. mimes := mimeStringFromFilter(filter)
  362. mimeStr := C.CString(mimes)
  363. defer C.free(unsafe.Pointer(mimeStr))
  364. filenameStr := C.CString(filename)
  365. defer C.free(unsafe.Pointer(filenameStr))
  366. save := func(vm, jniEnv, ctx uintptr) error {
  367. env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer
  368. C.showFileSave(env, mimeStr, filenameStr)
  369. return nil
  370. }
  371. if err := mobileinit.RunOnJVM(save); err != nil {
  372. log.Fatalf("app: %v", err)
  373. }
  374. }
  375. var mainUserFn func(App)
  376. var DisplayMetrics struct {
  377. WidthPx int
  378. HeightPx int
  379. }
  380. func mainUI(vm, jniEnv, ctx uintptr) error {
  381. workAvailable := theApp.worker.WorkAvailable()
  382. donec := make(chan struct{})
  383. go func() {
  384. mainUserFn(theApp)
  385. close(donec)
  386. }()
  387. var pixelsPerPt float32
  388. for {
  389. select {
  390. case <-donec:
  391. return nil
  392. case cfg := <-windowConfigChange:
  393. pixelsPerPt = cfg.pixelsPerPt
  394. case w := <-windowRedrawNeeded:
  395. if C.surface == nil {
  396. if errStr := C.createEGLSurface(w); errStr != nil {
  397. return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
  398. }
  399. DisplayMetrics.WidthPx = int(C.ANativeWindow_getWidth(w))
  400. DisplayMetrics.HeightPx = int(C.ANativeWindow_getHeight(w))
  401. }
  402. theApp.sendLifecycle(lifecycle.StageFocused)
  403. widthPx := int(C.ANativeWindow_getWidth(w))
  404. heightPx := int(C.ANativeWindow_getHeight(w))
  405. theApp.events.In() <- size.Event{
  406. WidthPx: widthPx,
  407. HeightPx: heightPx,
  408. WidthPt: float32(widthPx) / pixelsPerPt,
  409. HeightPt: float32(heightPx) / pixelsPerPt,
  410. InsetTopPx: screenInsetTop,
  411. InsetBottomPx: screenInsetBottom,
  412. InsetLeftPx: screenInsetLeft,
  413. InsetRightPx: screenInsetRight,
  414. PixelsPerPt: pixelsPerPt,
  415. Orientation: screenOrientation(widthPx, heightPx), // we are guessing orientation here as it was not always working
  416. DarkMode: darkMode,
  417. }
  418. theApp.events.In() <- paint.Event{External: true}
  419. case <-windowDestroyed:
  420. if C.surface != nil {
  421. if errStr := C.destroyEGLSurface(); errStr != nil {
  422. return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
  423. }
  424. }
  425. C.surface = nil
  426. theApp.sendLifecycle(lifecycle.StageAlive)
  427. case <-activityDestroyed:
  428. theApp.sendLifecycle(lifecycle.StageDead)
  429. case <-workAvailable:
  430. theApp.worker.DoWork()
  431. case <-theApp.publish:
  432. // TODO: compare a generation number to redrawGen for stale paints?
  433. if C.surface != nil {
  434. // eglSwapBuffers blocks until vsync.
  435. if C.eglSwapBuffers(C.display, C.surface) == C.EGL_FALSE {
  436. log.Printf("app: failed to swap buffers (%s)", eglGetError())
  437. }
  438. }
  439. select {
  440. case windowRedrawDone <- struct{}{}:
  441. default:
  442. }
  443. theApp.publishResult <- PublishResult{}
  444. }
  445. }
  446. }
  447. func runInputQueue(vm, jniEnv, ctx uintptr) error {
  448. env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer
  449. // Android loopers select on OS file descriptors, not Go channels, so we
  450. // translate the inputQueue channel to an ALooper_wake call.
  451. l := C.ALooper_prepare(C.ALOOPER_PREPARE_ALLOW_NON_CALLBACKS)
  452. pending := make(chan *C.AInputQueue, 1)
  453. go func() {
  454. for q := range inputQueue {
  455. pending <- q
  456. C.ALooper_wake(l)
  457. }
  458. }()
  459. var q *C.AInputQueue
  460. for {
  461. if C.ALooper_pollAll(-1, nil, nil, nil) == C.ALOOPER_POLL_WAKE {
  462. select {
  463. default:
  464. case p := <-pending:
  465. if q != nil {
  466. processEvents(env, q)
  467. C.AInputQueue_detachLooper(q)
  468. }
  469. q = p
  470. if q != nil {
  471. C.AInputQueue_attachLooper(q, l, 0, nil, nil)
  472. }
  473. inputQueueDone <- struct{}{}
  474. }
  475. }
  476. if q != nil {
  477. processEvents(env, q)
  478. }
  479. }
  480. }
  481. func processEvents(env *C.JNIEnv, q *C.AInputQueue) {
  482. var e *C.AInputEvent
  483. for C.AInputQueue_getEvent(q, &e) >= 0 {
  484. if C.AInputQueue_preDispatchEvent(q, e) != 0 {
  485. continue
  486. }
  487. processEvent(env, e)
  488. C.AInputQueue_finishEvent(q, e, 0)
  489. }
  490. }
  491. func processEvent(env *C.JNIEnv, e *C.AInputEvent) {
  492. switch C.AInputEvent_getType(e) {
  493. case C.AINPUT_EVENT_TYPE_KEY:
  494. processKey(env, e)
  495. case C.AINPUT_EVENT_TYPE_MOTION:
  496. // At most one of the events in this batch is an up or down event; get its index and change.
  497. upDownIndex := C.size_t(C.AMotionEvent_getAction(e)&C.AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> C.AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT
  498. upDownType := touch.TypeMove
  499. switch C.AMotionEvent_getAction(e) & C.AMOTION_EVENT_ACTION_MASK {
  500. case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN:
  501. upDownType = touch.TypeBegin
  502. case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP:
  503. upDownType = touch.TypeEnd
  504. }
  505. for i, n := C.size_t(0), C.AMotionEvent_getPointerCount(e); i < n; i++ {
  506. t := touch.TypeMove
  507. if i == upDownIndex {
  508. t = upDownType
  509. }
  510. theApp.events.In() <- touch.Event{
  511. X: float32(C.AMotionEvent_getX(e, i)),
  512. Y: float32(C.AMotionEvent_getY(e, i)),
  513. Sequence: touch.Sequence(C.AMotionEvent_getPointerId(e, i)),
  514. Type: t,
  515. }
  516. }
  517. default:
  518. log.Printf("unknown input event, type=%d", C.AInputEvent_getType(e))
  519. }
  520. }
  521. func processKey(env *C.JNIEnv, e *C.AInputEvent) {
  522. deviceID := C.AInputEvent_getDeviceId(e)
  523. if deviceID == 0 {
  524. // Software keyboard input, leaving for scribe/IME.
  525. return
  526. }
  527. k := key.Event{
  528. Rune: rune(C.getKeyRune(env, e)),
  529. Code: convAndroidKeyCode(int32(C.AKeyEvent_getKeyCode(e))),
  530. }
  531. if k.Rune >= '0' && k.Rune <= '9' { // GBoard generates key events for numbers, but we see them in textChanged
  532. return
  533. }
  534. switch C.AKeyEvent_getAction(e) {
  535. case C.AKEY_STATE_DOWN:
  536. k.Direction = key.DirPress
  537. case C.AKEY_STATE_UP:
  538. k.Direction = key.DirRelease
  539. default:
  540. k.Direction = key.DirNone
  541. }
  542. // TODO(crawshaw): set Modifiers.
  543. theApp.events.In() <- k
  544. }
  545. func eglGetError() string {
  546. switch errNum := C.eglGetError(); errNum {
  547. case C.EGL_SUCCESS:
  548. return "EGL_SUCCESS"
  549. case C.EGL_NOT_INITIALIZED:
  550. return "EGL_NOT_INITIALIZED"
  551. case C.EGL_BAD_ACCESS:
  552. return "EGL_BAD_ACCESS"
  553. case C.EGL_BAD_ALLOC:
  554. return "EGL_BAD_ALLOC"
  555. case C.EGL_BAD_ATTRIBUTE:
  556. return "EGL_BAD_ATTRIBUTE"
  557. case C.EGL_BAD_CONTEXT:
  558. return "EGL_BAD_CONTEXT"
  559. case C.EGL_BAD_CONFIG:
  560. return "EGL_BAD_CONFIG"
  561. case C.EGL_BAD_CURRENT_SURFACE:
  562. return "EGL_BAD_CURRENT_SURFACE"
  563. case C.EGL_BAD_DISPLAY:
  564. return "EGL_BAD_DISPLAY"
  565. case C.EGL_BAD_SURFACE:
  566. return "EGL_BAD_SURFACE"
  567. case C.EGL_BAD_MATCH:
  568. return "EGL_BAD_MATCH"
  569. case C.EGL_BAD_PARAMETER:
  570. return "EGL_BAD_PARAMETER"
  571. case C.EGL_BAD_NATIVE_PIXMAP:
  572. return "EGL_BAD_NATIVE_PIXMAP"
  573. case C.EGL_BAD_NATIVE_WINDOW:
  574. return "EGL_BAD_NATIVE_WINDOW"
  575. case C.EGL_CONTEXT_LOST:
  576. return "EGL_CONTEXT_LOST"
  577. default:
  578. return fmt.Sprintf("Unknown EGL err: %d", errNum)
  579. }
  580. }
  581. var androidKeycoe = map[int32]key.Code{
  582. C.AKEYCODE_HOME: key.CodeHome,
  583. C.AKEYCODE_0: key.Code0,
  584. C.AKEYCODE_1: key.Code1,
  585. C.AKEYCODE_2: key.Code2,
  586. C.AKEYCODE_3: key.Code3,
  587. C.AKEYCODE_4: key.Code4,
  588. C.AKEYCODE_5: key.Code5,
  589. C.AKEYCODE_6: key.Code6,
  590. C.AKEYCODE_7: key.Code7,
  591. C.AKEYCODE_8: key.Code8,
  592. C.AKEYCODE_9: key.Code9,
  593. C.AKEYCODE_VOLUME_UP: key.CodeVolumeUp,
  594. C.AKEYCODE_VOLUME_DOWN: key.CodeVolumeDown,
  595. C.AKEYCODE_A: key.CodeA,
  596. C.AKEYCODE_B: key.CodeB,
  597. C.AKEYCODE_C: key.CodeC,
  598. C.AKEYCODE_D: key.CodeD,
  599. C.AKEYCODE_E: key.CodeE,
  600. C.AKEYCODE_F: key.CodeF,
  601. C.AKEYCODE_G: key.CodeG,
  602. C.AKEYCODE_H: key.CodeH,
  603. C.AKEYCODE_I: key.CodeI,
  604. C.AKEYCODE_J: key.CodeJ,
  605. C.AKEYCODE_K: key.CodeK,
  606. C.AKEYCODE_L: key.CodeL,
  607. C.AKEYCODE_M: key.CodeM,
  608. C.AKEYCODE_N: key.CodeN,
  609. C.AKEYCODE_O: key.CodeO,
  610. C.AKEYCODE_P: key.CodeP,
  611. C.AKEYCODE_Q: key.CodeQ,
  612. C.AKEYCODE_R: key.CodeR,
  613. C.AKEYCODE_S: key.CodeS,
  614. C.AKEYCODE_T: key.CodeT,
  615. C.AKEYCODE_U: key.CodeU,
  616. C.AKEYCODE_V: key.CodeV,
  617. C.AKEYCODE_W: key.CodeW,
  618. C.AKEYCODE_X: key.CodeX,
  619. C.AKEYCODE_Y: key.CodeY,
  620. C.AKEYCODE_Z: key.CodeZ,
  621. C.AKEYCODE_COMMA: key.CodeComma,
  622. C.AKEYCODE_PERIOD: key.CodeFullStop,
  623. C.AKEYCODE_ALT_LEFT: key.CodeLeftAlt,
  624. C.AKEYCODE_ALT_RIGHT: key.CodeRightAlt,
  625. C.AKEYCODE_SHIFT_LEFT: key.CodeLeftShift,
  626. C.AKEYCODE_SHIFT_RIGHT: key.CodeRightShift,
  627. C.AKEYCODE_TAB: key.CodeTab,
  628. C.AKEYCODE_SPACE: key.CodeSpacebar,
  629. C.AKEYCODE_ENTER: key.CodeReturnEnter,
  630. C.AKEYCODE_DEL: key.CodeDeleteBackspace,
  631. C.AKEYCODE_GRAVE: key.CodeGraveAccent,
  632. C.AKEYCODE_MINUS: key.CodeHyphenMinus,
  633. C.AKEYCODE_EQUALS: key.CodeEqualSign,
  634. C.AKEYCODE_LEFT_BRACKET: key.CodeLeftSquareBracket,
  635. C.AKEYCODE_RIGHT_BRACKET: key.CodeRightSquareBracket,
  636. C.AKEYCODE_BACKSLASH: key.CodeBackslash,
  637. C.AKEYCODE_SEMICOLON: key.CodeSemicolon,
  638. C.AKEYCODE_APOSTROPHE: key.CodeApostrophe,
  639. C.AKEYCODE_SLASH: key.CodeSlash,
  640. C.AKEYCODE_PAGE_UP: key.CodePageUp,
  641. C.AKEYCODE_PAGE_DOWN: key.CodePageDown,
  642. C.AKEYCODE_ESCAPE: key.CodeEscape,
  643. C.AKEYCODE_FORWARD_DEL: key.CodeDeleteForward,
  644. C.AKEYCODE_CTRL_LEFT: key.CodeLeftControl,
  645. C.AKEYCODE_CTRL_RIGHT: key.CodeRightControl,
  646. C.AKEYCODE_CAPS_LOCK: key.CodeCapsLock,
  647. C.AKEYCODE_META_LEFT: key.CodeLeftGUI,
  648. C.AKEYCODE_META_RIGHT: key.CodeRightGUI,
  649. C.AKEYCODE_INSERT: key.CodeInsert,
  650. C.AKEYCODE_F1: key.CodeF1,
  651. C.AKEYCODE_F2: key.CodeF2,
  652. C.AKEYCODE_F3: key.CodeF3,
  653. C.AKEYCODE_F4: key.CodeF4,
  654. C.AKEYCODE_F5: key.CodeF5,
  655. C.AKEYCODE_F6: key.CodeF6,
  656. C.AKEYCODE_F7: key.CodeF7,
  657. C.AKEYCODE_F8: key.CodeF8,
  658. C.AKEYCODE_F9: key.CodeF9,
  659. C.AKEYCODE_F10: key.CodeF10,
  660. C.AKEYCODE_F11: key.CodeF11,
  661. C.AKEYCODE_F12: key.CodeF12,
  662. C.AKEYCODE_NUM_LOCK: key.CodeKeypadNumLock,
  663. C.AKEYCODE_NUMPAD_0: key.CodeKeypad0,
  664. C.AKEYCODE_NUMPAD_1: key.CodeKeypad1,
  665. C.AKEYCODE_NUMPAD_2: key.CodeKeypad2,
  666. C.AKEYCODE_NUMPAD_3: key.CodeKeypad3,
  667. C.AKEYCODE_NUMPAD_4: key.CodeKeypad4,
  668. C.AKEYCODE_NUMPAD_5: key.CodeKeypad5,
  669. C.AKEYCODE_NUMPAD_6: key.CodeKeypad6,
  670. C.AKEYCODE_NUMPAD_7: key.CodeKeypad7,
  671. C.AKEYCODE_NUMPAD_8: key.CodeKeypad8,
  672. C.AKEYCODE_NUMPAD_9: key.CodeKeypad9,
  673. C.AKEYCODE_NUMPAD_DIVIDE: key.CodeKeypadSlash,
  674. C.AKEYCODE_NUMPAD_MULTIPLY: key.CodeKeypadAsterisk,
  675. C.AKEYCODE_NUMPAD_SUBTRACT: key.CodeKeypadHyphenMinus,
  676. C.AKEYCODE_NUMPAD_ADD: key.CodeKeypadPlusSign,
  677. C.AKEYCODE_NUMPAD_DOT: key.CodeKeypadFullStop,
  678. C.AKEYCODE_NUMPAD_ENTER: key.CodeKeypadEnter,
  679. C.AKEYCODE_NUMPAD_EQUALS: key.CodeKeypadEqualSign,
  680. C.AKEYCODE_VOLUME_MUTE: key.CodeMute,
  681. }
  682. func convAndroidKeyCode(aKeyCode int32) key.Code {
  683. if code, ok := androidKeycoe[aKeyCode]; ok {
  684. return code
  685. }
  686. return key.CodeUnknown
  687. }
  688. /*
  689. Many Android key codes do not map into USB HID codes.
  690. For those, key.CodeUnknown is returned. This switch has all
  691. cases, even the unknown ones, to serve as a documentation
  692. and search aid.
  693. C.AKEYCODE_UNKNOWN
  694. C.AKEYCODE_SOFT_LEFT
  695. C.AKEYCODE_SOFT_RIGHT
  696. C.AKEYCODE_BACK
  697. C.AKEYCODE_CALL
  698. C.AKEYCODE_ENDCALL
  699. C.AKEYCODE_STAR
  700. C.AKEYCODE_POUND
  701. C.AKEYCODE_DPAD_UP
  702. C.AKEYCODE_DPAD_DOWN
  703. C.AKEYCODE_DPAD_LEFT
  704. C.AKEYCODE_DPAD_RIGHT
  705. C.AKEYCODE_DPAD_CENTER
  706. C.AKEYCODE_POWER
  707. C.AKEYCODE_CAMERA
  708. C.AKEYCODE_CLEAR
  709. C.AKEYCODE_SYM
  710. C.AKEYCODE_EXPLORER
  711. C.AKEYCODE_ENVELOPE
  712. C.AKEYCODE_AT
  713. C.AKEYCODE_NUM
  714. C.AKEYCODE_HEADSETHOOK
  715. C.AKEYCODE_FOCUS
  716. C.AKEYCODE_PLUS
  717. C.AKEYCODE_MENU
  718. C.AKEYCODE_NOTIFICATION
  719. C.AKEYCODE_SEARCH
  720. C.AKEYCODE_MEDIA_PLAY_PAUSE
  721. C.AKEYCODE_MEDIA_STOP
  722. C.AKEYCODE_MEDIA_NEXT
  723. C.AKEYCODE_MEDIA_PREVIOUS
  724. C.AKEYCODE_MEDIA_REWIND
  725. C.AKEYCODE_MEDIA_FAST_FORWARD
  726. C.AKEYCODE_MUTE
  727. C.AKEYCODE_PICTSYMBOLS
  728. C.AKEYCODE_SWITCH_CHARSET
  729. C.AKEYCODE_BUTTON_A
  730. C.AKEYCODE_BUTTON_B
  731. C.AKEYCODE_BUTTON_C
  732. C.AKEYCODE_BUTTON_X
  733. C.AKEYCODE_BUTTON_Y
  734. C.AKEYCODE_BUTTON_Z
  735. C.AKEYCODE_BUTTON_L1
  736. C.AKEYCODE_BUTTON_R1
  737. C.AKEYCODE_BUTTON_L2
  738. C.AKEYCODE_BUTTON_R2
  739. C.AKEYCODE_BUTTON_THUMBL
  740. C.AKEYCODE_BUTTON_THUMBR
  741. C.AKEYCODE_BUTTON_START
  742. C.AKEYCODE_BUTTON_SELECT
  743. C.AKEYCODE_BUTTON_MODE
  744. C.AKEYCODE_SCROLL_LOCK
  745. C.AKEYCODE_FUNCTION
  746. C.AKEYCODE_SYSRQ
  747. C.AKEYCODE_BREAK
  748. C.AKEYCODE_MOVE_HOME
  749. C.AKEYCODE_MOVE_END
  750. C.AKEYCODE_FORWARD
  751. C.AKEYCODE_MEDIA_PLAY
  752. C.AKEYCODE_MEDIA_PAUSE
  753. C.AKEYCODE_MEDIA_CLOSE
  754. C.AKEYCODE_MEDIA_EJECT
  755. C.AKEYCODE_MEDIA_RECORD
  756. C.AKEYCODE_NUMPAD_COMMA
  757. C.AKEYCODE_NUMPAD_LEFT_PAREN
  758. C.AKEYCODE_NUMPAD_RIGHT_PAREN
  759. C.AKEYCODE_INFO
  760. C.AKEYCODE_CHANNEL_UP
  761. C.AKEYCODE_CHANNEL_DOWN
  762. C.AKEYCODE_ZOOM_IN
  763. C.AKEYCODE_ZOOM_OUT
  764. C.AKEYCODE_TV
  765. C.AKEYCODE_WINDOW
  766. C.AKEYCODE_GUIDE
  767. C.AKEYCODE_DVR
  768. C.AKEYCODE_BOOKMARK
  769. C.AKEYCODE_CAPTIONS
  770. C.AKEYCODE_SETTINGS
  771. C.AKEYCODE_TV_POWER
  772. C.AKEYCODE_TV_INPUT
  773. C.AKEYCODE_STB_POWER
  774. C.AKEYCODE_STB_INPUT
  775. C.AKEYCODE_AVR_POWER
  776. C.AKEYCODE_AVR_INPUT
  777. C.AKEYCODE_PROG_RED
  778. C.AKEYCODE_PROG_GREEN
  779. C.AKEYCODE_PROG_YELLOW
  780. C.AKEYCODE_PROG_BLUE
  781. C.AKEYCODE_APP_SWITCH
  782. C.AKEYCODE_BUTTON_1
  783. C.AKEYCODE_BUTTON_2
  784. C.AKEYCODE_BUTTON_3
  785. C.AKEYCODE_BUTTON_4
  786. C.AKEYCODE_BUTTON_5
  787. C.AKEYCODE_BUTTON_6
  788. C.AKEYCODE_BUTTON_7
  789. C.AKEYCODE_BUTTON_8
  790. C.AKEYCODE_BUTTON_9
  791. C.AKEYCODE_BUTTON_10
  792. C.AKEYCODE_BUTTON_11
  793. C.AKEYCODE_BUTTON_12
  794. C.AKEYCODE_BUTTON_13
  795. C.AKEYCODE_BUTTON_14
  796. C.AKEYCODE_BUTTON_15
  797. C.AKEYCODE_BUTTON_16
  798. C.AKEYCODE_LANGUAGE_SWITCH
  799. C.AKEYCODE_MANNER_MODE
  800. C.AKEYCODE_3D_MODE
  801. C.AKEYCODE_CONTACTS
  802. C.AKEYCODE_CALENDAR
  803. C.AKEYCODE_MUSIC
  804. C.AKEYCODE_CALCULATOR
  805. Defined in an NDK API version beyond what we use today:
  806. C.AKEYCODE_ASSIST
  807. C.AKEYCODE_BRIGHTNESS_DOWN
  808. C.AKEYCODE_BRIGHTNESS_UP
  809. C.AKEYCODE_RO
  810. C.AKEYCODE_YEN
  811. C.AKEYCODE_ZENKAKU_HANKAKU
  812. */