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  }