forked from golang/mobile
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathdarwin_desktop.go
496 lines (461 loc) · 11.7 KB
/
darwin_desktop.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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build darwin && !ios
// +build darwin,!ios
package app
// Simple on-screen app debugging for OS X. Not an officially supported
// development target for apps, as screens with mice are very different
// than screens with touch panels.
/*
#cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION
#cgo LDFLAGS: -framework Cocoa -framework OpenGL
#import <Carbon/Carbon.h> // for HIToolbox/Events.h
#import <Cocoa/Cocoa.h>
#include <pthread.h>
void runApp(void);
void stopApp(void);
void makeCurrentContext(GLintptr);
uint64 threadID();
*/
import "C"
import (
"log"
"runtime"
"sync"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/geom"
)
var initThreadID uint64
func init() {
// Lock the goroutine responsible for initialization to an OS thread.
// This means the goroutine running main (and calling runApp below)
// is locked to the OS thread that started the program. This is
// necessary for the correct delivery of Cocoa events to the process.
//
// A discussion on this topic:
// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
runtime.LockOSThread()
initThreadID = uint64(C.threadID())
}
func main(f func(App)) {
if tid := uint64(C.threadID()); tid != initThreadID {
log.Fatalf("app.Main called on thread %d, but app.init ran on %d", tid, initThreadID)
}
go func() {
f(theApp)
C.stopApp()
}()
C.runApp()
}
// loop is the primary drawing loop.
//
// After Cocoa has captured the initial OS thread for processing Cocoa
// events in runApp, it starts loop on another goroutine. It is locked
// to an OS thread for its OpenGL context.
//
// The loop processes GL calls until a publish event appears.
// Then it runs any remaining GL calls and flushes the screen.
//
// As NSOpenGLCPSwapInterval is set to 1, the call to CGLFlushDrawable
// blocks until the screen refresh.
func (a *app) loop(ctx C.GLintptr) {
runtime.LockOSThread()
C.makeCurrentContext(ctx)
workAvailable := a.worker.WorkAvailable()
for {
select {
case <-workAvailable:
a.worker.DoWork()
case <-theApp.publish:
loop1:
for {
select {
case <-workAvailable:
a.worker.DoWork()
default:
break loop1
}
}
C.CGLFlushDrawable(C.CGLGetCurrentContext())
theApp.publishResult <- PublishResult{}
select {
case drawDone <- struct{}{}:
default:
}
}
}
}
var drawDone = make(chan struct{})
// drawgl is used by Cocoa to occasionally request screen updates.
//
//export drawgl
func drawgl() {
switch theApp.lifecycleStage {
case lifecycle.StageFocused, lifecycle.StageVisible:
theApp.Send(paint.Event{
External: true,
})
<-drawDone
}
}
//export startloop
func startloop(ctx C.GLintptr) {
go theApp.loop(ctx)
}
var windowHeightPx float32
//export setGeom
func setGeom(pixelsPerPt float32, widthPx, heightPx int) {
windowHeightPx = float32(heightPx)
theApp.eventsIn <- size.Event{
WidthPx: widthPx,
HeightPx: heightPx,
WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
PixelsPerPt: pixelsPerPt,
}
}
var touchEvents struct {
sync.Mutex
pending []touch.Event
}
func sendTouch(t touch.Type, x, y float32) {
theApp.eventsIn <- touch.Event{
X: x,
Y: windowHeightPx - y,
Sequence: 0,
Type: t,
}
}
//export eventMouseDown
func eventMouseDown(x, y float32) { sendTouch(touch.TypeBegin, x, y) }
//export eventMouseDragged
func eventMouseDragged(x, y float32) { sendTouch(touch.TypeMove, x, y) }
//export eventMouseEnd
func eventMouseEnd(x, y float32) { sendTouch(touch.TypeEnd, x, y) }
//export lifecycleDead
func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
//export eventKey
func eventKey(runeVal int32, direction uint8, code uint16, flags uint32) {
var modifiers key.Modifiers
for _, mod := range mods {
if flags&mod.flags == mod.flags {
modifiers |= mod.mod
}
}
theApp.eventsIn <- key.Event{
Rune: convRune(rune(runeVal)),
Code: convVirtualKeyCode(code),
Modifiers: modifiers,
Direction: key.Direction(direction),
}
}
//export eventFlags
func eventFlags(flags uint32) {
for _, mod := range mods {
if flags&mod.flags == mod.flags && lastFlags&mod.flags != mod.flags {
eventKey(-1, uint8(key.DirPress), mod.code, flags)
}
if lastFlags&mod.flags == mod.flags && flags&mod.flags != mod.flags {
eventKey(-1, uint8(key.DirRelease), mod.code, flags)
}
}
lastFlags = flags
}
var lastFlags uint32
var mods = [...]struct {
flags uint32
code uint16
mod key.Modifiers
}{
// Left and right variants of modifier keys have their own masks,
// but they are not documented. These were determined empirically.
{1<<17 | 0x102, C.kVK_Shift, key.ModShift},
{1<<17 | 0x104, C.kVK_RightShift, key.ModShift},
{1<<18 | 0x101, C.kVK_Control, key.ModControl},
// TODO key.ControlRight
{1<<19 | 0x120, C.kVK_Option, key.ModAlt},
{1<<19 | 0x140, C.kVK_RightOption, key.ModAlt},
{1<<20 | 0x108, C.kVK_Command, key.ModMeta},
{1<<20 | 0x110, C.kVK_Command, key.ModMeta}, // TODO: missing kVK_RightCommand
}
//export lifecycleAlive
func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
//export lifecycleVisible
func lifecycleVisible() {
theApp.sendLifecycle(lifecycle.StageVisible)
}
//export lifecycleFocused
func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
// convRune marks the Carbon/Cocoa private-range unicode rune representing
// a non-unicode key event to -1, used for Rune in the key package.
//
// http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
func convRune(r rune) rune {
if '\uE000' <= r && r <= '\uF8FF' {
return -1
}
return r
}
// convVirtualKeyCode converts a Carbon/Cocoa virtual key code number
// into the standard keycodes used by the key package.
//
// To get a sense of the key map, see the diagram on
//
// http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes
func convVirtualKeyCode(vkcode uint16) key.Code {
switch vkcode {
case C.kVK_ANSI_A:
return key.CodeA
case C.kVK_ANSI_B:
return key.CodeB
case C.kVK_ANSI_C:
return key.CodeC
case C.kVK_ANSI_D:
return key.CodeD
case C.kVK_ANSI_E:
return key.CodeE
case C.kVK_ANSI_F:
return key.CodeF
case C.kVK_ANSI_G:
return key.CodeG
case C.kVK_ANSI_H:
return key.CodeH
case C.kVK_ANSI_I:
return key.CodeI
case C.kVK_ANSI_J:
return key.CodeJ
case C.kVK_ANSI_K:
return key.CodeK
case C.kVK_ANSI_L:
return key.CodeL
case C.kVK_ANSI_M:
return key.CodeM
case C.kVK_ANSI_N:
return key.CodeN
case C.kVK_ANSI_O:
return key.CodeO
case C.kVK_ANSI_P:
return key.CodeP
case C.kVK_ANSI_Q:
return key.CodeQ
case C.kVK_ANSI_R:
return key.CodeR
case C.kVK_ANSI_S:
return key.CodeS
case C.kVK_ANSI_T:
return key.CodeT
case C.kVK_ANSI_U:
return key.CodeU
case C.kVK_ANSI_V:
return key.CodeV
case C.kVK_ANSI_W:
return key.CodeW
case C.kVK_ANSI_X:
return key.CodeX
case C.kVK_ANSI_Y:
return key.CodeY
case C.kVK_ANSI_Z:
return key.CodeZ
case C.kVK_ANSI_1:
return key.Code1
case C.kVK_ANSI_2:
return key.Code2
case C.kVK_ANSI_3:
return key.Code3
case C.kVK_ANSI_4:
return key.Code4
case C.kVK_ANSI_5:
return key.Code5
case C.kVK_ANSI_6:
return key.Code6
case C.kVK_ANSI_7:
return key.Code7
case C.kVK_ANSI_8:
return key.Code8
case C.kVK_ANSI_9:
return key.Code9
case C.kVK_ANSI_0:
return key.Code0
// TODO: move the rest of these codes to constants in key.go
// if we are happy with them.
case C.kVK_Return:
return key.CodeReturnEnter
case C.kVK_Escape:
return key.CodeEscape
case C.kVK_Delete:
return key.CodeDeleteBackspace
case C.kVK_Tab:
return key.CodeTab
case C.kVK_Space:
return key.CodeSpacebar
case C.kVK_ANSI_Minus:
return key.CodeHyphenMinus
case C.kVK_ANSI_Equal:
return key.CodeEqualSign
case C.kVK_ANSI_LeftBracket:
return key.CodeLeftSquareBracket
case C.kVK_ANSI_RightBracket:
return key.CodeRightSquareBracket
case C.kVK_ANSI_Backslash:
return key.CodeBackslash
// 50: Keyboard Non-US "#" and ~
case C.kVK_ANSI_Semicolon:
return key.CodeSemicolon
case C.kVK_ANSI_Quote:
return key.CodeApostrophe
case C.kVK_ANSI_Grave:
return key.CodeGraveAccent
case C.kVK_ANSI_Comma:
return key.CodeComma
case C.kVK_ANSI_Period:
return key.CodeFullStop
case C.kVK_ANSI_Slash:
return key.CodeSlash
case C.kVK_CapsLock:
return key.CodeCapsLock
case C.kVK_F1:
return key.CodeF1
case C.kVK_F2:
return key.CodeF2
case C.kVK_F3:
return key.CodeF3
case C.kVK_F4:
return key.CodeF4
case C.kVK_F5:
return key.CodeF5
case C.kVK_F6:
return key.CodeF6
case C.kVK_F7:
return key.CodeF7
case C.kVK_F8:
return key.CodeF8
case C.kVK_F9:
return key.CodeF9
case C.kVK_F10:
return key.CodeF10
case C.kVK_F11:
return key.CodeF11
case C.kVK_F12:
return key.CodeF12
// 70: PrintScreen
// 71: Scroll Lock
// 72: Pause
// 73: Insert
case C.kVK_Home:
return key.CodeHome
case C.kVK_PageUp:
return key.CodePageUp
case C.kVK_ForwardDelete:
return key.CodeDeleteForward
case C.kVK_End:
return key.CodeEnd
case C.kVK_PageDown:
return key.CodePageDown
case C.kVK_RightArrow:
return key.CodeRightArrow
case C.kVK_LeftArrow:
return key.CodeLeftArrow
case C.kVK_DownArrow:
return key.CodeDownArrow
case C.kVK_UpArrow:
return key.CodeUpArrow
case C.kVK_ANSI_KeypadClear:
return key.CodeKeypadNumLock
case C.kVK_ANSI_KeypadDivide:
return key.CodeKeypadSlash
case C.kVK_ANSI_KeypadMultiply:
return key.CodeKeypadAsterisk
case C.kVK_ANSI_KeypadMinus:
return key.CodeKeypadHyphenMinus
case C.kVK_ANSI_KeypadPlus:
return key.CodeKeypadPlusSign
case C.kVK_ANSI_KeypadEnter:
return key.CodeKeypadEnter
case C.kVK_ANSI_Keypad1:
return key.CodeKeypad1
case C.kVK_ANSI_Keypad2:
return key.CodeKeypad2
case C.kVK_ANSI_Keypad3:
return key.CodeKeypad3
case C.kVK_ANSI_Keypad4:
return key.CodeKeypad4
case C.kVK_ANSI_Keypad5:
return key.CodeKeypad5
case C.kVK_ANSI_Keypad6:
return key.CodeKeypad6
case C.kVK_ANSI_Keypad7:
return key.CodeKeypad7
case C.kVK_ANSI_Keypad8:
return key.CodeKeypad8
case C.kVK_ANSI_Keypad9:
return key.CodeKeypad9
case C.kVK_ANSI_Keypad0:
return key.CodeKeypad0
case C.kVK_ANSI_KeypadDecimal:
return key.CodeKeypadFullStop
case C.kVK_ANSI_KeypadEquals:
return key.CodeKeypadEqualSign
case C.kVK_F13:
return key.CodeF13
case C.kVK_F14:
return key.CodeF14
case C.kVK_F15:
return key.CodeF15
case C.kVK_F16:
return key.CodeF16
case C.kVK_F17:
return key.CodeF17
case C.kVK_F18:
return key.CodeF18
case C.kVK_F19:
return key.CodeF19
case C.kVK_F20:
return key.CodeF20
// 116: Keyboard Execute
case C.kVK_Help:
return key.CodeHelp
// 118: Keyboard Menu
// 119: Keyboard Select
// 120: Keyboard Stop
// 121: Keyboard Again
// 122: Keyboard Undo
// 123: Keyboard Cut
// 124: Keyboard Copy
// 125: Keyboard Paste
// 126: Keyboard Find
case C.kVK_Mute:
return key.CodeMute
case C.kVK_VolumeUp:
return key.CodeVolumeUp
case C.kVK_VolumeDown:
return key.CodeVolumeDown
// 130: Keyboard Locking Caps Lock
// 131: Keyboard Locking Num Lock
// 132: Keyboard Locking Scroll Lock
// 133: Keyboard Comma
// 134: Keyboard Equal Sign
// ...: Bunch of stuff
case C.kVK_Control:
return key.CodeLeftControl
case C.kVK_Shift:
return key.CodeLeftShift
case C.kVK_Option:
return key.CodeLeftAlt
case C.kVK_Command:
return key.CodeLeftGUI
case C.kVK_RightControl:
return key.CodeRightControl
case C.kVK_RightShift:
return key.CodeRightShift
case C.kVK_RightOption:
return key.CodeRightAlt
// TODO key.CodeRightGUI
default:
return key.CodeUnknown
}
}