-
Notifications
You must be signed in to change notification settings - Fork 35
/
mux.go
134 lines (121 loc) · 3.72 KB
/
mux.go
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"
"sync"
)
// Mux can be used to multiplex an Env, let's call it a root Env. Mux implements a way to
// create multiple virtual Envs that all interact with the root Env. They receive the same
// events and their draw functions get redirected to the root Env.
type Mux struct {
mu sync.Mutex
lastResize Event
eventsIns []chan<- Event
draw chan<- func(draw.Image) image.Rectangle
}
// NewMux creates a new Mux that multiplexes the given Env. It returns the Mux along with
// a master Env. The master Env is just like any other Env created by the Mux, except that
// closing the Draw() channel on the master Env closes the whole Mux and all other Envs
// created by the Mux.
func NewMux(env Env) (mux *Mux, master Env) {
drawChan := make(chan func(draw.Image) image.Rectangle)
mux = &Mux{draw: drawChan}
master = mux.makeEnv(true)
go func() {
for d := range drawChan {
env.Draw() <- d
}
close(env.Draw())
}()
go func() {
for e := range env.Events() {
mux.mu.Lock()
if resize, ok := e.(Resize); ok {
mux.lastResize = resize
}
for _, eventsIn := range mux.eventsIns {
eventsIn <- e
}
mux.mu.Unlock()
}
mux.mu.Lock()
for _, eventsIn := range mux.eventsIns {
close(eventsIn)
}
mux.mu.Unlock()
}()
return mux, master
}
// MakeEnv creates a new virtual Env that interacts with the root Env of the Mux. Closing
// the Draw() channel of the Env will not close the Mux, or any other Env created by the Mux
// but will delete the Env from the Mux.
func (mux *Mux) MakeEnv() Env {
return mux.makeEnv(false)
}
type muxEnv struct {
events <-chan Event
draw chan<- func(draw.Image) image.Rectangle
}
func (m *muxEnv) Events() <-chan Event { return m.events }
func (m *muxEnv) Draw() chan<- func(draw.Image) image.Rectangle { return m.draw }
func (mux *Mux) makeEnv(master bool) Env {
eventsOut, eventsIn := MakeEventsChan()
drawChan := make(chan func(draw.Image) image.Rectangle)
env := &muxEnv{eventsOut, drawChan}
mux.mu.Lock()
mux.eventsIns = append(mux.eventsIns, eventsIn)
// make sure to always send a resize event to a new Env if we got the size already
// that means it missed the resize event by the root Env
if mux.lastResize != nil {
eventsIn <- mux.lastResize
}
mux.mu.Unlock()
go func() {
func() {
// When the master Env gets its Draw() channel closed, it closes all the Events()
// channels of all the children Envs, and it also closes the internal draw channel
// of the Mux. Otherwise, closing the Draw() channel of the master Env wouldn't
// close the Env the Mux is muxing. However, some child Envs of the Mux may still
// send some drawing commmands before they realize that their Events() channel got
// closed.
//
// That is perfectly fine if their drawing commands simply get ignored. This down here
// is a little hacky, but (I hope) perfectly fine solution to the problem.
//
// When the internal draw channel of the Mux gets closed, the line marked with ! will
// cause panic. We recover this panic, then we receive, but ignore all furhter draw
// commands, correctly draining the Env until it closes itself.
defer func() {
if recover() != nil {
for range drawChan {
}
}
}()
for d := range drawChan {
mux.draw <- d // !
}
}()
if master {
mux.mu.Lock()
for _, eventsIn := range mux.eventsIns {
close(eventsIn)
}
mux.eventsIns = nil
close(mux.draw)
mux.mu.Unlock()
} else {
mux.mu.Lock()
i := -1
for i = range mux.eventsIns {
if mux.eventsIns[i] == eventsIn {
break
}
}
if i != -1 {
mux.eventsIns = append(mux.eventsIns[:i], mux.eventsIns[i+1:]...)
}
mux.mu.Unlock()
}
}()
return env
}