shiny/driver/gldriver: be less racy wrt size events.

Set the windowImpl.sz cached value on the receiver side of the channel
communication, not spawned by a goroutine 'soon' after the send.

This CL only affects x11. The same change applies in principle to cocoa
and win32, but I have not (manually) tested it there yet.

Change-Id: Ibe4bad965fe0c059378daef2f535dcae362447a9
Reviewed-on: https://go-review.googlesource.com/37918
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/shiny/driver/gldriver/cocoa.go b/shiny/driver/gldriver/cocoa.go
index cc01582..8e44d5b 100644
--- a/shiny/driver/gldriver/cocoa.go
+++ b/shiny/driver/gldriver/cocoa.go
@@ -48,6 +48,9 @@
 
 const useLifecycler = true
 
+// TODO: change this to true, after manual testing on OS X.
+const handleSizeEventsAtChannelReceive = false
+
 var initThreadID C.uint64_t
 
 func init() {
@@ -193,9 +196,11 @@
 		PixelsPerPt: ppp,
 	}
 
-	w.szMu.Lock()
-	w.sz = sz
-	w.szMu.Unlock()
+	if !handleSizeEventsAtChannelReceive {
+		w.szMu.Lock()
+		w.sz = sz
+		w.szMu.Unlock()
+	}
 
 	w.Send(sz)
 }
diff --git a/shiny/driver/gldriver/win32.go b/shiny/driver/gldriver/win32.go
index 9e73f71..343fb45 100755
--- a/shiny/driver/gldriver/win32.go
+++ b/shiny/driver/gldriver/win32.go
@@ -23,8 +23,12 @@
 	"golang.org/x/mobile/gl"
 )
 
+// TODO: change this to true, after manual testing on Win32.
 const useLifecycler = false
 
+// TODO: change this to true, after manual testing on Win32.
+const handleSizeEventsAtChannelReceive = false
+
 func main(f func(screen.Screen)) error {
 	return win32.Main(func() { f(theScreen) })
 }
@@ -170,6 +174,7 @@
 		return
 	}
 
+	// TODO: the paint.Event should have External: true.
 	w.Send(paint.Event{})
 }
 
@@ -187,12 +192,18 @@
 		go drawLoop(w)
 	}
 
-	w.szMu.Lock()
-	w.sz = e
-	w.szMu.Unlock()
+	if !handleSizeEventsAtChannelReceive {
+		w.szMu.Lock()
+		w.sz = e
+		w.szMu.Unlock()
+	}
 
 	w.Send(e)
 
+	if handleSizeEventsAtChannelReceive {
+		return
+	}
+
 	// Screen is dirty, generate a paint event.
 	//
 	// The sizeEvent function is called on the goroutine responsible for
diff --git a/shiny/driver/gldriver/window.go b/shiny/driver/gldriver/window.go
index 522fcca..82baf2c 100644
--- a/shiny/driver/gldriver/window.go
+++ b/shiny/driver/gldriver/window.go
@@ -65,6 +65,22 @@
 	sz   size.Event
 }
 
+// NextEvent implements the screen.EventDeque interface.
+func (w *windowImpl) NextEvent() interface{} {
+	e := w.Deque.NextEvent()
+	if handleSizeEventsAtChannelReceive {
+		if sz, ok := e.(size.Event); ok {
+			w.glctxMu.Lock()
+			w.backBufferBound = false
+			w.szMu.Lock()
+			w.sz = sz
+			w.szMu.Unlock()
+			w.glctxMu.Unlock()
+		}
+	}
+	return e
+}
+
 func (w *windowImpl) Release() {
 	// There are two ways a window can be closed: the Operating System or
 	// Desktop Environment can initiate (e.g. in response to a user clicking a
diff --git a/shiny/driver/gldriver/x11.go b/shiny/driver/gldriver/x11.go
index 28ef104..2a3b8f6 100644
--- a/shiny/driver/gldriver/x11.go
+++ b/shiny/driver/gldriver/x11.go
@@ -42,6 +42,8 @@
 
 const useLifecycler = true
 
+const handleSizeEventsAtChannelReceive = true
+
 var theKeysyms x11key.KeysymTable
 
 func init() {
@@ -274,18 +276,6 @@
 		return
 	}
 
-	// TODO: should this really be done on the receiving end of the w.Events()
-	// channel, in the same goroutine as other GL calls in the app's 'business
-	// logic'?
-	go func() {
-		w.glctxMu.Lock()
-		// Force a w.glctx.Viewport call.
-		//
-		// TODO: is this racy? See also the TODO immediately above.
-		w.backBufferBound = false
-		w.glctxMu.Unlock()
-	}()
-
 	w.lifecycler.SetVisible(x+width > 0 && y+height > 0)
 	w.lifecycler.SendEvent(w, w.glctx)
 
@@ -294,19 +284,13 @@
 		ptPerInch = 72
 	)
 	pixelsPerMM := float32(displayWidth) / float32(displayWidthMM)
-	sz := size.Event{
+	w.Send(size.Event{
 		WidthPx:     int(width),
 		HeightPx:    int(height),
 		WidthPt:     geom.Pt(width),
 		HeightPt:    geom.Pt(height),
 		PixelsPerPt: pixelsPerMM * mmPerInch / ptPerInch,
-	}
-
-	w.szMu.Lock()
-	w.sz = sz
-	w.szMu.Unlock()
-
-	w.Send(sz)
+	})
 }
 
 //export onDeleteWindow