driver.go 4.2 KB

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