twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

android.go (21895B)


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