forked from wailsapp/wails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
events.go
170 lines (143 loc) · 4.35 KB
/
events.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package runtime
import (
"sync"
"github.com/dzonerzy/wails/v2/internal/frontend"
"github.com/samber/lo"
)
type Logger interface {
Trace(format string, v ...interface{})
}
// eventListener holds a callback function which is invoked when
// the event listened for is emitted. It has a counter which indicates
// how the total number of events it is interested in. A value of zero
// means it does not expire (default).
type eventListener struct {
callback func(...interface{}) // Function to call with emitted event data
counter int // The number of times this callback may be called. -1 = infinite
delete bool // Flag to indicate that this listener should be deleted
}
// Events handles eventing
type Events struct {
log Logger
frontend []frontend.Frontend
// Go event listeners
listeners map[string][]*eventListener
notifyLock sync.RWMutex
}
func (e *Events) Notify(sender frontend.Frontend, name string, data ...interface{}) {
e.notifyBackend(name, data...)
for _, thisFrontend := range e.frontend {
if thisFrontend == sender {
continue
}
thisFrontend.Notify(name, data...)
}
}
func (e *Events) On(eventName string, callback func(...interface{})) func() {
return e.registerListener(eventName, callback, -1)
}
func (e *Events) OnMultiple(eventName string, callback func(...interface{}), counter int) func() {
return e.registerListener(eventName, callback, counter)
}
func (e *Events) Once(eventName string, callback func(...interface{})) func() {
return e.registerListener(eventName, callback, 1)
}
func (e *Events) Emit(eventName string, data ...interface{}) {
e.notifyBackend(eventName, data...)
for _, thisFrontend := range e.frontend {
thisFrontend.Notify(eventName, data...)
}
}
func (e *Events) Off(eventName string) {
e.unRegisterListener(eventName)
}
func (e *Events) OffAll() {
e.notifyLock.Lock()
for eventName := range e.listeners {
delete(e.listeners, eventName)
}
e.notifyLock.Unlock()
}
// NewEvents creates a new log subsystem
func NewEvents(log Logger) *Events {
result := &Events{
log: log,
listeners: make(map[string][]*eventListener),
}
return result
}
// registerListener provides a means of subscribing to events of type "eventName"
func (e *Events) registerListener(eventName string, callback func(...interface{}), counter int) func() {
// Create new eventListener
thisListener := &eventListener{
callback: callback,
counter: counter,
delete: false,
}
e.notifyLock.Lock()
// Append the new listener to the listeners slice
e.listeners[eventName] = append(e.listeners[eventName], thisListener)
e.notifyLock.Unlock()
return func() {
e.notifyLock.Lock()
defer e.notifyLock.Unlock()
if _, ok := e.listeners[eventName]; !ok {
return
}
e.listeners[eventName] = lo.Filter(e.listeners[eventName], func(l *eventListener, i int) bool {
return l != thisListener
})
}
}
// unRegisterListener provides a means of unsubscribing to events of type "eventName"
func (e *Events) unRegisterListener(eventName string) {
e.notifyLock.Lock()
// Clear the listeners
delete(e.listeners, eventName)
e.notifyLock.Unlock()
}
// Notify backend for the given event name
func (e *Events) notifyBackend(eventName string, data ...interface{}) {
e.notifyLock.Lock()
defer e.notifyLock.Unlock()
// Get list of event listeners
listeners := e.listeners[eventName]
if listeners == nil {
e.log.Trace("No listeners for event '%s'", eventName)
return
}
// We have a dirty flag to indicate that there are items to delete
itemsToDelete := false
// Callback in goroutine
for _, listener := range listeners {
if listener.counter > 0 {
listener.counter--
}
go listener.callback(data...)
if listener.counter == 0 {
listener.delete = true
itemsToDelete = true
}
}
// Do we have items to delete?
if itemsToDelete {
// Create a new Listeners slice
var newListeners []*eventListener
// Iterate over current listeners
for _, listener := range listeners {
// If we aren't deleting the listener, add it to the new list
if !listener.delete {
newListeners = append(newListeners, listener)
}
}
// Save new listeners or remove entry
if len(newListeners) > 0 {
e.listeners[eventName] = newListeners
} else {
delete(e.listeners, eventName)
}
}
}
func (e *Events) AddFrontend(appFrontend frontend.Frontend) {
e.frontend = append(e.frontend, appFrontend)
}