github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/app/app.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build linux darwin 6 7 package app 8 9 import ( 10 "github.com/c-darwin/mobile/event/lifecycle" 11 "github.com/c-darwin/mobile/event/paint" 12 "github.com/c-darwin/mobile/event/size" 13 "github.com/c-darwin/mobile/gl" 14 _ "github.com/c-darwin/mobile/internal/mobileinit" 15 ) 16 17 // Main is called by the main.main function to run the mobile application. 18 // 19 // It calls f on the App, in a separate goroutine, as some OS-specific 20 // libraries require being on 'the main thread'. 21 func Main(f func(App)) { 22 main(f) 23 } 24 25 // App is how a GUI mobile application interacts with the OS. 26 type App interface { 27 // Events returns the events channel. It carries events from the system to 28 // the app. The type of such events include: 29 // - lifecycle.Event 30 // - mouse.Event 31 // - paint.Event 32 // - size.Event 33 // - touch.Event 34 // from the github.com/c-darwin/mobile/event/etc packages. Other packages may 35 // define other event types that are carried on this channel. 36 Events() <-chan interface{} 37 38 // Send sends an event on the events channel. It does not block. 39 Send(event interface{}) 40 41 // EndPaint flushes any pending OpenGL commands or buffers to the screen. 42 // If EndPaint is called with an old generation number, it is ignored. 43 EndPaint(paint.Event) 44 } 45 46 var ( 47 lifecycleStage = lifecycle.StageDead 48 49 eventsOut = make(chan interface{}) 50 eventsIn = pump(eventsOut) 51 endPaint = make(chan paint.Event, 1) 52 ) 53 54 func sendLifecycle(to lifecycle.Stage) { 55 if lifecycleStage == to { 56 return 57 } 58 eventsIn <- lifecycle.Event{ 59 From: lifecycleStage, 60 To: to, 61 } 62 lifecycleStage = to 63 } 64 65 type app struct{} 66 67 func (app) Events() <-chan interface{} { 68 return eventsOut 69 } 70 71 func (app) Send(event interface{}) { 72 eventsIn <- event 73 } 74 75 func (app) EndPaint(e paint.Event) { 76 // gl.Flush is a lightweight (on modern GL drivers) blocking call 77 // that ensures all GL functions pending in the gl package have 78 // been passed onto the GL driver before the app package attempts 79 // to swap the screen buffer. 80 // 81 // This enforces that the final receive (for this paint cycle) on 82 // gl.WorkAvailable happens before the send on endPaint. 83 gl.Flush() 84 endPaint <- e 85 } 86 87 var filters []func(interface{}) interface{} 88 89 // Filter calls each registered event filter function in sequence. 90 func Filter(event interface{}) interface{} { 91 for _, f := range filters { 92 event = f(event) 93 } 94 return event 95 } 96 97 // RegisterFilter registers a event filter function to be called by Filter. The 98 // function can return a different event, or return nil to consume the event, 99 // but the function can also return its argument unchanged, where its purpose 100 // is to trigger a side effect rather than modify the event. 101 // 102 // RegisterFilter should only be called from init functions. 103 func RegisterFilter(f func(interface{}) interface{}) { 104 filters = append(filters, f) 105 } 106 107 type stopPumping struct{} 108 109 // pump returns a channel src such that sending on src will eventually send on 110 // dst, in order, but that src will always be ready to send/receive soon, even 111 // if dst currently isn't. It is effectively an infinitely buffered channel. 112 // 113 // In particular, goroutine A sending on src will not deadlock even if goroutine 114 // B that's responsible for receiving on dst is currently blocked trying to 115 // send to A on a separate channel. 116 // 117 // Send a stopPumping on the src channel to close the dst channel after all queued 118 // events are sent on dst. After that, other goroutines can still send to src, 119 // so that such sends won't block forever, but such events will be ignored. 120 func pump(dst chan interface{}) (src chan interface{}) { 121 src = make(chan interface{}) 122 go func() { 123 // initialSize is the initial size of the circular buffer. It must be a 124 // power of 2. 125 const initialSize = 16 126 i, j, buf, mask := 0, 0, make([]interface{}, initialSize), initialSize-1 127 128 maybeSrc := src 129 for { 130 maybeDst := dst 131 if i == j { 132 maybeDst = nil 133 } 134 if maybeDst == nil && maybeSrc == nil { 135 break 136 } 137 138 select { 139 case maybeDst <- buf[i&mask]: 140 buf[i&mask] = nil 141 i++ 142 143 case e := <-maybeSrc: 144 if _, ok := e.(stopPumping); ok { 145 maybeSrc = nil 146 continue 147 } 148 149 // Allocate a bigger buffer if necessary. 150 if i+len(buf) == j { 151 b := make([]interface{}, 2*len(buf)) 152 n := copy(b, buf[j&mask:]) 153 copy(b[n:], buf[:j&mask]) 154 i, j = 0, len(buf) 155 buf, mask = b, len(b)-1 156 } 157 158 buf[j&mask] = e 159 j++ 160 } 161 } 162 163 close(dst) 164 // Block forever. 165 for range src { 166 } 167 }() 168 return src 169 } 170 171 // TODO: do this for all build targets, not just linux (x11 and Android)? If 172 // so, should package gl instead of this package call RegisterFilter?? 173 // 174 // TODO: does Android need this?? It seems to work without it (Nexus 7, 175 // KitKat). If only x11 needs this, should we move this to x11.go?? 176 func registerGLViewportFilter() { 177 RegisterFilter(func(e interface{}) interface{} { 178 if e, ok := e.(size.Event); ok { 179 gl.Viewport(0, 0, e.WidthPx, e.HeightPx) 180 } 181 return e 182 }) 183 }