forked from faiface/gui
/
mux.go
151 lines (134 loc) · 4.13 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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
drawGL chan<- func()
}
// 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)
drawGLChan := make(chan func())
mux = &Mux{draw: drawChan, drawGL: drawGLChan}
master = mux.makeEnv(true)
go func() {
for d := range drawChan {
env.Draw() <- d
}
close(env.Draw())
}()
go func() {
for gld := range drawGLChan {
env.GL() <- gld
}
close(env.GL())
}()
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
drawGL chan<- func()
}
func (m *muxEnv) Events() <-chan Event { return m.events }
func (m *muxEnv) Draw() chan<- func(draw.Image) image.Rectangle { return m.draw }
func (m *muxEnv) GL() chan<- func() { return m.drawGL }
func (mux *Mux) makeEnv(master bool) Env {
eventsOut, eventsIn := MakeEventsChan()
drawChan := make(chan func(draw.Image) image.Rectangle)
drawGLChan := make(chan func())
env := &muxEnv{eventsOut, drawChan, drawGLChan}
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()
// TODO: Why is it this complicated for what it does?
// Add the gl draw channel?
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
}