go.mobile/app: an exceedingly simple display loop

Missing many features (like event processing). This is
just enough to get example/basic working.

LGTM=nigeltao
R=golang-codereviews, capnm9, bryanturley, nigeltao, crawshaw
CC=golang-codereviews
https://golang.org/cl/131030043
This commit is contained in:
David Crawshaw 2014-09-09 19:51:04 -04:00
Родитель 1d1714ebe9
Коммит e27dbf7bf9
4 изменённых файлов: 144 добавлений и 13 удалений

Просмотреть файл

@ -30,7 +30,10 @@ int go_started;
JavaVM* current_vm;
*/
import "C"
import "unsafe"
import (
"runtime"
"unsafe"
)
//export onStart
func onStart(activity *C.ANativeActivity) {
@ -63,6 +66,7 @@ func onWindowFocusChanged(activity *C.ANativeActivity, hasFocus int) {
//export onNativeWindowCreated
func onNativeWindowCreated(activity *C.ANativeActivity, w *C.ANativeWindow) {
windowCreated <- w
}
//export onNativeWindowResized
@ -75,6 +79,7 @@ func onNativeWindowRedrawNeeded(activity *C.ANativeActivity, window *C.ANativeWi
//export onNativeWindowDestroyed
func onNativeWindowDestroyed(activity *C.ANativeActivity, window *C.ANativeWindow) {
windowDestroyed <- true
}
//export onInputQueueCreated
@ -102,7 +107,15 @@ func onLowMemory(activity *C.ANativeActivity) {
// bindings access to the JNI *JavaVM object.
var JavaInit func(javaVM uintptr)
func run() {
var (
windowDestroyed = make(chan bool)
windowCreated = make(chan *C.ANativeWindow)
)
func run(cb Callbacks) {
// We want to keep the event loop on a consistent OS thread.
runtime.LockOSThread()
ctag := C.CString("Go")
cstr := C.CString("app.Run")
C.__android_log_write(C.ANDROID_LOG_INFO, ctag, cstr)
@ -119,5 +132,10 @@ func run() {
C.pthread_cond_signal(&C.go_started_cond)
C.pthread_mutex_unlock(&C.go_started_mu)
select {}
for {
select {
case w := <-windowCreated:
windowDrawLoop(cb, w)
}
}
}

Просмотреть файл

@ -4,16 +4,22 @@
package app
// Run starts the process.
func Run() {
run()
// Run starts the app.
//
// It must be called directly from from the main function and will
// block until the app exits.
func Run(cb Callbacks) {
run(cb)
}
// Draw is called by the render loop to draw the screen.
//
// Drawing is done into a framebuffer, which is then swapped onto the
// screen when Draw returns. It is called 60 times a second.
var Draw func()
// Callbacks is the set of functions called by the app.
type Callbacks struct {
// Draw is called by the render loop to draw the screen.
//
// Drawing is done into a framebuffer, which is then swapped onto the
// screen when Draw returns. It is called 60 times a second.
Draw func()
}
/*
TODO(crawshaw): Implement.

Просмотреть файл

@ -61,8 +61,9 @@ function exits, the app exits.
)
func main() {
app.Draw = draw
app.Run()
app.Run(app.Callbacks{
Draw: draw,
})
}
func draw() {

106
app/loop_android.go Normal file
Просмотреть файл

@ -0,0 +1,106 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package app
/*
#cgo android LDFLAGS: -llog -landroid -lEGL -lGLESv2
#include <android/log.h>
#include <android/native_activity.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
// TODO(crawshaw): Test configuration on more devices.
const EGLint RGB_888[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_NONE
};
EGLint windowWidth;
EGLint windowHeight;
EGLDisplay display;
EGLSurface surface;
#define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, "Go", __VA_ARGS__)
void createEGLWindow(ANativeWindow* window) {
EGLint numConfigs, format;
EGLConfig config;
EGLContext context;
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(display, 0, 0)) {
LOG_ERROR("EGL initialize failed");
return;
}
if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) {
LOG_ERROR("EGL choose RGB_888 config failed");
return;
}
if (numConfigs <= 0) {
LOG_ERROR("EGL no config found");
return;
}
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) {
LOG_ERROR("EGL set buffers geometry failed");
return;
}
surface = eglCreateWindowSurface(display, config, window, NULL);
if (surface == EGL_NO_SURFACE) {
LOG_ERROR("EGL create surface failed");
return;
}
const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
LOG_ERROR("eglMakeCurrent failed");
return;
}
eglQuerySurface(display, surface, EGL_WIDTH, &windowWidth);
eglQuerySurface(display, surface, EGL_HEIGHT, &windowHeight);
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, windowWidth, windowHeight);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
eglSwapBuffers(display, surface);
GLenum err;
if ((err = glGetError()) != GL_NO_ERROR) {
LOG_ERROR("GL error in createEGLWindow: 0x%x", err);
}
}
#undef LOG_ERROR
*/
import "C"
// windowDrawLoop calls Draw at 60 FPS processes input queue events.
func windowDrawLoop(cb Callbacks, window *C.ANativeWindow) {
C.createEGLWindow(window)
for {
select {
case <-windowDestroyed:
return
// TODO(crawshaw): Event processing goes here.
default:
cb.Draw()
C.eglSwapBuffers(C.display, C.surface)
}
}
}