Example #1
0
func windowDrawLoop(cb Callbacks, w *C.ANativeWindow, queue *C.AInputQueue) {
	C.createEGLWindow(w)

	// TODO: is the library or the app responsible for clearing the buffers?
	gl.ClearColor(0, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
	C.eglSwapBuffers(C.display, C.surface)

	if errv := gl.GetError(); errv != gl.NO_ERROR {
		log.Printf("GL initialization error: %s", errv)
	}

	geom.Width = geom.Pt(float32(C.windowWidth) / geom.PixelsPerPt)
	geom.Height = geom.Pt(float32(C.windowHeight) / geom.PixelsPerPt)

	for {
		processEvents(cb, queue)
		select {
		case <-windowDestroyed:
			return
		default:
			if cb.Draw != nil {
				cb.Draw()
			}
			C.eglSwapBuffers(C.display, C.surface)
		}
	}
}
Example #2
0
func windowDraw(w *C.ANativeWindow, queue *C.AInputQueue, donec chan struct{}) (done bool) {
	// Android can send a windowRedrawNeeded event any time, including
	// in the middle of a paint cycle. The redraw event may have changed
	// the size of the screen, so any partial painting is now invalidated.
	// We must also not return to Android (via sending on windowRedrawDone)
	// until a complete paint with the new configuration is complete.
	//
	// When a windowRedrawNeeded request comes in, we increment redrawGen
	// (Gen is short for generation number), and do not make a paint cycle
	// visible on <-endPaint unless paintGen agrees. If possible,
	// windowRedrawDone is signalled, allowing onNativeWindowRedrawNeeded
	// to return.
	var redrawGen, paintGen uint32
	for {
		processEvents(queue)
		select {
		case <-donec:
			return true
		case cfg := <-windowConfigChange:
			// TODO save orientation
			pixelsPerPt = cfg.pixelsPerPt
		case w := <-windowRedrawNeeded:
			sendLifecycle(lifecycle.StageFocused)
			widthPx := int(C.ANativeWindow_getWidth(w))
			heightPx := int(C.ANativeWindow_getHeight(w))
			eventsIn <- config.Event{
				WidthPx:     widthPx,
				HeightPx:    heightPx,
				WidthPt:     geom.Pt(float32(widthPx) / pixelsPerPt),
				HeightPt:    geom.Pt(float32(heightPx) / pixelsPerPt),
				PixelsPerPt: pixelsPerPt,
			}
			if paintGen == 0 {
				paintGen++
				C.createEGLWindow(w)
				eventsIn <- paint.Event{}
			}
			redrawGen++
		case <-windowDestroyed:
			sendLifecycle(lifecycle.StageAlive)
			return false
		case <-gl.WorkAvailable:
			gl.DoWork()
		case <-endPaint:
			if paintGen == redrawGen {
				// eglSwapBuffers blocks until vsync.
				C.eglSwapBuffers(C.display, C.surface)
				select {
				case windowRedrawDone <- struct{}{}:
				default:
				}
			}
			paintGen = redrawGen
			eventsIn <- paint.Event{}
		}
	}
}
Example #3
0
func windowDraw(w *C.ANativeWindow, queue *C.AInputQueue, donec chan struct{}) (done bool) {
	C.createEGLWindow(w)

	// TODO: is this needed if we also have the "case <-windowRedrawNeeded:" below??
	sendLifecycle(lifecycle.StageFocused)
	eventsIn <- config.Event{
		Width:       geom.Pt(float32(C.windowWidth) / pixelsPerPt),
		Height:      geom.Pt(float32(C.windowHeight) / pixelsPerPt),
		PixelsPerPt: pixelsPerPt,
	}
	if firstWindowDraw {
		firstWindowDraw = false
		// TODO: be more principled about when to send a paint event.
		eventsIn <- paint.Event{}
	}

	for {
		processEvents(queue)
		select {
		case <-donec:
			return true
		case <-windowRedrawNeeded:
			// Re-query the width and height.
			C.querySurfaceWidthAndHeight()
			sendLifecycle(lifecycle.StageFocused)
			eventsIn <- config.Event{
				Width:       geom.Pt(float32(C.windowWidth) / pixelsPerPt),
				Height:      geom.Pt(float32(C.windowHeight) / pixelsPerPt),
				PixelsPerPt: pixelsPerPt,
			}
		case <-windowDestroyed:
			sendLifecycle(lifecycle.StageAlive)
			return false
		case <-gl.WorkAvailable:
			gl.DoWork()
		case <-endDraw:
			// eglSwapBuffers blocks until vsync.
			C.eglSwapBuffers(C.display, C.surface)
			eventsIn <- paint.Event{}
		}
	}
}
Example #4
0
func windowDraw(w *C.ANativeWindow, queue *C.AInputQueue, donec chan struct{}) (done bool) {
	C.createEGLWindow(w)

	// TODO: is this needed if we also have the "case <-windowRedrawNeeded:" below??
	sendLifecycle(event.LifecycleStageFocused)
	eventsIn <- event.Config{
		Width:       geom.Pt(float32(C.windowWidth) / pixelsPerPt),
		Height:      geom.Pt(float32(C.windowHeight) / pixelsPerPt),
		PixelsPerPt: pixelsPerPt,
	}
	if firstWindowDraw {
		firstWindowDraw = false
		// TODO: be more principled about when to send a draw event.
		eventsIn <- event.Draw{}
	}

	for {
		processEvents(queue)
		select {
		case <-donec:
			return true
		case <-windowRedrawNeeded:
			// Re-query the width and height.
			C.querySurfaceWidthAndHeight()
			sendLifecycle(event.LifecycleStageFocused)
			eventsIn <- event.Config{
				Width:       geom.Pt(float32(C.windowWidth) / pixelsPerPt),
				Height:      geom.Pt(float32(C.windowHeight) / pixelsPerPt),
				PixelsPerPt: pixelsPerPt,
			}
			// This gl.Viewport call has to be in a separate goroutine because any gl
			// call can block until gl.DoWork is called, but this goroutine is the one
			// responsible for calling gl.DoWork.
			// TODO: again, should x/mobile/app be responsible for calling GL code, or
			// should package gl instead call event.RegisterFilter?
			{
				c := make(chan struct{})
				go func() {
					gl.Viewport(0, 0, int(C.windowWidth), int(C.windowHeight))
					close(c)
				}()
			loop1:
				for {
					select {
					case <-gl.WorkAvailable:
						gl.DoWork()
					case <-c:
						break loop1
					}
				}
			}
		case <-windowDestroyed:
			sendLifecycle(event.LifecycleStageAlive)
			return false
		case <-gl.WorkAvailable:
			gl.DoWork()
		case <-endDraw:
			// eglSwapBuffers blocks until vsync.
			C.eglSwapBuffers(C.display, C.surface)
			eventsIn <- event.Draw{}
		}
	}
}