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.

Fixes: https://todo.sr.ht/~eliasnaur/gio/596
Signed-off-by: Elias Naur <[email protected]>
  • Loading branch information
eliasnaur committed Jun 27, 2024
1 parent 42357a2 commit 4e2d418
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 83 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
24 changes: 0 additions & 24 deletions app/os_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"errors"
"unsafe"

"gioui.org/io/event"
"gioui.org/io/pointer"
)

Expand Down Expand Up @@ -57,35 +56,12 @@ func newWindow(window *callbacks, options []Option) {
errFirst = err
}
}
window.SetDriver(&dummyDriver{
win: window,
wakeups: make(chan event.Event, 1),
})
if errFirst == nil {
errFirst = errors.New("app: no window driver available")
}
window.ProcessEvent(DestroyEvent{Err: errFirst})
}

type dummyDriver struct {
win *callbacks
wakeups chan event.Event
}

func (d *dummyDriver) Event() event.Event {
if e, ok := d.win.nextEvent(); ok {
return e
}
return <-d.wakeups
}

func (d *dummyDriver) Invalidate() {
select {
case d.wakeups <- wakeupEvent{}:
default:
}
}

// xCursor contains mapping from pointer.Cursor to XCursor.
var xCursor = [...]string{
pointer.CursorDefault: "left_ptr",
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
55 changes: 38 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,11 @@ type Window struct {
}
imeState editorState
driver driver
// basic is the driver interface that is needed even after the window is gone.
basic basicDriver

// invMu protects mayInvalidate.
invMu sync.Mutex
mayInvalidate bool

// coalesced tracks the most recent events waiting to be delivered
// to the client.
coalesced eventSummary
Expand Down Expand Up @@ -272,8 +276,11 @@ func (w *Window) updateState() {
//
// Invalidate is safe for concurrent use.
func (w *Window) Invalidate() {
if w.basic != nil {
w.basic.Invalidate()
w.invMu.Lock()
defer w.invMu.Unlock()
if w.mayInvalidate {
w.mayInvalidate = false
w.driver.Invalidate()
}
}

Expand All @@ -283,7 +290,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 +385,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 @@ -549,9 +553,19 @@ 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 }()
return c.w.nextEvent()
}

func (w *Window) nextEvent() (event.Event, bool) {
s := &w.coalesced
mayInvalidate := true
defer func() {
// Every event counts as a wakeup.
s.wakeup = false
w.invMu.Lock()
defer w.invMu.Unlock()
w.mayInvalidate = (w.mayInvalidate || mayInvalidate) && w.driver != nil
}()
switch {
case s.view != nil:
e := *s.view
Expand All @@ -573,6 +587,7 @@ func (c *callbacks) nextEvent() (event.Event, bool) {
case s.wakeup:
return wakeupEvent{}, true
}
mayInvalidate = false
return nil, false
}

Expand Down Expand Up @@ -617,7 +632,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 +702,17 @@ 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()
if w.driver == nil {
e, ok := w.nextEvent()
if !ok {
panic("window initializion failed without a DestroyEvent")
}
return e
}
return w.driver.Event()
}

func (w *Window) init() {
Expand Down Expand Up @@ -832,7 +853,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 4e2d418

Please sign in to comment.