loop.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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. func (d *gLDriver) drawSingleFrame() {
  64. refreshingCanvases := make([]fyne.Canvas, 0)
  65. for _, win := range d.windowList() {
  66. w := win.(*window)
  67. w.viewLock.RLock()
  68. canvas := w.canvas
  69. closing := w.closing
  70. visible := w.visible
  71. w.viewLock.RUnlock()
  72. // CheckDirtyAndClear must be checked after visibility,
  73. // because when a window becomes visible, it could be
  74. // showing old content without a dirty flag set to true.
  75. // Do the clear if and only if the window is visible.
  76. if closing || !visible || !canvas.CheckDirtyAndClear() {
  77. continue
  78. }
  79. d.repaintWindow(w)
  80. refreshingCanvases = append(refreshingCanvases, canvas)
  81. }
  82. cache.CleanCanvases(refreshingCanvases)
  83. }
  84. func (d *gLDriver) runGL() {
  85. eventTick := time.NewTicker(time.Second / 60)
  86. run.L.Lock()
  87. run.flag = true
  88. run.L.Unlock()
  89. run.Broadcast()
  90. d.initGLFW()
  91. if d.trayStart != nil {
  92. d.trayStart()
  93. }
  94. fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerStarted()
  95. for {
  96. select {
  97. case <-d.done:
  98. eventTick.Stop()
  99. d.drawDone <- nil // wait for draw thread to stop
  100. d.Terminate()
  101. fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerStopped()
  102. return
  103. case f := <-funcQueue:
  104. f.f()
  105. if f.done != nil {
  106. f.done <- struct{}{}
  107. }
  108. case <-eventTick.C:
  109. d.tryPollEvents()
  110. newWindows := []fyne.Window{}
  111. reassign := false
  112. for _, win := range d.windowList() {
  113. w := win.(*window)
  114. if w.viewport == nil {
  115. continue
  116. }
  117. if w.viewport.ShouldClose() {
  118. reassign = true
  119. w.viewLock.Lock()
  120. w.visible = false
  121. v := w.viewport
  122. w.viewLock.Unlock()
  123. // remove window from window list
  124. v.Destroy()
  125. w.destroy(d)
  126. continue
  127. }
  128. w.viewLock.RLock()
  129. expand := w.shouldExpand
  130. fullScreen := w.fullScreen
  131. w.viewLock.RUnlock()
  132. if expand && !fullScreen {
  133. w.fitContent()
  134. w.viewLock.Lock()
  135. shouldExpand := w.shouldExpand
  136. w.shouldExpand = false
  137. view := w.viewport
  138. w.viewLock.Unlock()
  139. if shouldExpand {
  140. view.SetSize(w.shouldWidth, w.shouldHeight)
  141. }
  142. }
  143. newWindows = append(newWindows, win)
  144. if drawOnMainThread {
  145. d.drawSingleFrame()
  146. }
  147. }
  148. if reassign {
  149. d.windowLock.Lock()
  150. d.windows = newWindows
  151. d.windowLock.Unlock()
  152. if len(newWindows) == 0 {
  153. d.Quit()
  154. }
  155. }
  156. }
  157. }
  158. }
  159. func (d *gLDriver) repaintWindow(w *window) {
  160. canvas := w.canvas
  161. w.RunWithContext(func() {
  162. if canvas.EnsureMinSize() {
  163. w.viewLock.Lock()
  164. w.shouldExpand = true
  165. w.viewLock.Unlock()
  166. }
  167. canvas.FreeDirtyTextures()
  168. updateGLContext(w)
  169. canvas.paint(canvas.Size())
  170. w.viewLock.RLock()
  171. view := w.viewport
  172. visible := w.visible
  173. w.viewLock.RUnlock()
  174. if view != nil && visible {
  175. view.SwapBuffers()
  176. }
  177. })
  178. }
  179. func (d *gLDriver) startDrawThread() {
  180. settingsChange := make(chan fyne.Settings)
  181. fyne.CurrentApp().Settings().AddChangeListener(settingsChange)
  182. var drawCh <-chan time.Time
  183. if drawOnMainThread {
  184. drawCh = make(chan time.Time) // don't tick when on M1
  185. } else {
  186. drawCh = time.NewTicker(time.Second / 60).C
  187. }
  188. go func() {
  189. runtime.LockOSThread()
  190. for {
  191. select {
  192. case <-d.drawDone:
  193. return
  194. case f := <-drawFuncQueue:
  195. f.win.RunWithContext(f.f)
  196. if f.done != nil {
  197. f.done <- struct{}{}
  198. }
  199. case set := <-settingsChange:
  200. painter.ClearFontCache()
  201. cache.ResetThemeCaches()
  202. app.ApplySettingsWithCallback(set, fyne.CurrentApp(), func(w fyne.Window) {
  203. c, ok := w.Canvas().(*glCanvas)
  204. if !ok {
  205. return
  206. }
  207. c.applyThemeOutOfTreeObjects()
  208. go c.reloadScale()
  209. })
  210. case <-drawCh:
  211. d.drawSingleFrame()
  212. }
  213. }
  214. }()
  215. }
  216. // refreshWindow requests that the specified window be redrawn
  217. func refreshWindow(w *window) {
  218. w.canvas.SetDirty()
  219. }
  220. func updateGLContext(w *window) {
  221. canvas := w.Canvas().(*glCanvas)
  222. size := canvas.Size()
  223. // w.width and w.height are not correct if we are maximised, so figure from canvas
  224. winWidth := float32(scale.ToScreenCoordinate(canvas, size.Width)) * canvas.texScale
  225. winHeight := float32(scale.ToScreenCoordinate(canvas, size.Height)) * canvas.texScale
  226. canvas.Painter().SetFrameBufferScale(canvas.texScale)
  227. w.canvas.Painter().SetOutputSize(int(winWidth), int(winHeight))
  228. }