app.go 5.8 KB

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