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