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