loop.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. package glfw
  2. import (
  3. "runtime"
  4. "sync"
  5. "time"
  6. "fyne.io/fyne/v2"
  7. "fyne.io/fyne/v2/internal"
  8. "fyne.io/fyne/v2/internal/app"
  9. "fyne.io/fyne/v2/internal/cache"
  10. "fyne.io/fyne/v2/internal/painter"
  11. )
  12. type funcData struct {
  13. f func()
  14. done chan struct{} // Zero allocation signalling channel
  15. }
  16. type drawData struct {
  17. f func()
  18. win *window
  19. done chan struct{} // Zero allocation signalling channel
  20. }
  21. type runFlag struct {
  22. sync.Mutex
  23. flag bool
  24. cond *sync.Cond
  25. }
  26. // channel for queuing functions on the main thread
  27. var funcQueue = make(chan funcData)
  28. var drawFuncQueue = make(chan drawData)
  29. var run *runFlag
  30. var initOnce = &sync.Once{}
  31. var donePool = &sync.Pool{New: func() interface{} {
  32. return make(chan struct{})
  33. }}
  34. func newRun() *runFlag {
  35. r := runFlag{}
  36. r.cond = sync.NewCond(&r)
  37. return &r
  38. }
  39. // Arrange that main.main runs on main thread.
  40. func init() {
  41. runtime.LockOSThread()
  42. mainGoroutineID = goroutineID()
  43. run = newRun()
  44. }
  45. // force a function f to run on the main thread
  46. func runOnMain(f func()) {
  47. // If we are on main just execute - otherwise add it to the main queue and wait.
  48. // The "running" variable is normally false when we are on the main thread.
  49. run.Lock()
  50. if !run.flag {
  51. f()
  52. run.Unlock()
  53. } else {
  54. run.Unlock()
  55. done := donePool.Get().(chan struct{})
  56. defer donePool.Put(done)
  57. funcQueue <- funcData{f: f, done: done}
  58. <-done
  59. }
  60. }
  61. // force a function f to run on the draw thread
  62. func runOnDraw(w *window, f func()) {
  63. if drawOnMainThread {
  64. runOnMain(func() { w.RunWithContext(f) })
  65. return
  66. }
  67. done := donePool.Get().(chan struct{})
  68. defer donePool.Put(done)
  69. drawFuncQueue <- drawData{f: f, win: w, done: done}
  70. <-done
  71. }
  72. func (d *gLDriver) drawSingleFrame() {
  73. refreshingCanvases := make([]fyne.Canvas, 0)
  74. for _, win := range d.windowList() {
  75. w := win.(*window)
  76. w.viewLock.RLock()
  77. canvas := w.canvas
  78. closing := w.closing
  79. visible := w.visible
  80. w.viewLock.RUnlock()
  81. // CheckDirtyAndClear must be checked after visibility,
  82. // because when a window becomes visible, it could be
  83. // showing old content without a dirty flag set to true.
  84. // Do the clear if and only if the window is visible.
  85. if closing || !visible || !canvas.CheckDirtyAndClear() {
  86. continue
  87. }
  88. d.repaintWindow(w)
  89. refreshingCanvases = append(refreshingCanvases, canvas)
  90. }
  91. cache.CleanCanvases(refreshingCanvases)
  92. }
  93. func (d *gLDriver) runGL() {
  94. eventTick := time.NewTicker(time.Second / 60)
  95. run.Lock()
  96. run.flag = true
  97. run.Unlock()
  98. run.cond.Broadcast()
  99. d.initGLFW()
  100. if d.trayStart != nil {
  101. d.trayStart()
  102. }
  103. fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerStarted()
  104. for {
  105. select {
  106. case <-d.done:
  107. eventTick.Stop()
  108. d.drawDone <- nil // wait for draw thread to stop
  109. d.Terminate()
  110. fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerStopped()
  111. return
  112. case f := <-funcQueue:
  113. f.f()
  114. if f.done != nil {
  115. f.done <- struct{}{}
  116. }
  117. case <-eventTick.C:
  118. d.tryPollEvents()
  119. newWindows := []fyne.Window{}
  120. reassign := false
  121. for _, win := range d.windowList() {
  122. w := win.(*window)
  123. if w.viewport == nil {
  124. continue
  125. }
  126. if w.viewport.ShouldClose() {
  127. reassign = true
  128. w.viewLock.Lock()
  129. w.visible = false
  130. v := w.viewport
  131. w.viewLock.Unlock()
  132. // remove window from window list
  133. v.Destroy()
  134. w.destroy(d)
  135. continue
  136. }
  137. w.viewLock.RLock()
  138. expand := w.shouldExpand
  139. fullScreen := w.fullScreen
  140. w.viewLock.RUnlock()
  141. if expand && !fullScreen {
  142. w.fitContent()
  143. w.viewLock.Lock()
  144. shouldExpand := w.shouldExpand
  145. w.shouldExpand = false
  146. view := w.viewport
  147. w.viewLock.Unlock()
  148. if shouldExpand {
  149. view.SetSize(w.shouldWidth, w.shouldHeight)
  150. }
  151. }
  152. newWindows = append(newWindows, win)
  153. if drawOnMainThread {
  154. d.drawSingleFrame()
  155. }
  156. }
  157. if reassign {
  158. d.windowLock.Lock()
  159. d.windows = newWindows
  160. d.windowLock.Unlock()
  161. if len(newWindows) == 0 {
  162. d.Quit()
  163. }
  164. }
  165. }
  166. }
  167. }
  168. func (d *gLDriver) repaintWindow(w *window) {
  169. canvas := w.canvas
  170. w.RunWithContext(func() {
  171. if w.canvas.EnsureMinSize() {
  172. w.viewLock.Lock()
  173. w.shouldExpand = true
  174. w.viewLock.Unlock()
  175. }
  176. canvas.FreeDirtyTextures()
  177. updateGLContext(w)
  178. canvas.paint(canvas.Size())
  179. w.viewLock.RLock()
  180. view := w.viewport
  181. visible := w.visible
  182. w.viewLock.RUnlock()
  183. if view != nil && visible {
  184. view.SwapBuffers()
  185. }
  186. })
  187. }
  188. func (d *gLDriver) startDrawThread() {
  189. settingsChange := make(chan fyne.Settings)
  190. fyne.CurrentApp().Settings().AddChangeListener(settingsChange)
  191. var drawCh <-chan time.Time
  192. if drawOnMainThread {
  193. drawCh = make(chan time.Time) // don't tick when on M1
  194. } else {
  195. drawCh = time.NewTicker(time.Second / 60).C
  196. }
  197. go func() {
  198. runtime.LockOSThread()
  199. for {
  200. select {
  201. case <-d.drawDone:
  202. return
  203. case f := <-drawFuncQueue:
  204. f.win.RunWithContext(f.f)
  205. if f.done != nil {
  206. f.done <- struct{}{}
  207. }
  208. case set := <-settingsChange:
  209. painter.ClearFontCache()
  210. cache.ResetThemeCaches()
  211. app.ApplySettingsWithCallback(set, fyne.CurrentApp(), func(w fyne.Window) {
  212. c, ok := w.Canvas().(*glCanvas)
  213. if !ok {
  214. return
  215. }
  216. c.applyThemeOutOfTreeObjects()
  217. go c.reloadScale()
  218. })
  219. case <-drawCh:
  220. d.drawSingleFrame()
  221. }
  222. }
  223. }()
  224. }
  225. // refreshWindow requests that the specified window be redrawn
  226. func refreshWindow(w *window) {
  227. w.canvas.SetDirty()
  228. }
  229. func updateGLContext(w *window) {
  230. canvas := w.Canvas().(*glCanvas)
  231. size := canvas.Size()
  232. // w.width and w.height are not correct if we are maximised, so figure from canvas
  233. winWidth := float32(internal.ScaleInt(canvas, size.Width)) * canvas.texScale
  234. winHeight := float32(internal.ScaleInt(canvas, size.Height)) * canvas.texScale
  235. canvas.Painter().SetFrameBufferScale(canvas.texScale)
  236. w.canvas.Painter().SetOutputSize(int(winWidth), int(winHeight))
  237. }