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