/
driver.go
147 lines (120 loc) · 3.31 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
// 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 (
"os"
"runtime"
"strconv"
"strings"
"sync"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/internal/animation"
"fyne.io/fyne/v2/internal/driver"
"fyne.io/fyne/v2/internal/painter"
intRepo "fyne.io/fyne/v2/internal/repository"
"fyne.io/fyne/v2/storage/repository"
)
const mainGoroutineID = 1
var (
canvasMutex sync.RWMutex
canvases = make(map[fyne.CanvasObject]fyne.Canvas)
isWayland = false
)
// Declare conformity with Driver
var _ fyne.Driver = (*gLDriver)(nil)
type gLDriver struct {
windowLock sync.RWMutex
windows []fyne.Window
device *glDevice
done chan interface{}
drawDone chan interface{}
animation *animation.Runner
}
func (d *gLDriver) RenderedTextSize(text string, size float32, style fyne.TextStyle) fyne.Size {
return painter.RenderedTextSize(text, size, style)
}
func (d *gLDriver) CanvasForObject(obj fyne.CanvasObject) fyne.Canvas {
canvasMutex.RLock()
defer canvasMutex.RUnlock()
return canvases[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() {
defer func() {
recover() // we could be called twice - no safe way to check if d.done is closed
}()
close(d.done)
}
func (d *gLDriver) Run() {
if goroutineID() != mainGoroutineID {
panic("Run() or ShowAndRun() must be called from main goroutine")
}
d.runGL()
}
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 {
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) {
fyne.LogError(msg, err)
if running() {
d.Quit()
} else {
os.Exit(1)
}
}
func goroutineID() int {
b := make([]byte, 64)
b = b[:runtime.Stack(b, false)]
// string format expects "goroutine X [running..."
id := strings.Split(strings.TrimSpace(string(b)), " ")[1]
num, _ := strconv.Atoi(id)
return num
}
// 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
}