Skip to content

Commit

Permalink
Process events in runloop only when needed
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacalz committed Nov 18, 2023
1 parent 802f92b commit 5a0eead
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 77 deletions.
9 changes: 3 additions & 6 deletions internal/driver/glfw/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type gLDriver struct {
windowLock sync.RWMutex
windows []fyne.Window
device *glDevice
done chan struct{}
mainDone uint32
drawDone chan struct{}
waitForStart chan struct{}

Expand Down Expand Up @@ -105,10 +105,8 @@ func (d *gLDriver) Quit() {
fyne.CurrentApp().Lifecycle().(*intapp.Lifecycle).TriggerExitedForeground()
}

// Only call close once to avoid panic.
if atomic.CompareAndSwapUint32(&running, 1, 0) {
close(d.done)
}
atomic.StoreUint32(&d.mainDone, 1)
postEmptyEvent()
}

func (d *gLDriver) addWindow(w *window) {
Expand Down Expand Up @@ -173,7 +171,6 @@ func NewGLDriver() *gLDriver {
repository.Register("file", intRepo.NewFileRepository())

return &gLDriver{
done: make(chan struct{}),
drawDone: make(chan struct{}),
waitForStart: make(chan struct{}),
animation: &animation.Runner{},
Expand Down
2 changes: 1 addition & 1 deletion internal/driver/glfw/driver_desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (d *gLDriver) CurrentKeyModifiers() fyne.KeyModifier {

func (d *gLDriver) catchTerm() {
terminateSignal := make(chan os.Signal, 1)
signal.Notify(terminateSignal, syscall.SIGINT, syscall.SIGTERM)
signal.Notify(terminateSignal, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)

<-terminateSignal
d.Quit()
Expand Down
139 changes: 75 additions & 64 deletions internal/driver/glfw/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func runOnMain(f func()) {
defer common.DonePool.Put(done)

funcQueue <- funcData{f: f, done: done}
postEmptyEvent()

<-done
}
Expand Down Expand Up @@ -115,87 +116,97 @@ func (d *gLDriver) runGL() {
d.trayStart()
}
fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerStarted()
eventTick := time.NewTicker(time.Second / 60)

postEmptyEvent()

for {
select {
case <-d.done:
eventTick.Stop()
d.drawDone <- struct{}{} // wait for draw thread to stop
d.Terminate()
d.waitForEvents()

if atomic.LoadUint32(&d.mainDone) == 1 {
d.drawDone <- struct{}{} // Tell draw thread to stop.
d.terminate()
fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerStopped()
return
}

select {
case f := <-funcQueue:
f.f()
f.done <- struct{}{}
case <-eventTick.C:
d.tryPollEvents()
windowsToRemove := 0
for _, win := range d.windowList() {
w := win.(*window)
if w.viewport == nil {
continue
}
default: // Do not wait for items to populate in the queue.
}

if w.viewport.ShouldClose() {
windowsToRemove++
continue
}
windowsToRemove := 0
for _, win := range d.windowList() {
w := win.(*window)
if w.viewport == nil {
continue
}

w.viewLock.RLock()
expand := w.shouldExpand
fullScreen := w.fullScreen
w.viewLock.RUnlock()

if expand && !fullScreen {
w.fitContent()
w.viewLock.Lock()
shouldExpand := w.shouldExpand
w.shouldExpand = false
view := w.viewport
w.viewLock.Unlock()
if shouldExpand {
view.SetSize(w.shouldWidth, w.shouldHeight)
}
}
if w.viewport.ShouldClose() {
windowsToRemove++
continue
}

if drawOnMainThread {
d.drawSingleFrame()
w.viewLock.RLock()
expand := w.shouldExpand
fullScreen := w.fullScreen
w.viewLock.RUnlock()

if expand && !fullScreen {
w.fitContent()
w.viewLock.Lock()
shouldExpand := w.shouldExpand
w.shouldExpand = false
view := w.viewport
w.viewLock.Unlock()
if shouldExpand {
view.SetSize(w.shouldWidth, w.shouldHeight)
}
}
if windowsToRemove > 0 {
oldWindows := d.windowList()
newWindows := make([]fyne.Window, 0, len(oldWindows)-windowsToRemove)

for _, win := range oldWindows {
w := win.(*window)
if w.viewport == nil {
continue
}

if w.viewport.ShouldClose() {
w.viewLock.Lock()
w.visible = false
v := w.viewport
w.viewLock.Unlock()
if drawOnMainThread {
d.drawSingleFrame()
}
}

if windowsToRemove > 0 {
d.removeWindows(windowsToRemove)
}
}
}

// remove window from window list
v.Destroy()
w.destroy(d)
continue
}
func (d *gLDriver) removeWindows(windowsToRemove int) {
oldWindows := d.windowList()
newWindows := make([]fyne.Window, 0, len(oldWindows)-windowsToRemove)

newWindows = append(newWindows, win)
}
for _, win := range oldWindows {
w := win.(*window)
if w.viewport == nil {
continue
}

d.windowLock.Lock()
d.windows = newWindows
d.windowLock.Unlock()
if w.viewport.ShouldClose() {
w.viewLock.Lock()
w.visible = false
v := w.viewport
w.viewLock.Unlock()

if len(newWindows) == 0 {
d.Quit()
}
}
// remove window from window list
v.Destroy()
w.destroy(d)
continue
}

newWindows = append(newWindows, win)
}

d.windowLock.Lock()
d.windows = newWindows
d.windowLock.Unlock()

if len(newWindows) == 0 {
d.Quit()
}
}

Expand Down
11 changes: 8 additions & 3 deletions internal/driver/glfw/loop_desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,21 @@ func (d *gLDriver) initGLFW() {
})
}

func (d *gLDriver) tryPollEvents() {
// waitForEvents() will block until one or more events occur.
func (*gLDriver) waitForEvents() {
defer func() {
if r := recover(); r != nil {
fyne.LogError(fmt.Sprint("GLFW poll event error: ", r), nil)
}
}()

glfw.PollEvents() // This call blocks while window is being resized, which prevents freeDirtyTextures from being called
glfw.WaitEvents()
}

func (d *gLDriver) Terminate() {
func postEmptyEvent() {
glfw.PostEmptyEvent()
}

func (*gLDriver) terminate() {
glfw.Terminate()
}
10 changes: 7 additions & 3 deletions internal/driver/glfw/loop_goxjs.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,20 @@ func (d *gLDriver) initGLFW() {
})
}

func (d *gLDriver) tryPollEvents() {
func (*gLDriver) waitForEvents() {
defer func() {
if r := recover(); r != nil {
fyne.LogError(fmt.Sprint("GLFW poll event error: ", r), nil)
}
}()

glfw.PollEvents() // This call blocks while window is being resized, which prevents freeDirtyTextures from being called
glfw.WaitEvents()
}

func (d *gLDriver) Terminate() {
func postEmptyEvent() {
glfw.PostEmptyEvent()
}

func (*gLDriver) terminate() {
glfw.Terminate()
}

0 comments on commit 5a0eead

Please sign in to comment.