func (m *mainLoop) frame() { var timeout C.int = 0 if !m.isRunning() { timeout = -1 } ident := C.ALooper_pollAll(timeout, nil, nil, nil) switch ident { case LOOPER_ID_INPUT: if m.inputQ != nil { m.processInput(m.inputQ) } case C.ALOOPER_POLL_ERROR: log.Fatalf("ALooper_pollAll returned ALOOPER_POLL_ERROR\n") } if m.isRunning() { m.checkSize() createCtx := m.renderState.ctx == egl.Context(unsafe.Pointer(nil)) if createCtx { log.Printf("Creating context\n") ctx_attribs := [...]int32{ egl.CONTEXT_CLIENT_VERSION, 2, egl.NONE, } m.renderState.ctx = egl.CreateContext(m.renderState.disp, m.renderState.conf, egl.NO_CONTEXT, &ctx_attribs[0]) if m.renderState.ctx == egl.Context(unsafe.Pointer(nil)) { panic("Error: eglCreateContext failed\n") } } if !egl.MakeCurrent(m.renderState.disp, m.renderState.surf, m.renderState.surf, m.renderState.ctx) { panic("Error: eglMakeCurrent() failed\n") } if createCtx { m.game.initGL() } m.game.drawFrame() egl.SwapBuffers(m.renderState.disp, m.renderState.surf) } }
// 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) } } }