aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--env.go12
-rw-r--r--event.go46
-rw-r--r--go.mod2
-rw-r--r--gui_test.go15
-rw-r--r--layout_test.go4
-rw-r--r--mux.go17
-rw-r--r--mux_test.go2
-rw-r--r--win.go43
8 files changed, 47 insertions, 94 deletions
diff --git a/env.go b/env.go
index f5f31cf..4094846 100644
--- a/env.go
+++ b/env.go
@@ -3,6 +3,8 @@ package gui
import (
"image"
"image/draw"
+
+ "git.samanthony.xyz/share"
)
// Env is the most important thing in this package. It is an interactive graphical
@@ -15,7 +17,7 @@ import (
//
// An Env guarantees to produce a "resize/<x0>/<y0>/<x1>/<y1>" event as its first event.
//
-// The Events() channel must be unlimited in capacity. Use makeEventsChan() to create
+// The Events() channel must be unlimited in capacity. Use share.Queue to create
// a channel of events with an unlimited capacity.
//
// The Draw() channel may be synchronous.
@@ -53,7 +55,7 @@ func newEnv(parent Env,
filterDraws func(func(draw.Image) image.Rectangle, chan<- func(draw.Image) image.Rectangle),
shutdown func(),
) Env {
- eventsOut, eventsIn := makeEventsChan()
+ events := share.NewQueue[Event]()
drawChan := make(chan func(draw.Image) image.Rectangle)
child := newAttachHandler()
kill := make(chan bool)
@@ -70,7 +72,7 @@ func newEnv(parent Env,
close(detachFromParent)
}()
defer shutdown()
- defer close(eventsIn)
+ defer close(events.Enqueue)
defer close(drawChan)
defer close(kill)
defer func() {
@@ -82,7 +84,7 @@ func newEnv(parent Env,
for {
select {
case e := <-parent.Events():
- filterEvents(e, eventsIn)
+ filterEvents(e, events.Enqueue)
case d := <-drawChan:
filterDraws(d, parent.Draw())
case <-kill:
@@ -92,7 +94,7 @@ func newEnv(parent Env,
}()
e := env{
- events: eventsOut,
+ events: events.Dequeue,
draw: drawChan,
attachChan: child.attach(),
kill: kill,
diff --git a/event.go b/event.go
index eff095b..7954151 100644
--- a/event.go
+++ b/event.go
@@ -23,52 +23,6 @@ func (r Resize) String() string {
return fmt.Sprintf("resize/%d/%d/%d/%d", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)
}
-// makeEventsChan implements a channel of events with an unlimited capacity. It does so
-// by creating a goroutine that queues incoming events. Sending to this channel never blocks
-// and no events get lost.
-//
-// The unlimited capacity channel is very suitable for delivering events because the consumer
-// may be unavailable for some time (doing a heavy computation), but will get to the events
-// later.
-//
-// An unlimited capacity channel has its dangers in general, but is completely fine for
-// the purpose of delivering events. This is because the production of events is fairly
-// infrequent and should never out-run their consumption in the long term.
-func makeEventsChan() (<-chan Event, chan<- Event) {
- out, in := make(chan Event), make(chan Event)
-
- go func() {
- var queue []Event
-
- for {
- x, ok := <-in
- if !ok {
- close(out)
- return
- }
- queue = append(queue, x)
-
- for len(queue) > 0 {
- select {
- case out <- queue[0]:
- queue = queue[1:]
- case x, ok := <-in:
- if !ok {
- for _, x := range queue {
- out <- x
- }
- close(out)
- return
- }
- queue = append(queue, x)
- }
- }
- }
- }()
-
- return out, in
-}
-
// Button indicates a mouse button in an event.
type Button string
diff --git a/go.mod b/go.mod
index 758b712..7d10dfb 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/faiface/gui
go 1.22.4
require (
- git.samanthony.xyz/share v0.0.0
+ git.samanthony.xyz/share v0.2.0
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3
github.com/fogleman/gg v1.3.0
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71
diff --git a/gui_test.go b/gui_test.go
index 0d18c92..c8e685e 100644
--- a/gui_test.go
+++ b/gui_test.go
@@ -4,6 +4,8 @@ import (
"image"
"image/draw"
"time"
+
+ "git.samanthony.xyz/share"
)
const timeout = 1 * time.Second
@@ -31,8 +33,7 @@ func tryRecv[T any](c <-chan T, timeout time.Duration) (*T, bool) {
}
type dummyEnv struct {
- eventsIn chan<- Event
- eventsOut <-chan Event
+ events share.Queue[Event]
drawIn chan<- func(draw.Image) image.Rectangle
drawOut <-chan func(draw.Image) image.Rectangle
@@ -44,7 +45,7 @@ type dummyEnv struct {
}
func newDummyEnv(size image.Rectangle) dummyEnv {
- eventsOut, eventsIn := makeEventsChan()
+ events := share.NewQueue[Event]()
drawIn := make(chan func(draw.Image) image.Rectangle)
drawOut := make(chan func(draw.Image) image.Rectangle)
kill := make(chan bool)
@@ -60,7 +61,7 @@ func newDummyEnv(size image.Rectangle) dummyEnv {
defer close(kill)
defer close(drawOut)
defer close(drawIn)
- defer close(eventsIn)
+ defer close(events.Enqueue)
defer func() {
go drain(drawIn)
attached.kill <- true
@@ -77,13 +78,13 @@ func newDummyEnv(size image.Rectangle) dummyEnv {
}
}()
- eventsIn <- Resize{size}
+ events.Enqueue <- Resize{size}
- return dummyEnv{eventsIn, eventsOut, drawIn, drawOut, kill, dead, attached.attach()}
+ return dummyEnv{events, drawIn, drawOut, kill, dead, attached.attach()}
}
func (de dummyEnv) Events() <-chan Event {
- return de.eventsOut
+ return de.events.Dequeue
}
func (de dummyEnv) Draw() chan<- func(draw.Image) image.Rectangle {
diff --git a/layout_test.go b/layout_test.go
index 1999c63..4a78092 100644
--- a/layout_test.go
+++ b/layout_test.go
@@ -33,7 +33,7 @@ func TestSniffer(t *testing.T) {
// Send events to sniffer.
events := []Event{dummyEvent{"barEvent"}, dummyEvent{"fooEvent"}}
for _, event := range events {
- root.eventsIn <- event
+ root.events.Enqueue <- event
eventp, ok := tryRecv(sniffer.Events(), timeout)
if !ok {
@@ -91,6 +91,6 @@ func TestResizer(t *testing.T) {
}
// this event should be replaced by the resizer
- root.eventsIn <- Resize{image.Rectangle{}}
+ root.events.Enqueue <- Resize{image.Rectangle{}}
}
}
diff --git a/mux.go b/mux.go
index 5c6afe9..486fe8f 100644
--- a/mux.go
+++ b/mux.go
@@ -66,7 +66,7 @@ func NewMux(parent Env) Mux {
size.Set <- resize.Rectangle
}
for _, child := range children {
- child.eventsIn <- e
+ child.events.Enqueue <- e
}
case child := <-addChild:
children = append(children, child)
@@ -108,8 +108,7 @@ func (mux Mux) detach() <-chan bool {
}
type muxEnv struct {
- eventsIn chan<- Event
- eventsOut <-chan Event
+ events share.Queue[Event]
draw chan<- func(draw.Image) image.Rectangle
attachChan chan<- attachable
kill chan<- bool
@@ -118,7 +117,7 @@ type muxEnv struct {
}
func (mux Mux) MakeEnv() Env {
- eventsOut, eventsIn := makeEventsChan()
+ events := share.NewQueue[Event]()
drawChan := make(chan func(draw.Image) image.Rectangle)
attached := newAttachHandler()
kill := make(chan bool)
@@ -126,8 +125,7 @@ func (mux Mux) MakeEnv() Env {
detachFromMux := make(chan bool)
env := muxEnv{
- eventsIn: eventsIn,
- eventsOut: eventsOut,
+ events: events,
draw: drawChan,
attachChan: attached.attach(),
kill: kill,
@@ -136,7 +134,7 @@ func (mux Mux) MakeEnv() Env {
}
mux.addChild <- env
// make sure to always send a resize event to a new Env
- eventsIn <- Resize{mux.size.Get()}
+ events.Enqueue <- Resize{mux.size.Get()}
go func() {
defer func() {
@@ -145,8 +143,7 @@ func (mux Mux) MakeEnv() Env {
}()
defer close(kill)
defer close(drawChan)
- defer close(eventsIn)
- // eventsOut closed automatically by makeEventsChan()
+ defer close(events.Enqueue)
defer func() {
mux.removeChild <- env
@@ -174,7 +171,7 @@ func (mux Mux) MakeEnv() Env {
}
func (env muxEnv) Events() <-chan Event {
- return env.eventsOut
+ return env.events.Dequeue
}
func (env muxEnv) Draw() chan<- func(draw.Image) image.Rectangle {
diff --git a/mux_test.go b/mux_test.go
index 75f6cac..4736641 100644
--- a/mux_test.go
+++ b/mux_test.go
@@ -25,7 +25,7 @@ func TestMuxEvent(t *testing.T) {
events := []Event{Resize{rect}, dummyEvent{"fooEvent"}, dummyEvent{"barEvent"}, dummyEvent{"bazEvent"}}
go func() {
for _, event := range events[1:] { // skip resize—it's sent automatically by the root Env
- root.eventsIn <- event
+ root.events.Enqueue <- event
}
}()
diff --git a/win.go b/win.go
index 41c68a3..405314a 100644
--- a/win.go
+++ b/win.go
@@ -7,6 +7,7 @@ import (
"time"
"unsafe"
+ "git.samanthony.xyz/share"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v2.1/gl"
"github.com/go-gl/glfw/v3.2/glfw"
@@ -75,14 +76,13 @@ func New(opts ...Option) (*Win, error) {
opt(&o)
}
- eventsOut, eventsIn := makeEventsChan()
+ events := share.NewQueue[Event]()
w := &Win{
- eventsOut: eventsOut,
- eventsIn: eventsIn,
- draw: make(chan func(draw.Image) image.Rectangle),
- newSize: make(chan image.Rectangle),
- finish: make(chan struct{}),
+ events: events,
+ draw: make(chan func(draw.Image) image.Rectangle),
+ newSize: make(chan image.Rectangle),
+ finish: make(chan struct{}),
}
var err error
@@ -157,9 +157,8 @@ func makeGLFWWin(o *options) (*glfw.Window, error) {
//
// Warning: only one window can be open at a time. This will be fixed.
type Win struct {
- eventsOut <-chan Event
- eventsIn chan<- Event
- draw chan func(draw.Image) image.Rectangle
+ events share.Queue[Event]
+ draw chan func(draw.Image) image.Rectangle
newSize chan image.Rectangle
finish chan struct{}
@@ -170,7 +169,7 @@ type Win struct {
}
// Events returns the events channel of the window.
-func (w *Win) Events() <-chan Event { return w.eventsOut }
+func (w *Win) Events() <-chan Event { return w.events.Dequeue }
// Draw returns the draw channel of the window.
func (w *Win) Draw() chan<- func(draw.Image) image.Rectangle { return w.draw }
@@ -209,7 +208,7 @@ func (w *Win) eventThread() {
w.w.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) {
moX, moY = int(x), int(y)
- w.eventsIn <- MoMove{image.Pt(moX*w.ratio, moY*w.ratio)}
+ w.events.Enqueue <- MoMove{image.Pt(moX*w.ratio, moY*w.ratio)}
})
w.w.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
@@ -219,18 +218,18 @@ func (w *Win) eventThread() {
}
switch action {
case glfw.Press:
- w.eventsIn <- MoDown{image.Pt(moX*w.ratio, moY*w.ratio), b}
+ w.events.Enqueue <- MoDown{image.Pt(moX*w.ratio, moY*w.ratio), b}
case glfw.Release:
- w.eventsIn <- MoUp{image.Pt(moX*w.ratio, moY*w.ratio), b}
+ w.events.Enqueue <- MoUp{image.Pt(moX*w.ratio, moY*w.ratio), b}
}
})
w.w.SetScrollCallback(func(_ *glfw.Window, xoff, yoff float64) {
- w.eventsIn <- MoScroll{image.Pt(int(xoff), int(yoff))}
+ w.events.Enqueue <- MoScroll{image.Pt(int(xoff), int(yoff))}
})
w.w.SetCharCallback(func(_ *glfw.Window, r rune) {
- w.eventsIn <- KbType{r}
+ w.events.Enqueue <- KbType{r}
})
w.w.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, _ int, action glfw.Action, _ glfw.ModifierKey) {
@@ -240,31 +239,31 @@ func (w *Win) eventThread() {
}
switch action {
case glfw.Press:
- w.eventsIn <- KbDown{k}
+ w.events.Enqueue <- KbDown{k}
case glfw.Release:
- w.eventsIn <- KbUp{k}
+ w.events.Enqueue <- KbUp{k}
case glfw.Repeat:
- w.eventsIn <- KbRepeat{k}
+ w.events.Enqueue <- KbRepeat{k}
}
})
w.w.SetFramebufferSizeCallback(func(_ *glfw.Window, width, height int) {
r := image.Rect(0, 0, width, height)
w.newSize <- r
- w.eventsIn <- Resize{Rectangle: r}
+ w.events.Enqueue <- Resize{Rectangle: r}
})
w.w.SetCloseCallback(func(_ *glfw.Window) {
- w.eventsIn <- WiClose{}
+ w.events.Enqueue <- WiClose{}
})
r := w.img.Bounds()
- w.eventsIn <- Resize{Rectangle: r}
+ w.events.Enqueue <- Resize{Rectangle: r}
for {
select {
case <-w.finish:
- close(w.eventsIn)
+ close(w.events.Enqueue)
w.w.Destroy()
return
default: