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
|
package gui
import (
"image"
"image/draw"
"git.samanthony.xyz/share"
)
// Env is the most important thing in this package. It is an interactive graphical
// environment, such as a window.
//
// The Events() channel produces events, like mouse and keyboard presses, while the
// Draw() channel receives drawing functions. A drawing function draws onto the
// supplied draw.Image, which is the drawing area of the Env and returns a rectangle
// covering the whole part of the image that got changed.
//
// 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 share.Queue to create
// a channel of events with an unlimited capacity.
//
// The Draw() channel may be synchronous.
//
// Drawing functions sent to the Draw() channel are not guaranteed to be executed.
type Env interface {
Events() <-chan Event
Draw() chan<- func(draw.Image) image.Rectangle
Killable
killer
}
type env struct {
events <-chan Event
draw chan<- func(draw.Image) image.Rectangle
attachChan chan<- victim
kill chan<- bool
dead <-chan bool
detachChan <-chan bool
}
// newEnv makes an Env that receives Events from, and sends draw functions to, the parent.
//
// Each Event received from parent is passed to filterEvents() along with the Events() channel
// of the Env. Each draw function received from the Env's Draw() channel is passed to
// filterDraws() along with the Draw() channel of the parent Env.
//
// filterEvents() and filterDraws() can be used to, e.g., simply forward the Event or draw function
// to the channel, modify it before sending, not send it at all, or produce some other side-effects.
//
// shutdown() is called before the Env dies.
func newEnv(parent Env,
filterEvents func(Event, chan<- Event),
filterDraws func(func(draw.Image) image.Rectangle, chan<- func(draw.Image) image.Rectangle),
shutdown func(),
) Env {
events := share.NewQueue[Event]()
drawChan := make(chan func(draw.Image) image.Rectangle)
child := newKiller()
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 shutdown()
defer close(events.Enqueue)
defer close(drawChan)
defer close(kill)
defer func() {
go drain(drawChan)
child.Kill() <- true
<-child.Dead()
}()
for {
select {
case e := <-parent.Events():
filterEvents(e, events.Enqueue)
case d := <-drawChan:
filterDraws(d, parent.Draw())
case <-kill:
return
}
}
}()
e := env{
events: events.Dequeue,
draw: drawChan,
attachChan: child.attach(),
kill: kill,
dead: dead,
detachChan: detachFromParent,
}
parent.attach() <- e
return e
}
func (e env) Events() <-chan Event {
return e.events
}
func (e env) Draw() chan<- func(draw.Image) image.Rectangle {
return e.draw
}
func (e env) Kill() chan<- bool {
return e.kill
}
func (e env) Dead() <-chan bool {
return e.dead
}
func (e env) attach() chan<- victim {
return e.attachChan
}
func (e env) detach() <-chan bool {
return e.detachChan
}
func send[T any](v T, c chan<- T) {
c <- v
}
|