| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- // 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
- }
- }
|