preferences.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package app
  2. import (
  3. "encoding/json"
  4. "os"
  5. "path/filepath"
  6. "sync"
  7. "time"
  8. "fyne.io/fyne/v2"
  9. "fyne.io/fyne/v2/internal"
  10. )
  11. type preferences struct {
  12. *internal.InMemoryPreferences
  13. prefLock sync.RWMutex
  14. loadingInProgress bool
  15. savedRecently bool
  16. changedDuringSaving bool
  17. app *fyneApp
  18. }
  19. // Declare conformity with Preferences interface
  20. var _ fyne.Preferences = (*preferences)(nil)
  21. func (p *preferences) resetSavedRecently() {
  22. go func() {
  23. time.Sleep(time.Millisecond * 100) // writes are not always atomic. 10ms worked, 100 is safer.
  24. p.prefLock.Lock()
  25. p.savedRecently = false
  26. changedDuringSaving := p.changedDuringSaving
  27. p.changedDuringSaving = false
  28. p.prefLock.Unlock()
  29. if changedDuringSaving {
  30. p.save()
  31. }
  32. }()
  33. }
  34. func (p *preferences) save() error {
  35. return p.saveToFile(p.storagePath())
  36. }
  37. func (p *preferences) saveToFile(path string) error {
  38. p.prefLock.Lock()
  39. p.savedRecently = true
  40. p.prefLock.Unlock()
  41. defer p.resetSavedRecently()
  42. err := os.MkdirAll(filepath.Dir(path), 0700)
  43. if err != nil { // this is not an exists error according to docs
  44. return err
  45. }
  46. file, err := os.Create(path)
  47. if err != nil {
  48. if !os.IsExist(err) {
  49. return err
  50. }
  51. file, err = os.Open(path) // #nosec
  52. if err != nil {
  53. return err
  54. }
  55. }
  56. defer file.Close()
  57. encode := json.NewEncoder(file)
  58. p.InMemoryPreferences.ReadValues(func(values map[string]interface{}) {
  59. err = encode.Encode(&values)
  60. })
  61. err2 := file.Sync()
  62. if err == nil {
  63. err = err2
  64. }
  65. return err
  66. }
  67. func (p *preferences) load() {
  68. err := p.loadFromFile(p.storagePath())
  69. if err != nil {
  70. fyne.LogError("Preferences load error:", err)
  71. }
  72. }
  73. func (p *preferences) loadFromFile(path string) (err error) {
  74. file, err := os.Open(path) // #nosec
  75. if err != nil {
  76. if os.IsNotExist(err) {
  77. if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
  78. return err
  79. }
  80. return nil
  81. }
  82. return err
  83. }
  84. defer func() {
  85. if r := file.Close(); r != nil && err == nil {
  86. err = r
  87. }
  88. }()
  89. decode := json.NewDecoder(file)
  90. p.prefLock.Lock()
  91. p.loadingInProgress = true
  92. p.prefLock.Unlock()
  93. p.InMemoryPreferences.WriteValues(func(values map[string]interface{}) {
  94. err = decode.Decode(&values)
  95. })
  96. p.prefLock.Lock()
  97. p.loadingInProgress = false
  98. p.prefLock.Unlock()
  99. return err
  100. }
  101. func newPreferences(app *fyneApp) *preferences {
  102. p := &preferences{}
  103. p.app = app
  104. p.InMemoryPreferences = internal.NewInMemoryPreferences()
  105. // don't load or watch if not setup
  106. if app.uniqueID == "" && app.Metadata().ID == "" {
  107. return p
  108. }
  109. p.AddChangeListener(func() {
  110. if p != app.prefs {
  111. return
  112. }
  113. p.prefLock.Lock()
  114. shouldIgnoreChange := p.savedRecently || p.loadingInProgress
  115. if p.savedRecently && !p.loadingInProgress {
  116. p.changedDuringSaving = true
  117. }
  118. p.prefLock.Unlock()
  119. if shouldIgnoreChange { // callback after loading file, or too many updates in a row
  120. return
  121. }
  122. err := p.save()
  123. if err != nil {
  124. fyne.LogError("Failed on saving preferences", err)
  125. }
  126. })
  127. p.watch()
  128. return p
  129. }