-
Notifications
You must be signed in to change notification settings - Fork 22
/
ipc.go
376 lines (348 loc) · 8.65 KB
/
ipc.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
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
//----------------------------------------
//
// Copyright © yanghy. All Rights Reserved.
//
// Licensed under Apache License Version 2.0, January 2004
//
// https://www.apache.org/licenses/LICENSE-2.0
//
//----------------------------------------
// IPC - Based on pkgs IPC, CEF Internal implementation
// event listeners
// event triggered
package ipc
import (
"github.com/energye/energy/v2/cef/internal/cef"
"github.com/energye/energy/v2/cef/ipc/callback"
"github.com/energye/energy/v2/cef/ipc/context"
"github.com/energye/energy/v2/cef/ipc/target"
"github.com/energye/energy/v2/cef/ipc/types"
"github.com/energye/energy/v2/cef/process"
"reflect"
"sync"
"time"
)
var (
isMainProcess bool
isSubProcess bool
browser *browserIPC
)
// EmitContextCallback IPC context Callback Function
type EmitContextCallback func(context context.IContext)
// browserIPC browser process IPC
type browserIPC struct {
onEvent map[string]*callback.Callback
emitCallback map[int32]*callback.Callback
emitCallbackMessageId int32
onLock sync.Mutex
emitLock sync.Mutex
messageLock sync.Mutex
window target.IWindow
browserWindow target.IBrowserWindow
}
// WaitChan
//
// IPC synchronous receiving data channel
type WaitChan struct {
count int32
Pending *sync.Map
}
func init() {
isMainProcess = process.Args.IsMain()
isSubProcess = process.Args.IsRender()
if isMainProcess || isSubProcess {
browser = &browserIPC{onEvent: make(map[string]*callback.Callback), emitCallback: make(map[int32]*callback.Callback)}
}
}
// createCallback
//
// Create and return a callback function
func createCallback(fn interface{}) *callback.Callback {
switch fn.(type) {
case func(context context.IContext):
return &callback.Callback{Context: &callback.ContextCallback{Callback: fn.(func(context context.IContext))}}
default:
v := reflect.ValueOf(fn)
// fn must be a function
if v.Kind() != reflect.Func {
return nil
}
return &callback.Callback{Argument: &callback.ArgumentCallback{Callback: &v}}
}
}
// SetProcessMessage
//
// Set the process message object
// without manually calling it
func SetProcessMessage(pm target.IWindow) {
if pm == nil {
return
}
browser.messageLock.Lock()
defer browser.messageLock.Unlock()
browser.window = pm
}
// SetBrowserWindow
// Set BrowserWindow on initialization
func SetBrowserWindow(bw target.IBrowserWindow) {
if browser.browserWindow == nil {
browser.browserWindow = bw
}
}
// On
//
// IPC GO Listening for events
func On(name string, fn interface{}, options ...types.OnOptions) {
if name == "" || fn == nil {
return
}
var (
isOn = false
option *types.OnOptions
)
if options != nil && len(options) > 0 && !cef.Application().SingleProcess() {
option = &options[0]
if option.OnType == types.OtAll {
isOn = true
} else if option.OnType == types.OtMain && isMainProcess {
isOn = true
} else if option.OnType == types.OtSub && isSubProcess {
isOn = true
}
} else if isMainProcess || isSubProcess {
isOn = true
}
if isOn {
if callbackFN := createCallback(fn); callbackFN != nil {
if option != nil {
callbackFN.IsAsync = option.Mode == types.MAsync
}
browser.addOnEvent(name, callbackFN)
}
}
}
// RemoveOn
// IPC GO Remove listening events
func RemoveOn(name string) {
if name == "" {
return
}
browser.onLock.Lock()
defer browser.onLock.Unlock()
delete(browser.onEvent, name)
}
// Emit
//
// Event that triggers listening
// default to triggering the main process
func Emit(name string, argument ...interface{}) bool {
if name == "" || browser.window == nil {
return false
}
// When the window is closed
if browser.window == nil || browser.window.IsClosing() {
// This window is the first one created and not closed
SetProcessMessage(browser.browserWindow.LookForMainWindow())
}
browser.window.ProcessMessage().EmitRender(0, name, nil, argument...)
return true
}
// EmitAndCallback
//
// Event that triggers listening
// with callback function
// default to the main process
func EmitAndCallback(name string, argument []interface{}, fn interface{}) bool {
if name == "" || browser.window == nil {
return false
}
// When the window is closed
if browser.window == nil || browser.window.IsClosing() {
// This window is the first one created and not closed
SetProcessMessage(browser.browserWindow.LookForMainWindow())
}
messageId := browser.addEmitCallback(fn)
if ok := browser.window.ProcessMessage().EmitRender(messageId, name, nil, argument...); !ok {
//fail in send
if messageId > 0 {
removeEmitCallback(messageId)
}
return false
}
return true
}
// EmitTarget
//
// Trigger an event for the specified target to listen to
func EmitTarget(name string, tag target.ITarget, argument ...interface{}) bool {
if name == "" {
return false
}
if tag != nil {
// Send Go
if (tag.ChannelId() > 0 && tag.TargetType() == target.TgGoSub) || (tag.TargetType() == target.TgGoMain) {
emitSendToGoChannel(0, tag, name, argument)
return true
}
}
// Send JS
var window = tag.Window()
if window == nil {
// default window
window = browser.window
}
if window.IsClosing() {
return false
}
window.ProcessMessage().EmitRender(0, name, tag, argument...)
return true
}
// EmitTargetAndCallback
//
// Trigger an event with a callback function for the specified target to listen on
func EmitTargetAndCallback(name string, tag target.ITarget, argument []interface{}, fn interface{}) bool {
if name == "" {
return false
}
var messageId int32 = 0
if tag != nil {
if (tag.ChannelId() > 0 && tag.TargetType() == target.TgGoSub) || (tag.TargetType() == target.TgGoMain) {
messageId = browser.addEmitCallback(fn)
emitSendToGoChannel(messageId, tag, name, argument)
return true
}
}
var window = tag.Window()
if window == nil {
// default window
window = browser.window
}
if window.IsClosing() {
return false
}
messageId = browser.addEmitCallback(fn)
if ok := window.ProcessMessage().EmitRender(messageId, name, tag, argument...); !ok {
if messageId > 0 {
removeEmitCallback(messageId)
}
return false
}
return true
}
// CheckOnEvent
//
// IPC checks if the event listening in GO exists
// returns the function and removes it
func CheckOnEvent(name string) *callback.Callback {
if name == "" {
return nil
}
browser.onLock.Lock()
defer browser.onLock.Unlock()
if fn, ok := browser.onEvent[name]; ok {
return fn
}
return nil
}
// CheckEmitCallback
//
// IPC checks if the GO Emit callback function exists
// returns the function and removes it
func CheckEmitCallback(id int32) *callback.Callback {
browser.emitLock.Lock()
defer browser.emitLock.Unlock()
if fn, ok := browser.emitCallback[id]; ok {
delete(browser.emitCallback, id)
return fn
}
return nil
}
// removeEmitCallback
//
// Delete callback function for specified message ID
func removeEmitCallback(id int32) {
browser.emitLock.Lock()
defer browser.emitLock.Unlock()
delete(browser.emitCallback, id)
}
// addOnEvent
//
// Add listening event
// callback function
// 1. context 2.argument list
func (m *browserIPC) addOnEvent(name string, fn *callback.Callback) {
if m == nil || name == "" || fn == nil {
return
}
m.onLock.Lock()
defer m.onLock.Unlock()
m.onEvent[name] = fn
}
// emitOnEvent
//
// Trigger listening event
func (m *browserIPC) emitOnEvent(name string, argumentList types.IArrayValue) {
if m == nil || name == "" || argumentList == nil {
return
}
m.onLock.Lock()
defer m.onLock.Unlock()
}
// addOnEvent
//
// Add emit callback function
func (m *browserIPC) addEmitCallback(fn interface{}) int32 {
if m == nil || fn == nil {
return 0
}
m.emitLock.Lock()
defer m.emitLock.Unlock()
if callbackFN := createCallback(fn); callbackFN != nil {
if m.emitCallbackMessageId == -1 {
m.emitCallbackMessageId = 1
} else {
m.emitCallbackMessageId++
}
m.emitCallback[m.emitCallbackMessageId] = callbackFN
return m.emitCallbackMessageId
}
return 0
}
// 计时器
type delayer struct {
stop bool
timer *time.Timer
}
// Stop Immediate stop delay
func (m *delayer) Stop() {
if m.timer != nil {
if !m.stop {
m.timer.Stop()
}
m.stop = true
m.timer = nil
}
}
// NextMessageId 返回下一个消息ID
func (m *WaitChan) NextMessageId() (id int32) {
id = m.count
m.count++
return
}
// NewDelayer 创建一个计时器
func (m *WaitChan) NewDelayer(messageId int32, delay time.Duration) *delayer {
md := new(delayer)
md.timer = time.AfterFunc(delay, func() {
if !md.stop {
md.stop = true
m.Done(messageId, nil)
}
})
return md
}
func (m *WaitChan) Done(messageId int32, data interface{}) {
if val, ok := m.Pending.Load(messageId); ok {
val.(func(result interface{}))(data)
m.Pending.Delete(messageId)
}
}