adam-gui/vendor/fyne.io/fyne/v2/internal/driver/glfw/driver.go

182 lines
4.2 KiB
Go
Raw Normal View History

2024-04-29 19:13:50 +02:00
// 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"
"sync"
"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
// Declare conformity with Driver
var _ fyne.Driver = (*gLDriver)(nil)
// A workaround on Apple M1/M2, just use 1 thread until fixed upstream.
const drawOnMainThread bool = runtime.GOOS == "darwin" && runtime.GOARCH == "arm64"
type gLDriver struct {
windowLock sync.RWMutex
windows []fyne.Window
device *glDevice
done chan interface{}
drawDone chan interface{}
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
systrayLock sync.Mutex
}
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.L.Lock()
running := !run.flag
run.L.Unlock()
if running {
d.Quit()
} else {
os.Exit(1)
}
}
func (d *gLDriver) Run() {
if goroutineID() != mainGoroutineID {
panic("Run() or ShowAndRun() must be called from main goroutine")
}
go d.catchTerm()
d.runGL()
}
// NewGLDriver sets up a new Driver instance implemented using the GLFW Go library and OpenGL bindings.
func NewGLDriver() fyne.Driver {
repository.Register("file", intRepo.NewFileRepository())
return &gLDriver{
done: make(chan interface{}),
drawDone: make(chan interface{}),
animation: &animation.Runner{},
}
}