-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
driver.go
192 lines (158 loc) · 4.32 KB
/
driver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// Package glfw provides a full Fyne desktop driver that uses the system OpenGL libraries.
// This supports Windows, Mac OS X and Linux using the gl and glfw packages from go-gl.
package glfw
import (
"bytes"
"image"
"os"
"os/signal"
"runtime"
"sync"
"syscall"
"github.com/fyne-io/image/ico"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/internal/animation"
intapp "fyne.io/fyne/v2/internal/app"
"fyne.io/fyne/v2/internal/driver"
"fyne.io/fyne/v2/internal/driver/common"
"fyne.io/fyne/v2/internal/painter"
intRepo "fyne.io/fyne/v2/internal/repository"
"fyne.io/fyne/v2/storage/repository"
)
// mainGoroutineID stores the main goroutine ID.
// This ID must be initialized in main.init because
// a main goroutine may not equal to 1 due to the
// influence of a garbage collector.
var mainGoroutineID uint64
var (
curWindow *window
isWayland = false
)
// Declare conformity with Driver
var _ fyne.Driver = (*gLDriver)(nil)
var drawOnMainThread bool // A workaround on Apple M1, just use 1 thread until fixed upstream
type gLDriver struct {
windowLock sync.RWMutex
windows []fyne.Window
device *glDevice
done chan interface{}
drawDone chan interface{}
animation *animation.Runner
trayStart, trayStop func() // shut down the system tray, if used
systrayMenu *fyne.Menu // cache the menu set so we know when to refresh
}
func toOSIcon(icon []byte) ([]byte, error) {
if runtime.GOOS != "windows" {
return icon, nil
}
img, _, err := image.Decode(bytes.NewReader(icon))
if err != nil {
return nil, err
}
buf := &bytes.Buffer{}
err = ico.Encode(buf, img)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (d *gLDriver) RenderedTextSize(text string, textSize float32, style fyne.TextStyle) (size fyne.Size, baseline float32) {
return painter.RenderedTextSize(text, textSize, style)
}
func (d *gLDriver) CanvasForObject(obj fyne.CanvasObject) fyne.Canvas {
return common.CanvasForObject(obj)
}
func (d *gLDriver) AbsolutePositionForObject(co fyne.CanvasObject) fyne.Position {
c := d.CanvasForObject(co)
if c == nil {
return fyne.NewPos(0, 0)
}
glc := c.(*glCanvas)
return driver.AbsolutePositionForObject(co, glc.ObjectTrees())
}
func (d *gLDriver) Device() fyne.Device {
if d.device == nil {
d.device = &glDevice{}
}
return d.device
}
func (d *gLDriver) Quit() {
if curWindow != nil {
curWindow = nil
if d.trayStop != nil {
d.trayStop()
}
fyne.CurrentApp().Lifecycle().(*intapp.Lifecycle).TriggerExitedForeground()
}
defer func() {
recover() // we could be called twice - no safe way to check if d.done is closed
}()
close(d.done)
}
func (d *gLDriver) addWindow(w *window) {
d.windowLock.Lock()
defer d.windowLock.Unlock()
d.windows = append(d.windows, w)
}
// a trivial implementation of "focus previous" - return to the most recently opened, or master if set.
// This may not do the right thing if your app has 3 or more windows open, but it was agreed this was not much
// of an issue, and the added complexity to track focus was not needed at this time.
func (d *gLDriver) focusPreviousWindow() {
d.windowLock.RLock()
wins := d.windows
d.windowLock.RUnlock()
var chosen fyne.Window
for _, w := range wins {
if !w.(*window).visible {
continue
}
chosen = w
if w.(*window).master {
break
}
}
if chosen == nil || chosen.(*window).view() == nil {
return
}
chosen.RequestFocus()
}
func (d *gLDriver) windowList() []fyne.Window {
d.windowLock.RLock()
defer d.windowLock.RUnlock()
return d.windows
}
func (d *gLDriver) initFailed(msg string, err error) {
logError(msg, err)
run.Lock()
if !run.flag {
run.Unlock()
d.Quit()
} else {
run.Unlock()
os.Exit(1)
}
}
func (d *gLDriver) Run() {
if goroutineID() != mainGoroutineID {
panic("Run() or ShowAndRun() must be called from main goroutine")
}
go catchTerm(d)
d.runGL()
}
// NewGLDriver sets up a new Driver instance implemented using the GLFW Go library and OpenGL bindings.
func NewGLDriver() fyne.Driver {
d := new(gLDriver)
d.done = make(chan interface{})
d.drawDone = make(chan interface{})
d.animation = &animation.Runner{}
repository.Register("file", intRepo.NewFileRepository())
return d
}
func catchTerm(d *gLDriver) {
terminateSignals := make(chan os.Signal, 1)
signal.Notify(terminateSignals, syscall.SIGINT, syscall.SIGTERM)
for range terminateSignals {
d.Quit()
break
}
}