182 lines
4.0 KiB
Go
182 lines
4.0 KiB
Go
// 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"
|
|
"runtime"
|
|
|
|
"fyne.io/fyne/v2/internal/async"
|
|
"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"
|
|
)
|
|
|
|
var curWindow *window
|
|
|
|
// Declare conformity with Driver
|
|
var _ fyne.Driver = (*gLDriver)(nil)
|
|
|
|
type gLDriver struct {
|
|
windows []fyne.Window
|
|
initialized bool
|
|
done chan struct{}
|
|
|
|
animation animation.Runner
|
|
|
|
currentKeyModifiers fyne.KeyModifier // desktop driver only
|
|
|
|
trayStart, trayStop func() // shut down the system tray, if used
|
|
systrayMenu *fyne.Menu // cache the menu set so we know when to refresh
|
|
}
|
|
|
|
func (d *gLDriver) init() {
|
|
if !d.initialized {
|
|
d.initialized = true
|
|
d.initGLFW()
|
|
}
|
|
}
|
|
|
|
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) DoFromGoroutine(f func(), wait bool) {
|
|
if wait {
|
|
async.EnsureNotMain(func() {
|
|
runOnMainWithWait(f, true)
|
|
})
|
|
} else {
|
|
runOnMainWithWait(f, false)
|
|
}
|
|
}
|
|
|
|
func (d *gLDriver) RenderedTextSize(text string, textSize float32, style fyne.TextStyle, source fyne.Resource) (size fyne.Size, baseline float32) {
|
|
return painter.RenderedTextSize(text, textSize, style, source)
|
|
}
|
|
|
|
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 {
|
|
return &glDevice{}
|
|
}
|
|
|
|
func (d *gLDriver) Quit() {
|
|
if curWindow != nil {
|
|
if f := fyne.CurrentApp().Lifecycle().(*intapp.Lifecycle).OnExitedForeground(); f != nil {
|
|
f()
|
|
}
|
|
curWindow = nil
|
|
if d.trayStop != nil {
|
|
d.trayStop()
|
|
}
|
|
}
|
|
|
|
// Only call close once to avoid panic.
|
|
if running.CompareAndSwap(true, false) {
|
|
close(d.done)
|
|
}
|
|
}
|
|
|
|
func (d *gLDriver) addWindow(w *window) {
|
|
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() {
|
|
var chosen *window
|
|
for _, w := range d.windows {
|
|
win := w.(*window)
|
|
if !win.visible {
|
|
continue
|
|
}
|
|
chosen = win
|
|
if win.master {
|
|
break
|
|
}
|
|
}
|
|
|
|
if chosen == nil || chosen.view() == nil {
|
|
return
|
|
}
|
|
chosen.RequestFocus()
|
|
}
|
|
|
|
func (d *gLDriver) windowList() []fyne.Window {
|
|
return d.windows
|
|
}
|
|
|
|
func (d *gLDriver) initFailed(msg string, err error) {
|
|
logError(msg, err)
|
|
|
|
if !running.Load() {
|
|
d.Quit()
|
|
} else {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func (d *gLDriver) Run() {
|
|
if !async.IsMainGoroutine() {
|
|
panic("Run() or ShowAndRun() must be called from main goroutine")
|
|
}
|
|
|
|
go d.catchTerm()
|
|
d.runGL()
|
|
|
|
// Ensure lifecycle events run to completion before the app exits
|
|
l := fyne.CurrentApp().Lifecycle().(*intapp.Lifecycle)
|
|
l.WaitForEvents()
|
|
l.DestroyEventQueue()
|
|
}
|
|
|
|
func (d *gLDriver) SetDisableScreenBlanking(disable bool) {
|
|
setDisableScreenBlank(disable)
|
|
}
|
|
|
|
// NewGLDriver sets up a new Driver instance implemented using the GLFW Go library and OpenGL bindings.
|
|
func NewGLDriver() *gLDriver {
|
|
repository.Register("file", intRepo.NewFileRepository())
|
|
|
|
return &gLDriver{
|
|
done: make(chan struct{}),
|
|
}
|
|
}
|