zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

android.go (21929B)


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