• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fyne-io / fyne / 18808454873

25 Oct 2025 09:01PM UTC coverage: 61.62% (+0.6%) from 61.061%
18808454873

Pull #5776

github

Jacalz
Merge remote-tracking branch 'fyne/develop' into cleanup-cmd-fyne
Pull Request #5776: cmd/fyne: Remove everything but the public APIs

0 of 4 new or added lines in 2 files covered. (0.0%)

167 existing lines in 3 files now uncovered.

25475 of 41342 relevant lines covered (61.62%)

703.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.0
/internal/driver/glfw/loop.go
1
package glfw
2

3
import (
4
        "runtime"
5
        "sync/atomic"
6
        "time"
7

8
        "fyne.io/fyne/v2"
9
        "fyne.io/fyne/v2/internal/app"
10
        "fyne.io/fyne/v2/internal/async"
11
        "fyne.io/fyne/v2/internal/cache"
12
        "fyne.io/fyne/v2/internal/driver/common"
13
        "fyne.io/fyne/v2/internal/painter"
14
        "fyne.io/fyne/v2/internal/scale"
15
)
16

17
type funcData struct {
18
        f    func()
19
        done chan struct{} // Zero allocation signalling channel
20
}
21

22
// channel for queuing functions on the main thread
23
var (
24
        funcQueue        = async.NewUnboundedChan[funcData]()
25
        running, drained atomic.Bool
26
)
27

28
// Arrange that main.main runs on main thread.
29
func init() {
1✔
30
        runtime.LockOSThread()
1✔
31
        async.SetMainGoroutine()
1✔
32
}
1✔
33

34
// force a function f to run on the main thread
35
func runOnMain(f func()) {
1,058✔
36
        runOnMainWithWait(f, true)
1,058✔
37
}
1,058✔
38

39
// force a function f to run on the main thread and specify if we should wait for it to return
40
func runOnMainWithWait(f func(), wait bool) {
1,058✔
41
        // If we are on main before app run just execute - otherwise add it to the main queue and wait.
1,058✔
42
        // We also need to run it as-is if the app is in the process of shutting down as the queue will be stopped.
1,058✔
43
        if (!running.Load() && async.IsMainGoroutine()) || drained.Load() {
1,060✔
44
                f()
2✔
45
                return
2✔
46
        }
2✔
47

48
        if wait {
2,112✔
49
                done := common.DonePool.Get()
1,056✔
50
                defer common.DonePool.Put(done)
1,056✔
51

1,056✔
52
                funcQueue.In() <- funcData{f: f, done: done}
1,056✔
53
                <-done
1,056✔
54
        } else {
1,056✔
55
                funcQueue.In() <- funcData{f: f}
×
56
        }
×
57
}
58

59
func (d *gLDriver) drawSingleFrame() {
854✔
60
        refreshed := false
854✔
61
        for _, win := range d.windowList() {
34,279✔
62
                w := win.(*window)
33,425✔
63
                if w.closing {
33,425✔
64
                        continue
×
65
                }
66

67
                // CheckDirtyAndClear must be checked after visibility,
68
                // because when a window becomes visible, it could be
69
                // showing old content without a dirty flag set to true.
70
                // Do the clear if and only if the window is visible.
71
                if !w.visible || !w.canvas.CheckDirtyAndClear() {
66,849✔
72
                        // Window hidden or not being redrawn, mark canvasForObject
33,424✔
73
                        // cache alive if it hasn't been done recently
33,424✔
74
                        // n.b. we need to make sure threshold is a bit *after*
33,424✔
75
                        // time.Now() - CacheDuration()
33,424✔
76
                        threshold := time.Now().Add(10*time.Second - cache.ValidDuration)
33,424✔
77
                        if w.lastWalkedTime.Before(threshold) {
33,501✔
78
                                w.canvas.WalkTrees(nil, func(node *common.RenderCacheNode, _ fyne.Position) {
154✔
79
                                        // marks canvas for object cache entry alive
77✔
80
                                        _ = cache.GetCanvasForObject(node.Obj())
77✔
81
                                        // marks renderer cache entry alive
77✔
82
                                        if wid, ok := node.Obj().(fyne.Widget); ok {
77✔
UNCOV
83
                                                _, _ = cache.CachedRenderer(wid)
×
UNCOV
84
                                        }
×
85
                                })
86
                                w.lastWalkedTime = time.Now()
77✔
87
                        }
88
                        continue
33,424✔
89
                }
90

91
                w.RunWithContext(func() {
2✔
92
                        if w.driver.repaintWindow(w) {
1✔
93
                                refreshed = true
×
94
                        }
×
95
                })
96
        }
97
        cache.Clean(refreshed)
854✔
98
}
99

100
func (d *gLDriver) runGL() {
1✔
101
        if !running.CompareAndSwap(false, true) {
1✔
102
                return // Run was called twice.
×
103
        }
×
104

105
        d.init()
1✔
106
        if d.trayStart != nil {
1✔
107
                d.trayStart()
×
108
        }
×
109

110
        fyne.CurrentApp().Settings().AddListener(func(set fyne.Settings) {
2✔
111
                painter.ClearFontCache()
1✔
112
                cache.ResetThemeCaches()
1✔
113
                app.ApplySettingsWithCallback(set, fyne.CurrentApp(), func(w fyne.Window) {
2✔
114
                        c, ok := w.Canvas().(*glCanvas)
1✔
115
                        if !ok {
2✔
116
                                return
1✔
117
                        }
1✔
118
                        c.applyThemeOutOfTreeObjects()
×
119
                        c.reloadScale()
×
120
                })
121
        })
122

123
        if f := fyne.CurrentApp().Lifecycle().(*app.Lifecycle).OnStarted(); f != nil {
1✔
124
                f()
×
125
        }
×
126

127
        eventTick := time.NewTicker(time.Second / 60)
1✔
128
        for {
1,912✔
129
                select {
1,911✔
130
                case <-d.done:
×
131
                        eventTick.Stop()
×
132
                        d.Terminate()
×
133
                        l := fyne.CurrentApp().Lifecycle().(*app.Lifecycle)
×
134
                        if f := l.OnStopped(); f != nil {
×
135
                                l.QueueEvent(f)
×
136
                        }
×
137

138
                        // as we are shutting down make sure we drain the pending funcQueue and close it out.
139
                        for len(funcQueue.Out()) > 0 {
×
140
                                f := <-funcQueue.Out()
×
141
                                if f.done != nil {
×
142
                                        f.done <- struct{}{}
×
143
                                }
×
144
                        }
145
                        drained.Store(true)
×
146
                        funcQueue.Close()
×
147
                        return
×
148
                case f := <-funcQueue.Out():
1,056✔
149
                        f.f()
1,056✔
150
                        if f.done != nil {
2,112✔
151
                                f.done <- struct{}{}
1,056✔
152
                        }
1,056✔
153
                case <-eventTick.C:
854✔
154
                        d.pollEvents()
854✔
155
                        for i := 0; i < len(d.windows); i++ {
34,282✔
156
                                w := d.windows[i].(*window)
33,428✔
157
                                if w.viewport == nil {
33,428✔
158
                                        continue
×
159
                                }
160

161
                                if w.viewport.ShouldClose() {
33,431✔
162
                                        d.destroyWindow(w, i)
3✔
163
                                        i-- // Trailing windows are moved forward one step.
3✔
164
                                        continue
3✔
165
                                }
166

167
                                expand := w.shouldExpand
33,425✔
168
                                fullScreen := w.fullScreen
33,425✔
169

33,425✔
170
                                if expand && !fullScreen {
33,570✔
171
                                        w.fitContent()
145✔
172
                                        shouldExpand := w.shouldExpand
145✔
173
                                        w.shouldExpand = false
145✔
174
                                        view := w.viewport
145✔
175

145✔
176
                                        if shouldExpand && runtime.GOOS != "js" {
290✔
177
                                                view.SetSize(w.shouldWidth, w.shouldHeight)
145✔
178
                                        }
145✔
179
                                }
180
                        }
181

182
                        d.animation.TickAnimations()
854✔
183
                        d.drawSingleFrame()
854✔
184
                }
185
        }
186
}
187

188
func (d *gLDriver) destroyWindow(w *window, index int) {
3✔
189
        w.visible = false
3✔
190
        w.viewport.Destroy()
3✔
191
        w.destroy(d)
3✔
192

3✔
193
        if index < len(d.windows)-1 {
6✔
194
                copy(d.windows[index:], d.windows[index+1:])
3✔
195
        }
3✔
196
        d.windows[len(d.windows)-1] = nil
3✔
197
        d.windows = d.windows[:len(d.windows)-1]
3✔
198

3✔
199
        if len(d.windows) == 0 {
3✔
200
                d.Quit()
×
201
        }
×
202
}
203

204
func (d *gLDriver) repaintWindow(w *window) bool {
43✔
205
        canvas := w.canvas
43✔
206
        freed := false
43✔
207
        if canvas.EnsureMinSize() {
75✔
208
                w.shouldExpand = true
32✔
209
        }
32✔
210
        freed = canvas.FreeDirtyTextures() > 0
43✔
211

43✔
212
        updateGLContext(w)
43✔
213
        canvas.paint(canvas.Size())
43✔
214

43✔
215
        view := w.viewport
43✔
216
        visible := w.visible
43✔
217

43✔
218
        if view != nil && visible {
45✔
219
                view.SwapBuffers()
2✔
220
        }
2✔
221

222
        // mark that we have walked the window and don't
223
        // need to walk it again to mark caches alive
224
        w.lastWalkedTime = time.Now()
43✔
225
        return freed
43✔
226
}
227

228
// refreshWindow requests that the specified window be redrawn
229
func refreshWindow(w *window) {
4✔
230
        w.canvas.SetDirty()
4✔
231
}
4✔
232

233
func updateGLContext(w *window) {
43✔
234
        canvas := w.canvas
43✔
235
        size := canvas.Size()
43✔
236

43✔
237
        // w.width and w.height are not correct if we are maximised, so figure from canvas
43✔
238
        winWidth := float32(scale.ToScreenCoordinate(canvas, size.Width)) * canvas.texScale
43✔
239
        winHeight := float32(scale.ToScreenCoordinate(canvas, size.Height)) * canvas.texScale
43✔
240

43✔
241
        canvas.Painter().SetFrameBufferScale(canvas.texScale)
43✔
242
        canvas.Painter().SetOutputSize(int(winWidth), int(winHeight))
43✔
243
}
43✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc