loop.go 6.5 KB

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