aboutsummaryrefslogtreecommitdiffstats
path: root/win
diff options
context:
space:
mode:
authorfaiface <faiface@ksp.sk>2017-08-23 23:31:59 +0200
committerfaiface <faiface@ksp.sk>2017-08-23 23:31:59 +0200
commitbed85864040e8d4fe42805628c84b56a627730cb (patch)
tree3a2085ff0a8dc6cded6470f6dda3ad50088ae06d /win
parentf9580b1ec7d503ae1a38f92bfeae16f081c580e2 (diff)
downloadgui-bed85864040e8d4fe42805628c84b56a627730cb.zip
win: more solid events -> handlers -> flushing concurrency handling
Diffstat (limited to 'win')
-rw-r--r--win/win.go260
1 files changed, 176 insertions, 84 deletions
diff --git a/win/win.go b/win/win.go
index 0b34ee1..4270c11 100644
--- a/win/win.go
+++ b/win/win.go
@@ -3,7 +3,7 @@ package win
import (
"image"
"image/draw"
- "time"
+ "runtime"
"github.com/faiface/gui/event"
"github.com/faiface/mainthread"
@@ -50,7 +50,14 @@ func New(opts ...Option) (*Win, error) {
}
w := &Win{
- closed: make(chan struct{}),
+ events: make(chan struct {
+ event string
+ result chan<- bool
+ }),
+ flushes: make(chan struct {
+ r image.Rectangle
+ flushed chan<- struct{}
+ }),
}
var err error
@@ -61,36 +68,30 @@ func New(opts ...Option) (*Win, error) {
return nil, err
}
- w.mainthreadEvents = make(chan string)
- mainthread.Call(func() {
- w.resize(o.width, o.height)
- w.setUpMainthreadEvents()
- })
+ var (
+ cancelOpenGLThread = make(chan chan struct{})
+ cancelEventHandling = make(chan chan struct{})
+ cancelEventThread = make(chan chan struct{})
+ )
- go func() {
- for {
- select {
- case event := <-w.mainthreadEvents:
- w.Dispatch.Happen(event)
- case <-w.closed:
- return
- }
- }
- }()
+ w.cancels = []chan<- chan struct{}{
+ cancelEventThread,
+ cancelEventHandling,
+ cancelOpenGLThread,
+ }
go func() {
- ticker := time.NewTicker(time.Second / 120)
- defer ticker.Stop()
- for {
- select {
- case <-ticker.C:
- mainthread.Call(glfw.PollEvents)
- case <-w.closed:
- return
- }
- }
+ runtime.LockOSThread()
+ openGLThread(w, cancelOpenGLThread, w.flushes)
}()
+ w.resize(o.width, o.height)
+
+ go eventHandling(w, cancelEventHandling, w.events)
+ mainthread.CallNonBlock(func() {
+ eventThread(w, cancelEventThread)
+ })
+
return w, nil
}
@@ -113,82 +114,173 @@ func makeGLFWWin(o *options) (*glfw.Window, error) {
}
type Win struct {
- event.Dispatch
- w *glfw.Window
- rgba *image.RGBA
- mainthreadEvents chan string
- closed chan struct{}
+ dispatch event.Dispatch
+ w *glfw.Window
+ rgba *image.RGBA
+ events chan struct {
+ event string
+ result chan<- bool
+ }
+ flushes chan struct {
+ r image.Rectangle
+ flushed chan<- struct{}
+ }
+ cancels []chan<- chan struct{}
+}
+
+func (w *Win) Event(pattern string, handler func(evt string) bool) {
+ w.dispatch.Event(pattern, handler)
+}
+
+func (w *Win) Happen(evt string) bool {
+ result := make(chan bool)
+ w.events <- struct {
+ event string
+ result chan<- bool
+ }{evt, result}
+ return <-result
}
func (w *Win) Close() error {
- return mainthread.CallErr(w.close)
+ go func() {
+ for _, cancel := range w.cancels {
+ confirm := make(chan struct{})
+ cancel <- confirm
+ <-confirm
+ }
+ }()
+ return nil
}
func (w *Win) Image() *image.RGBA {
return w.rgba
}
-var curWin *Win = nil
-
func (w *Win) Flush(r image.Rectangle) {
- mainthread.Call(func() {
- w.flush(r)
- })
+ flushed := make(chan struct{})
+ w.flushes <- struct {
+ r image.Rectangle
+ flushed chan<- struct{}
+ }{r, flushed}
+ <-flushed
}
-func (w *Win) flush(r image.Rectangle) {
- if curWin != w {
- w.w.MakeContextCurrent()
- err := gl.Init()
- if err != nil {
- return
- }
- curWin = w
+func (w *Win) resize(width, height int) {
+ bounds := image.Rect(0, 0, width, height)
+ rgba := image.NewRGBA(bounds)
+ if w.rgba != nil {
+ draw.Draw(rgba, w.rgba.Bounds(), w.rgba, w.rgba.Bounds().Min, draw.Src)
}
+ w.rgba = rgba
+ w.Flush(bounds)
+}
- bounds := w.rgba.Bounds()
- r = bounds.Intersect(r)
- if r.Empty() {
- return
+func eventHandling(w *Win, cancel <-chan chan struct{}, events chan struct {
+ event string
+ result chan<- bool
+}) {
+loop:
+ for {
+ select {
+ case evt := <-events:
+ evt.result <- w.dispatch.Happen(evt.event)
+ case confirm := <-cancel:
+ close(confirm)
+ break loop
+ }
}
+}
- tmp := image.NewRGBA(r)
- draw.Draw(tmp, r, w.rgba, r.Min, draw.Src)
+func eventThread(w *Win, cancel <-chan chan struct{}) {
+ var moX, moY int
- gl.DrawBuffer(gl.FRONT)
- gl.Viewport(
- int32(bounds.Min.X),
- int32(bounds.Min.Y),
- int32(bounds.Dx()),
- int32(bounds.Dy()),
- )
- gl.RasterPos2d(
- -1+2*float64(r.Min.X)/float64(bounds.Dx()),
- +1-2*float64(r.Min.Y)/float64(bounds.Dy()),
- )
- gl.PixelZoom(1, -1)
- gl.DrawPixels(
- int32(r.Dx()),
- int32(r.Dy()),
- gl.RGBA,
- gl.UNSIGNED_BYTE,
- gl.Ptr(tmp.Pix),
- )
- gl.Flush()
-}
+ w.w.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
+ switch action {
+ case glfw.Press:
+ w.Happen(event.Sprint("mo", "down", moX, moY))
+ case glfw.Release:
+ w.Happen(event.Sprint("mo", "up", moX, moY))
+ }
+ })
-func (w *Win) close() error {
- close(w.closed)
- w.w.Destroy()
- return nil
+ w.w.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) {
+ moX, moY = int(x), int(y)
+ w.Happen(event.Sprint("mo", "move", moX, moY))
+ })
+
+ w.w.SetCharCallback(func(_ *glfw.Window, r rune) {
+ w.Happen(event.Sprint("kb", "type", r))
+ })
+
+ w.w.SetSizeCallback(func(_ *glfw.Window, width, height int) {
+ w.resize(width, height)
+ w.Happen(event.Sprint("resize", 0, 0, width, height))
+ })
+
+ w.w.SetCloseCallback(func(_ *glfw.Window) {
+ w.Happen(event.Sprint("wi", "close"))
+ })
+
+loop:
+ for {
+ select {
+ default:
+ glfw.WaitEvents()
+ case confirm := <-cancel:
+ close(confirm)
+ break loop
+ }
+ }
}
-func (w *Win) resize(width, height int) {
- bounds := image.Rect(0, 0, width, height)
- rgba := image.NewRGBA(bounds)
- if w.rgba != nil {
- draw.Draw(rgba, w.rgba.Bounds(), w.rgba, w.rgba.Bounds().Min, draw.Src)
+func openGLThread(w *Win, cancel <-chan chan struct{}, flushes <-chan struct {
+ r image.Rectangle
+ flushed chan<- struct{}
+}) {
+ w.w.MakeContextCurrent()
+ gl.Init()
+
+loop:
+ for {
+ select {
+ case flush := <-w.flushes:
+ r := flush.r
+
+ bounds := w.rgba.Bounds()
+ r = bounds.Intersect(r)
+ if r.Empty() {
+ return
+ }
+
+ tmp := image.NewRGBA(r)
+ draw.Draw(tmp, r, w.rgba, r.Min, draw.Src)
+
+ gl.DrawBuffer(gl.FRONT)
+ gl.Viewport(
+ int32(bounds.Min.X),
+ int32(bounds.Min.Y),
+ int32(bounds.Dx()),
+ int32(bounds.Dy()),
+ )
+ gl.RasterPos2d(
+ -1+2*float64(r.Min.X)/float64(bounds.Dx()),
+ +1-2*float64(r.Min.Y)/float64(bounds.Dy()),
+ )
+ gl.PixelZoom(1, -1)
+ gl.DrawPixels(
+ int32(r.Dx()),
+ int32(r.Dy()),
+ gl.RGBA,
+ gl.UNSIGNED_BYTE,
+ gl.Ptr(tmp.Pix),
+ )
+ gl.Flush()
+
+ close(flush.flushed)
+ case confirm := <-cancel:
+ w.w.Destroy()
+ close(confirm)
+ break loop
+ }
}
- w.rgba = rgba
- w.flush(bounds)
}