aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfaiface <faiface@ksp.sk>2017-08-29 00:35:28 +0200
committerfaiface <faiface@ksp.sk>2017-08-29 00:35:28 +0200
commit09db3d0b721685fda1cee71833ec4dd0fe1b4c9a (patch)
tree7473dfeb9d1b4664c25be1e7cc939a804855c650
parent0c433c89a218c428cbc6dc093d4b4d6dd8174114 (diff)
downloadgui-09db3d0b721685fda1cee71833ec4dd0fe1b4c9a.zip
start over, concurrency full on
-rw-r--r--event/dispatch.go91
-rw-r--r--layout/interface.go40
-rw-r--r--layout/layer.go118
-rw-r--r--layout/split.go163
-rw-r--r--win/win.go187
5 files changed, 99 insertions, 500 deletions
diff --git a/event/dispatch.go b/event/dispatch.go
deleted file mode 100644
index 738161a..0000000
--- a/event/dispatch.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package event
-
-import (
- "bytes"
- "fmt"
- "strings"
- "sync"
-)
-
-const Sep = "/"
-
-func Sprint(a ...interface{}) string {
- var buf bytes.Buffer
- for i := range a {
- if i > 0 {
- buf.WriteString(Sep)
- }
- fmt.Fprint(&buf, a[i])
- }
- return buf.String()
-}
-
-func Sscan(event string, a ...interface{}) {
- for i, part := range strings.Split(event, Sep) {
- fmt.Sscan(part, a[i])
- }
-}
-
-type Dispatch struct {
- mu sync.Mutex
- handlers []func(event string) bool
- trie map[string]*Dispatch
-}
-
-func (d *Dispatch) Event(pattern string, handler func(event string) bool) {
- if pattern == "" {
- d.event(nil, handler)
- return
- }
- d.event(strings.Split(pattern, Sep), handler)
-}
-
-func (d *Dispatch) Happen(event string) bool {
- if event == "" {
- return d.happen(nil)
- }
- return d.happen(strings.Split(event, Sep))
-}
-
-func (d *Dispatch) event(pattern []string, handler func(event string) bool) {
- d.mu.Lock()
- defer d.mu.Unlock()
-
- if len(pattern) == 0 {
- d.handlers = append(d.handlers, handler)
- return
- }
-
- if d.trie == nil {
- d.trie = make(map[string]*Dispatch)
- }
- if d.trie[pattern[0]] == nil {
- d.trie[pattern[0]] = &Dispatch{}
- }
- d.trie[pattern[0]].event(pattern[1:], handler)
-}
-
-func (d *Dispatch) happen(event []string) bool {
- d.mu.Lock()
- handlers := d.handlers
- d.mu.Unlock()
-
- for _, handler := range handlers {
- if handler(strings.Join(event, Sep)) {
- return true
- }
- }
-
- if len(event) == 0 {
- return false
- }
-
- d.mu.Lock()
- next := d.trie[event[0]]
- d.mu.Unlock()
-
- if next != nil {
- return next.happen(event[1:])
- }
- return false
-}
diff --git a/layout/interface.go b/layout/interface.go
new file mode 100644
index 0000000..1dcf34a
--- /dev/null
+++ b/layout/interface.go
@@ -0,0 +1,40 @@
+package layout
+
+import (
+ "fmt"
+ "image"
+ "image/draw"
+)
+
+type EventDrawer interface {
+ Event() <-chan EventConsume
+ Draw() chan<- ImageFlush
+}
+
+type EventConsume struct {
+ Event string
+ Consume chan<- bool
+}
+
+func SendEvent(ch chan<- EventConsume, format string, a ...interface{}) (consume <-chan bool) {
+ cons := make(chan bool)
+ ch <- EventConsume{fmt.Sprintf(format, a...), cons}
+ return cons
+}
+
+func (ec EventConsume) Matches(format string, a ...interface{}) bool {
+ _, err := fmt.Sscanf(ec.Event, format, a...)
+ return err == nil
+}
+
+type ImageFlush struct {
+ Image chan<- draw.Image
+ Flush <-chan image.Rectangle
+}
+
+func SendDraw(ch chan<- ImageFlush) (img <-chan draw.Image, flush chan<- image.Rectangle) {
+ imgC := make(chan draw.Image)
+ flushC := make(chan image.Rectangle)
+ ch <- ImageFlush{imgC, flushC}
+ return imgC, flushC
+}
diff --git a/layout/layer.go b/layout/layer.go
deleted file mode 100644
index 42000a5..0000000
--- a/layout/layer.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package layout
-
-import (
- "container/list"
- "errors"
- "image"
- "image/draw"
-
- "github.com/faiface/gui/event"
-)
-
-type EventImageFlusher interface {
- Event(pattern string, handler func(event string) bool)
- Image() *image.RGBA
- Flush(r image.Rectangle)
-}
-
-type LayerList struct {
- event.Dispatch
- dst EventImageFlusher
- layers list.List
-}
-
-func NewLayerList(dst EventImageFlusher) *LayerList {
- l := &LayerList{dst: dst}
-
- dst.Event("", l.Happen)
-
- l.Event("resize", func(evt string) bool {
- var x1, y1, x2, y2 int
- event.Sscan(evt, &x1, &y1, &x2, &y2)
-
- for e := l.layers.Back(); e != nil; e = e.Prev() {
- layer := e.Value.(*Layer)
- rgba := image.NewRGBA(dst.Image().Bounds())
- draw.Draw(rgba, layer.rgba.Bounds(), layer.rgba, layer.rgba.Bounds().Min, draw.Src)
- layer.rgba = rgba
- }
-
- return false
- })
-
- r := dst.Image().Bounds()
- l.Happen(event.Sprint("resize", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y))
-
- return l
-}
-
-func (l *LayerList) Add() *Layer {
- layer := &Layer{
- lst: l,
- rgba: image.NewRGBA(l.dst.Image().Bounds()),
- }
- layer.elm = l.layers.PushFront(layer)
- return layer
-}
-
-func (l *LayerList) Remove(layer *Layer) {
- if layer.lst == nil {
- panic(errors.New("layer: Remove: layer already removed"))
- }
- l.layers.Remove(layer.elm)
- layer.lst = nil
-}
-
-func (l *LayerList) Front(layer *Layer) {
- if layer.lst == nil {
- panic(errors.New("layer: Front: layer removed"))
- }
- l.layers.MoveToFront(layer.elm)
-}
-
-func (l *LayerList) Happen(event string) bool {
- if l.Dispatch.Happen(event) {
- return true
- }
- for e := l.layers.Front(); e != nil; e = e.Next() {
- layer := e.Value.(*Layer)
- if layer.Happen(event) {
- return true
- }
- }
- return false
-}
-
-func (l *LayerList) Flush(r image.Rectangle) {
- if l.dst == nil {
- panic(errors.New("layer: Flush: no destination"))
- }
- draw.Draw(l.dst.Image(), r, image.Transparent, r.Min, draw.Src)
- for e := l.layers.Back(); e != nil; e = e.Prev() {
- layer := e.Value.(*Layer)
- draw.Draw(l.dst.Image(), r, layer.rgba, r.Min, draw.Over)
- }
- l.dst.Flush(r)
-}
-
-type Layer struct {
- event.Dispatch
- lst *LayerList
- elm *list.Element
- rgba *image.RGBA
-}
-
-func (l *Layer) List() *LayerList {
- return l.lst
-}
-
-func (l *Layer) Image() *image.RGBA {
- return l.rgba
-}
-
-func (l *Layer) Flush(r image.Rectangle) {
- if l.lst == nil {
- panic(errors.New("layer: Flush: layer removed"))
- }
- l.lst.Flush(r)
-}
diff --git a/layout/split.go b/layout/split.go
deleted file mode 100644
index 90c4a0a..0000000
--- a/layout/split.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package layout
-
-import (
- "image"
-
- "github.com/faiface/gui/event"
-)
-
-type Box struct {
- event.Dispatch
- dst EventImageFlusher
- sub *image.RGBA
-}
-
-func (b *Box) Image() *image.RGBA {
- return b.sub
-}
-
-func (b *Box) Flush(r image.Rectangle) {
- r = r.Intersect(b.sub.Bounds())
- b.dst.Flush(r)
-}
-
-type Splitter func(r image.Rectangle, i, n int) image.Rectangle
-
-func Vertical() Splitter {
- return func(r image.Rectangle, i, n int) image.Rectangle {
- width := r.Dx()
- return image.Rect(
- r.Min.X+width*i/n,
- r.Min.Y,
- r.Min.X+width*(i+1)/n,
- r.Max.Y,
- )
- }
-}
-
-func Horizontal() Splitter {
- return func(r image.Rectangle, i, n int) image.Rectangle {
- height := r.Dy()
- return image.Rect(
- r.Min.X,
- r.Min.Y+height*i/n,
- r.Max.X,
- r.Min.Y+height*(i+1)/n,
- )
- }
-}
-
-func FixedTop(thickness int, rest Splitter) Splitter {
- return VariableTop(&thickness, rest)
-}
-
-func FixedBottom(thickness int, rest Splitter) Splitter {
- return VariableBottom(&thickness, rest)
-}
-
-func FixedLeft(thickness int, rest Splitter) Splitter {
- return VariableLeft(&thickness, rest)
-}
-
-func FixedRight(thickness int, rest Splitter) Splitter {
- return VariableRight(&thickness, rest)
-}
-
-func VariableTop(thickness *int, rest Splitter) Splitter {
- return func(r image.Rectangle, i, n int) image.Rectangle {
- if i == 0 {
- r.Max.Y = r.Min.Y + *thickness
- return r
- }
- r.Min.Y += *thickness
- return rest(r, i-1, n-1)
- }
-}
-
-func VariableBottom(thickness *int, rest Splitter) Splitter {
- return func(r image.Rectangle, i, n int) image.Rectangle {
- if i == 0 {
- r.Min.Y = r.Max.Y - *thickness
- return r
- }
- r.Max.Y -= *thickness
- return rest(r, i-1, n-1)
- }
-}
-
-func VariableLeft(thickness *int, rest Splitter) Splitter {
- return func(r image.Rectangle, i, n int) image.Rectangle {
- if i == 0 {
- r.Max.X = r.Min.X + *thickness
- return r
- }
- r.Min.X += *thickness
- return rest(r, i-1, n-1)
- }
-}
-
-func VariableRight(thickness *int, rest Splitter) Splitter {
- return func(r image.Rectangle, i, n int) image.Rectangle {
- if i == 0 {
- r.Min.X = r.Max.X - *thickness
- return r
- }
- r.Max.X -= *thickness
- return rest(r, i-1, n-1)
- }
-}
-
-type Split struct {
- event.Dispatch
- dst EventImageFlusher
- splitter Splitter
- boxes []*Box
-}
-
-func NewSplit(dst EventImageFlusher, splitter Splitter) *Split {
- s := &Split{
- dst: dst,
- splitter: splitter,
- }
- s.Event("resize", func(string) bool {
- s.Split()
- s.dst.Flush(s.dst.Image().Bounds())
- return true
- })
- dst.Event("", s.Happen)
- return s
-}
-
-func (s *Split) Happen(evt string) bool {
- if s.Dispatch.Happen(evt) {
- return true
- }
- for _, box := range s.boxes {
- if box.Happen(evt) {
- return true
- }
- }
- return false
-}
-
-func (s *Split) Add() *Box {
- box := &Box{
- dst: s.dst,
- }
- s.boxes = append(s.boxes, box)
- s.Split()
- return box
-}
-
-func (s *Split) Split() {
- r := s.dst.Image().Bounds()
- n := len(s.boxes)
- for i := range s.boxes {
- subR := s.splitter(r, i, n)
- s.boxes[i].sub = s.dst.Image().SubImage(subR).(*image.RGBA)
- }
- for _, box := range s.boxes {
- subR := box.sub.Bounds()
- box.Happen(event.Sprint("resize", subR.Min.X, subR.Min.Y, subR.Max.X, subR.Max.Y))
- }
-}
diff --git a/win/win.go b/win/win.go
index 8b5f917..ccecc94 100644
--- a/win/win.go
+++ b/win/win.go
@@ -6,7 +6,7 @@ import (
"runtime"
"time"
- "github.com/faiface/gui/event"
+ "github.com/faiface/gui/layout"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v2.1/gl"
"github.com/go-gl/glfw/v3.2/glfw"
@@ -51,14 +51,10 @@ func New(opts ...Option) (*Win, error) {
}
w := &Win{
- events: make(chan struct {
- event string
- result chan<- bool
- }),
- flushes: make(chan struct {
- r image.Rectangle
- flushed chan<- struct{}
- }),
+ event: make(chan layout.EventConsume),
+ draw: make(chan layout.ImageFlush),
+ newSize: make(chan image.Rectangle),
+ finish: make(chan struct{}),
}
var err error
@@ -69,29 +65,15 @@ func New(opts ...Option) (*Win, error) {
return nil, err
}
- var (
- cancelOpenGLThread = make(chan chan<- struct{})
- cancelEventDispatch = make(chan chan<- struct{})
- cancelEventThread = make(chan chan<- struct{})
- )
-
- w.cancels = []chan<- chan<- struct{}{
- cancelEventThread,
- cancelEventDispatch,
- cancelOpenGLThread,
- }
+ bounds := image.Rect(0, 0, o.width, o.height)
+ w.img = image.NewRGBA(bounds)
go func() {
runtime.LockOSThread()
- openGLThread(w, cancelOpenGLThread, w.flushes)
+ w.openGLThread()
}()
- w.resize(o.width, o.height)
-
- go eventDispatch(w, cancelEventDispatch, w.events)
- mainthread.CallNonBlock(func() {
- eventThread(w, cancelEventThread)
- })
+ mainthread.CallNonBlock(w.eventThread)
return w, nil
}
@@ -115,135 +97,79 @@ func makeGLFWWin(o *options) (*glfw.Window, error) {
}
type Win struct {
- dispatch event.Dispatch
- w *glfw.Window
- front *image.RGBA
- back *image.RGBA
- events chan struct {
- event string
- result chan<- bool
- }
- flushes chan struct {
- r image.Rectangle
- flushed chan<- struct{}
- }
- cancels []chan<- chan<- struct{}
-}
+ event chan layout.EventConsume
+ draw chan layout.ImageFlush
-func (w *Win) Event(pattern string, handler func(evt string) bool) {
- w.dispatch.Event(pattern, handler)
-}
+ newSize chan image.Rectangle
+ finish chan struct{}
-func (w *Win) Happen(evt string) bool {
- result := make(chan bool)
- w.events <- struct {
- event string
- result chan<- bool
- }{evt, result}
- return <-result
+ w *glfw.Window
+ img *image.RGBA
}
-func (w *Win) Close() error {
+func (w *Win) Cancel() {
go func() {
- for _, cancel := range w.cancels {
- confirm := make(chan struct{})
- cancel <- confirm
- <-confirm
- }
+ <-layout.SendEvent(w.event, "cancel")
+ <-layout.SendEvent(w.event, "return")
+ close(w.event)
+ close(w.finish)
}()
- return nil
}
-func (w *Win) Image() *image.RGBA {
- return w.front
+func (w *Win) Event() <-chan layout.EventConsume {
+ return w.event
}
-func (w *Win) Flush(r image.Rectangle) {
- flushed := make(chan struct{})
- w.flushes <- struct {
- r image.Rectangle
- flushed chan<- struct{}
- }{r, flushed}
- <-flushed
+func (w *Win) Draw() chan<- layout.ImageFlush {
+ return w.draw
}
-func (w *Win) resize(width, height int) {
- bounds := image.Rect(0, 0, width, height)
- front := image.NewRGBA(bounds)
- if w.front != nil {
- draw.Draw(front, w.front.Bounds(), w.front, w.front.Bounds().Min, draw.Src)
- }
- w.front = front
- back := image.NewRGBA(bounds)
- if w.back != nil {
- draw.Draw(back, w.back.Bounds(), w.back, w.back.Bounds().Min, draw.Src)
- }
- w.back = back
- w.Flush(bounds)
-}
-
-func eventDispatch(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
- }
- }
-}
-
-func eventThread(w *Win, cancel <-chan chan<- struct{}) {
+func (w *Win) eventThread() {
var moX, moY int
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))
+ <-layout.SendEvent(w.event, "mo/down/%d/%d", moX, moY)
case glfw.Release:
- w.Happen(event.Sprint("mo", "up", moX, moY))
+ <-layout.SendEvent(w.event, "mo/up/%d/%d", moX, moY)
}
})
w.w.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) {
moX, moY = int(x), int(y)
- w.Happen(event.Sprint("mo", "move", moX, moY))
+ <-layout.SendEvent(w.event, "mo/move/%d/%d", moX, moY)
})
w.w.SetCharCallback(func(_ *glfw.Window, r rune) {
- w.Happen(event.Sprint("kb", "type", r))
+ <-layout.SendEvent(w.event, "kb/type/%d", r)
})
w.w.SetSizeCallback(func(_ *glfw.Window, width, height int) {
- w.resize(width, height)
- w.Happen(event.Sprint("resize", 0, 0, width, height))
+ r := image.Rect(0, 0, width, height)
+ w.newSize <- r
+ <-layout.SendEvent(w.event, "resize/%d/%d/%d/%d", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)
})
w.w.SetCloseCallback(func(_ *glfw.Window) {
- w.Happen(event.Sprint("wi", "close"))
+ <-layout.SendEvent(w.event, "wi/close")
})
-loop:
+ r := w.img.Bounds()
+ <-layout.SendEvent(w.event, "resize/%d/%d/%d/%d", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)
+
for {
select {
+ case <-w.finish:
+ w.w.Destroy()
+ return
default:
- glfw.WaitEvents()
- case confirm := <-cancel:
- close(confirm)
- break loop
+ glfw.WaitEventsTimeout(1.0 / 30)
}
}
}
-func openGLThread(w *Win, cancel <-chan chan<- struct{}, flushes <-chan struct {
- r image.Rectangle
- flushed chan<- struct{}
-}) {
+func (w *Win) openGLThread() {
w.w.MakeContextCurrent()
gl.Init()
@@ -253,25 +179,34 @@ func openGLThread(w *Win, cancel <-chan chan<- struct{}, flushes <-chan struct {
)
defer openGLFlush.Stop()
-loop:
for {
select {
- case flush := <-w.flushes:
- draw.Draw(w.back, flush.r, w.front, flush.r.Min, draw.Src)
- openGLFlushR = openGLFlushR.Union(flush.r)
- close(flush.flushed)
+ case <-w.finish:
+ return
+
+ case r := <-w.newSize:
+ img := image.NewRGBA(r)
+ draw.Draw(img, w.img.Bounds(), w.img, w.img.Bounds().Min, draw.Src)
+ w.img = img
+ openGLFlushR = r
+
+ case imfl := <-w.draw:
+ imfl.Image <- w.img
+ r := <-imfl.Flush
+ openGLFlushR = openGLFlushR.Union(r)
+
case <-openGLFlush.C:
r := openGLFlushR
+ openGLFlushR = image.Rectangle{}
- back := w.back
- bounds := back.Bounds()
+ bounds := w.img.Bounds()
r = r.Intersect(bounds)
if r.Empty() {
- return
+ continue
}
tmp := image.NewRGBA(r)
- draw.Draw(tmp, r, back, r.Min, draw.Src)
+ draw.Draw(tmp, r, w.img, r.Min, draw.Src)
gl.DrawBuffer(gl.FRONT)
gl.Viewport(
@@ -293,10 +228,6 @@ loop:
gl.Ptr(tmp.Pix),
)
gl.Flush()
- case confirm := <-cancel:
- w.w.Destroy()
- close(confirm)
- break loop
}
}
}