Skip to content

Commit

Permalink
app: ignore Invalidate for Windows not yet created
Browse files Browse the repository at this point in the history
While here, don't overflow the Windows event queue.

Signed-off-by: Elias Naur <[email protected]>
  • Loading branch information
eliasnaur committed Jun 27, 2024
1 parent 42357a2 commit 037391a
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 59 deletions.
12 changes: 3 additions & 9 deletions app/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,19 +173,13 @@ type context interface {
Unlock()
}

// basicDriver is the subset of [driver] that may be called even after
// a window is destroyed.
type basicDriver interface {
// driver is the interface for the platform implementation
// of a window.
type driver interface {
// Event blocks until an event is available and returns it.
Event() event.Event
// Invalidate requests a FrameEvent.
Invalidate()
}

// driver is the interface for the platform implementation
// of a window.
type driver interface {
basicDriver
// SetAnimating sets the animation flag. When the window is animating,
// FrameEvents are delivered as fast as the display can handle them.
SetAnimating(anim bool)
Expand Down
12 changes: 1 addition & 11 deletions app/os_wayland.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,6 @@ type window struct {
wakeups chan struct{}

closing bool

// invMu avoids the race between the destruction of disp and
// Invalidate waking it up.
invMu sync.Mutex
}

type poller struct {
Expand Down Expand Up @@ -1369,10 +1365,8 @@ func (w *window) close(err error) {
w.ProcessEvent(WaylandViewEvent{})
w.ProcessEvent(DestroyEvent{Err: err})
w.destroy()
w.invMu.Lock()
w.disp.destroy()
w.disp = nil
w.invMu.Unlock()
}

func (w *window) dispatch() {
Expand Down Expand Up @@ -1416,11 +1410,7 @@ func (w *window) Invalidate() {
default:
return
}
w.invMu.Lock()
defer w.invMu.Unlock()
if w.disp != nil {
w.disp.wakeup()
}
w.disp.wakeup()
}

func (w *window) Run(f func()) {
Expand Down
12 changes: 0 additions & 12 deletions app/os_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ type window struct {
borderSize image.Point
config Config
loop *eventLoop

// invMu avoids the race between destroying the window and Invalidate.
invMu sync.Mutex
}

const _WM_WAKEUP = windows.WM_USER + iota
Expand Down Expand Up @@ -304,10 +301,8 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
windows.ReleaseDC(w.hdc)
w.hdc = 0
}
w.invMu.Lock()
// The system destroys the HWND for us.
w.hwnd = 0
w.invMu.Unlock()
windows.PostQuitMessage(0)
case windows.WM_NCCALCSIZE:
if w.config.Decorated {
Expand Down Expand Up @@ -620,13 +615,6 @@ func (w *window) Frame(frame *op.Ops) {
}

func (w *window) wakeup() {
w.invMu.Lock()
defer w.invMu.Unlock()
if w.hwnd == 0 {
w.loop.Wakeup()
w.loop.FlushEvents()
return
}
if err := windows.PostMessage(w.hwnd, _WM_WAKEUP, 0, 0); err != nil {
panic(err)
}
Expand Down
10 changes: 0 additions & 10 deletions app/os_x11.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,6 @@ type x11Window struct {
wakeups chan struct{}
handler x11EventHandler
buf [100]byte

// invMy avoids the race between destroy and Invalidate.
invMu sync.Mutex
}

var (
Expand Down Expand Up @@ -416,11 +413,6 @@ func (w *x11Window) Invalidate() {
case w.wakeups <- struct{}{}:
default:
}
w.invMu.Lock()
defer w.invMu.Unlock()
if w.x == nil {
return
}
if _, err := syscall.Write(w.notify.write, x11OneByte); err != nil && err != syscall.EAGAIN {
panic(fmt.Errorf("failed to write to pipe: %v", err))
}
Expand Down Expand Up @@ -509,8 +501,6 @@ func (w *x11Window) dispatch() {
}

func (w *x11Window) destroy() {
w.invMu.Lock()
defer w.invMu.Unlock()
if w.notify.write != 0 {
syscall.Close(w.notify.write)
w.notify.write = 0
Expand Down
58 changes: 41 additions & 17 deletions app/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"image/color"
"reflect"
"runtime"
"sync"
"time"
"unicode/utf8"

Expand Down Expand Up @@ -89,8 +90,13 @@ type Window struct {
}
imeState editorState
driver driver
// basic is the driver interface that is needed even after the window is gone.
basic basicDriver

// once initializes pendingInvalidates.
once sync.Once
// pendingInvalidates ensures only one Invalidate may
// be pending at any time.
pendingInvalidates chan struct{}

// coalesced tracks the most recent events waiting to be delivered
// to the client.
coalesced eventSummary
Expand Down Expand Up @@ -272,8 +278,10 @@ func (w *Window) updateState() {
//
// Invalidate is safe for concurrent use.
func (w *Window) Invalidate() {
if w.basic != nil {
w.basic.Invalidate()
select {
case <-w.invalidateChan():
w.driver.Invalidate()
default:
}
}

Expand All @@ -283,7 +291,7 @@ func (w *Window) Option(opts ...Option) {
if len(opts) == 0 {
return
}
if w.basic == nil {
if w.driver == nil {
w.initialOpts = append(w.initialOpts, opts...)
return
}
Expand Down Expand Up @@ -378,11 +386,8 @@ func (w *Window) setNextFrame(at time.Time) {
}
}

func (c *callbacks) SetDriver(d basicDriver) {
c.w.basic = d
if d, ok := d.(driver); ok {
c.w.driver = d
}
func (c *callbacks) SetDriver(d driver) {
c.w.driver = d
}

func (c *callbacks) ProcessFrame(frame *op.Ops, ack chan<- struct{}) {
Expand Down Expand Up @@ -550,8 +555,9 @@ func (c *callbacks) Invalidate() {

func (c *callbacks) nextEvent() (event.Event, bool) {
s := &c.w.coalesced
// Every event counts as a wakeup.
defer func() { s.wakeup = false }()
wakeup := s.wakeup
s.wakeup = false
c.w.invalidateOk()
switch {
case s.view != nil:
e := *s.view
Expand All @@ -561,6 +567,11 @@ func (c *callbacks) nextEvent() (event.Event, bool) {
e := *s.destroy
// Clear pending events after DestroyEvent is delivered.
*s = eventSummary{}
// Don't allow invalidates from now on.
select {
case <-c.w.invalidateChan():
default:
}
return e, true
case s.cfg != nil:
e := *s.cfg
Expand All @@ -570,12 +581,26 @@ func (c *callbacks) nextEvent() (event.Event, bool) {
e := *s.frame
s.frame = nil
return e.FrameEvent, true
case s.wakeup:
case wakeup:
return wakeupEvent{}, true
}
return nil, false
}

func (w *Window) invalidateChan() chan struct{} {
w.once.Do(func() {
w.pendingInvalidates = make(chan struct{}, 1)
})
return w.pendingInvalidates
}

func (w *Window) invalidateOk() {
select {
case w.invalidateChan() <- struct{}{}:
default:
}
}

func (w *Window) processEvent(e event.Event) bool {
switch e2 := e.(type) {
case wakeupEvent:
Expand Down Expand Up @@ -617,7 +642,6 @@ func (w *Window) processEvent(e event.Event) bool {
case DestroyEvent:
w.destroyGPU()
w.driver = nil
w.basic = nil
if q := w.timer.quit; q != nil {
q <- struct{}{}
<-q
Expand Down Expand Up @@ -688,10 +712,10 @@ func (w *Window) processEvent(e event.Event) bool {
// [FrameEvent], or until [Invalidate] is called. The window is created
// and shown the first time Event is called.
func (w *Window) Event() event.Event {
if w.basic == nil {
if w.driver == nil {
w.init()
}
return w.basic.Event()
return w.driver.Event()
}

func (w *Window) init() {
Expand Down Expand Up @@ -832,7 +856,7 @@ func (w *Window) Perform(actions system.Action) {
if acts == 0 {
return
}
if w.basic == nil {
if w.driver == nil {
w.initialActions = append(w.initialActions, acts)
return
}
Expand Down

0 comments on commit 037391a

Please sign in to comment.