driver.go 4.3 KB

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