func (m *mainLoop) loop(init chan<- struct{}) { runtime.LockOSThread() defer runtime.UnlockOSThread() looper := C.ALooper_prepare(C.ALOOPER_PREPARE_ALLOW_NON_CALLBACKS) if looper == nil { panic("ALooper_prepare returned nil") } m.looper = looper init <- struct{}{} for { select { case <-m.quit: if m.renderState != nil && m.renderState.ctx != egl.Context(unsafe.Pointer(nil)) { if !egl.MakeCurrent(m.renderState.disp, egl.Surface(unsafe.Pointer(nil)), egl.Surface(unsafe.Pointer(nil)), egl.Context(unsafe.Pointer(nil))) { panic("Error: eglMakeCurrent() failed\n") } } m.ack <- struct{}{} break case <-m.resume: m.running = true m.ack <- struct{}{} case <-m.pause: m.running = false m.width, m.height = 0, 0 m.ack <- struct{}{} case m.focused = <-m.focus: m.width, m.height = 0, 0 m.ack <- struct{}{} case m.renderState = <-m.render: m.ack <- struct{}{} case inputQ := <-m.input: if inputQ != nil { C.AInputQueue_attachLooper(inputQ, m.looper, LOOPER_ID_INPUT, nil, nil) } else { C.AInputQueue_detachLooper(m.inputQ) } m.inputQ = inputQ m.ack <- struct{}{} default: m.frame() } } }
// The loop handles native input events. func androidEventLoopFunc(event chan interface{}, looperCh chan *C.ALooper) loop.LoopFunc { return func(l loop.Loop) error { var inputQueue *C.AInputQueue runtime.LockOSThread() defer runtime.UnlockOSThread() looper := C.ALooper_prepare(C.ALOOPER_PREPARE_ALLOW_NON_CALLBACKS) if looper == nil { Fatalf("ALooper_prepare returned nil") } looperCh <- looper for { select { case untypedEvent := <-event: switch event := untypedEvent.(type) { case inputQueueCreatedEvent: inputQueue = (*C.AInputQueue)(event.inputQueue) C.AInputQueue_attachLooper(inputQueue, looper, LOOPER_ID_INPUT, nil, nil) case inputQueueDestroyedEvent: inputQueue = (*C.AInputQueue)(event.inputQueue) C.AInputQueue_detachLooper(inputQueue) } default: if inputQueue != nil { ident := C.ALooper_pollAll(-1, nil, nil, nil) switch ident { case LOOPER_ID_INPUT: processInput(inputQueue) case C.ALOOPER_POLL_ERROR: Fatalf("ALooper_pollAll returned ALOOPER_POLL_ERROR\n") } } } } } }
func runInputQueue(vm, jniEnv, ctx uintptr) error { env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer // Android loopers select on OS file descriptors, not Go channels, so we // translate the inputQueue channel to an ALooper_wake call. l := C.ALooper_prepare(C.ALOOPER_PREPARE_ALLOW_NON_CALLBACKS) pending := make(chan *C.AInputQueue, 1) go func() { for q := range inputQueue { pending <- q C.ALooper_wake(l) } }() var q *C.AInputQueue for { if C.ALooper_pollAll(-1, nil, nil, nil) == C.ALOOPER_POLL_WAKE { select { default: case p := <-pending: if q != nil { processEvents(env, q) C.AInputQueue_detachLooper(q) } q = p if q != nil { C.AInputQueue_attachLooper(q, l, 0, nil, nil) } inputQueueDone <- struct{}{} } } if q != nil { processEvents(env, q) } } }
//export onInputQueueCreated func onInputQueueCreated(activity *C.ANativeActivity, q *C.AInputQueue) { C.AInputQueue_detachLooper(q) inputQueue <- q <-inputQueueDone }