app.go 5.8 KB

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