func main(f func(App)) { runtime.LockOSThread() C.createWindow() // TODO: send lifecycle events when e.g. the X11 window is iconified or moved off-screen. sendLifecycle(lifecycle.StageFocused) donec := make(chan struct{}) go func() { f(app{}) close(donec) }() // TODO: can we get the actual vsync signal? ticker := time.NewTicker(time.Second / 60) defer ticker.Stop() tc := ticker.C for { select { case <-donec: return case <-gl.WorkAvailable: gl.DoWork() case <-endPaint: C.swapBuffers() tc = ticker.C case <-tc: tc = nil eventsIn <- paint.Event{} } C.processEvents() } }
// loop is the primary drawing loop. // // After UIKit has captured the initial OS thread for processing UIKit // events in runApp, it starts loop on another goroutine. It is locked // to an OS thread for its OpenGL context. func (a *app) loop(ctx C.GLintptr) { runtime.LockOSThread() C.makeCurrentContext(ctx) workAvailable := a.worker.WorkAvailable() for { select { case <-workAvailable: a.worker.DoWork() case <-theApp.publish: loop1: for { select { case <-workAvailable: a.worker.DoWork() default: break loop1 } } C.swapBuffers(ctx) theApp.publishResult <- PublishResult{} } } }
func main(f func(screen.Screen)) error { if gl.Version() == "GL_ES_2_0" { return errors.New("gldriver: ES 3 required on X11") } C.startDriver() glctx, worker = gl.NewContext() closec := make(chan struct{}) go func() { f(theScreen) close(closec) }() // heartbeat is a channel that, at regular intervals, directs the select // below to also consider X11 events, not just Go events (channel // communications). // // TODO: select instead of poll. Note that knowing whether to call // C.processEvents needs to select on a file descriptor, and the other // cases below select on Go channels. heartbeat := time.NewTicker(time.Second / 60) workAvailable := worker.WorkAvailable() for { select { case <-closec: return nil case ctx := <-glcontextc: // TODO: do we need to synchronize with seeing a size event for // this window's context before or after calling makeCurrent? // Otherwise, are we racing with the gl.Viewport call? I've // occasionally seen a stale viewport, if the window manager sets // the window width and height to something other than that // requested by XCreateWindow, but it's not easily reproducible. C.makeCurrent(C.uintptr_t(ctx)) case w := <-publishc: C.swapBuffers(C.uintptr_t(w.ctx.(uintptr))) w.publishDone <- screen.PublishResult{} case req := <-uic: ret := req.f() if req.retc != nil { req.retc <- ret } case <-heartbeat.C: C.processEvents() case <-workAvailable: worker.DoWork() } } }
func main(f func(App)) { runtime.LockOSThread() var worker gl.Worker glctx, worker = gl.NewContext() workAvailable := worker.WorkAvailable() C.createWindow() // TODO: send lifecycle events when e.g. the X11 window is iconified or moved off-screen. theApp.sendLifecycle(lifecycle.StageFocused) // TODO: translate X11 expose events to shiny paint events, instead of // sending this synthetic paint event as a hack. theApp.eventsIn <- paint.Event{} donec := make(chan struct{}) go func() { f(theApp) close(donec) }() // TODO: can we get the actual vsync signal? ticker := time.NewTicker(time.Second / 60) defer ticker.Stop() var tc <-chan time.Time for { select { case <-donec: return case <-workAvailable: worker.DoWork() case <-theApp.publish: C.swapBuffers() tc = ticker.C case <-tc: tc = nil theApp.publishResult <- PublishResult{} } C.processEvents() } }