1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
package gui
import (
"fmt"
"image"
"image/draw"
"git.samanthony.xyz/share"
)
// Mux can be used to multiplex an Env, let's call it the parent Env. Mux implements a way to
// create multiple virtual Envs that all interact with the parent Env. They receive the same
// events and their draw functions get redirected to the parent Env.
type Mux struct {
size share.Val[image.Rectangle]
draw chan<- func(draw.Image) image.Rectangle
addChild chan<- muxEnv
removeChild chan<- muxEnv
kill chan<- bool
dead <-chan bool
detachChan <-chan bool
}
func NewMux(parent Env) Mux {
size := share.NewVal[image.Rectangle]()
drawChan := make(chan func(draw.Image) image.Rectangle)
addChild := make(chan muxEnv)
removeChild := make(chan muxEnv)
kill := make(chan bool)
dead := make(chan bool)
detachFromParent := make(chan bool)
go func() {
defer func() {
dead <- true
close(dead)
}()
defer func() {
detachFromParent <- true
close(detachFromParent)
}()
defer close(kill)
defer close(removeChild)
defer close(addChild)
defer close(drawChan)
defer size.Close()
var children []muxEnv
defer func() {
go drain(drawChan) // children may still be sending
for _, child := range children {
child.kill <- true
}
for range children {
<-removeChild
}
}()
for {
select {
case d := <-drawChan:
parent.Draw() <- d
case e := <-parent.Events():
if resize, ok := e.(Resize); ok {
size.Set <- resize.Rectangle
}
for _, child := range children {
child.eventsIn <- e
}
case child := <-addChild:
children = append(children, child)
case child := <-removeChild:
var err error
// TODO: faster search
if children, err = remove(child, children); err != nil {
panic(fmt.Sprintf("Mux: failed to remove child Env: %v", err))
}
case <-kill:
return
}
}
}()
mux := Mux{
size: size,
draw: drawChan,
addChild: addChild,
removeChild: removeChild,
kill: kill,
dead: dead,
detachChan: detachFromParent,
}
parent.attach() <- mux
return mux
}
func (mux Mux) Kill() chan<- bool {
return mux.kill
}
func (mux Mux) Dead() <-chan bool {
return mux.dead
}
func (mux Mux) detach() <-chan bool {
return mux.detachChan
}
type muxEnv struct {
eventsIn chan<- Event
eventsOut <-chan Event
draw chan<- func(draw.Image) image.Rectangle
attachChan chan<- attachable
kill chan<- bool
dead <-chan bool
detachFromMux <-chan bool
}
func (mux Mux) MakeEnv() Env {
eventsOut, eventsIn := MakeEventsChan()
drawChan := make(chan func(draw.Image) image.Rectangle)
attached := newAttachHandler()
kill := make(chan bool)
dead := make(chan bool)
detachFromMux := make(chan bool)
env := muxEnv{
eventsIn: eventsIn,
eventsOut: eventsOut,
draw: drawChan,
attachChan: attached.attach,
kill: kill,
dead: dead,
detachFromMux: detachFromMux,
}
mux.addChild <- env
// make sure to always send a resize event to a new Env
eventsIn <- Resize{mux.size.Get()}
go func() {
defer func() {
dead <- true
close(dead)
}()
defer close(kill)
defer close(drawChan)
defer close(eventsIn)
// eventsOut closed automatically by MakeEventsChan()
defer func() {
mux.removeChild <- env
}()
defer func() {
attached.kill <- true
<-attached.dead
}()
defer func() {
go drain(drawChan)
}()
for {
select {
case d := <-drawChan:
mux.draw <- d
case <-kill:
return
}
}
}()
return env
}
func (env muxEnv) Events() <-chan Event {
return env.eventsOut
}
func (env muxEnv) Draw() chan<- func(draw.Image) image.Rectangle {
return env.draw
}
func (env muxEnv) Kill() chan<- bool {
return env.kill
}
func (env muxEnv) Dead() <-chan bool {
return env.dead
}
func (env muxEnv) attach() chan<- attachable {
return env.attachChan
}
// remove removes element e from slice s, returning the modified slice, or error if e is not in s.
func remove[S ~[]E, E comparable](e E, s S) ([]E, error) {
for i := range s {
if s[i] == e {
return append(s[:i], s[i+1:]...), nil
}
}
return s, fmt.Errorf("%v not found in %v", e, s)
}
func drain[T any](c <-chan T) {
for range c {
}
}
|