github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/exp/shiny/driver/gldriver/cocoa.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build darwin 6 // +build 386 amd64 7 // +build !ios 8 9 package gldriver 10 11 /* 12 #cgo CFLAGS: -x objective-c 13 #cgo LDFLAGS: -framework Cocoa -framework OpenGL 14 #include <OpenGL/gl3.h> 15 #import <Carbon/Carbon.h> // for HIToolbox/Events.h 16 #import <Cocoa/Cocoa.h> 17 #include <pthread.h> 18 #include <stdint.h> 19 20 void startDriver(); 21 void stopDriver(); 22 void makeCurrentContext(uintptr_t ctx); 23 void flushContext(uintptr_t ctx); 24 uintptr_t doNewWindow(int width, int height); 25 void doShowWindow(uintptr_t id); 26 void doCloseWindow(uintptr_t id); 27 uint64_t threadID(); 28 */ 29 import "C" 30 31 import ( 32 "fmt" 33 "log" 34 "runtime" 35 36 "golang.org/x/exp/shiny/screen" 37 "golang.org/x/mobile/event/key" 38 "golang.org/x/mobile/event/lifecycle" 39 "golang.org/x/mobile/event/mouse" 40 "golang.org/x/mobile/event/paint" 41 "golang.org/x/mobile/event/size" 42 "golang.org/x/mobile/geom" 43 "golang.org/x/mobile/gl" 44 ) 45 46 var initThreadID C.uint64_t 47 48 func init() { 49 // Lock the goroutine responsible for initialization to an OS thread. 50 // This means the goroutine running main (and calling startDriver below) 51 // is locked to the OS thread that started the program. This is 52 // necessary for the correct delivery of Cocoa events to the process. 53 // 54 // A discussion on this topic: 55 // https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ 56 runtime.LockOSThread() 57 initThreadID = C.threadID() 58 } 59 60 func newWindow(width, height int32) uintptr { 61 return uintptr(C.doNewWindow(C.int(width), C.int(height))) 62 } 63 64 func showWindow(w *windowImpl) { 65 w.glctxMu.Lock() 66 w.glctx, w.worker = gl.NewContext() 67 w.glctxMu.Unlock() 68 69 C.doShowWindow(C.uintptr_t(w.id)) 70 } 71 72 //export preparedOpenGL 73 func preparedOpenGL(id, ctx, vba uintptr) { 74 theScreen.mu.Lock() 75 w := theScreen.windows[id] 76 theScreen.mu.Unlock() 77 78 w.ctx = ctx 79 go drawLoop(w, vba) 80 } 81 82 func closeWindow(id uintptr) { 83 C.doCloseWindow(C.uintptr_t(id)) 84 } 85 86 var mainCallback func(screen.Screen) 87 88 func main(f func(screen.Screen)) error { 89 if tid := C.threadID(); tid != initThreadID { 90 log.Fatalf("gldriver.Main called on thread %d, but gldriver.init ran on %d", tid, initThreadID) 91 } 92 93 mainCallback = f 94 C.startDriver() 95 return nil 96 } 97 98 //export driverStarted 99 func driverStarted() { 100 go func() { 101 mainCallback(theScreen) 102 C.stopDriver() 103 }() 104 } 105 106 //export drawgl 107 func drawgl(id uintptr) { 108 theScreen.mu.Lock() 109 w := theScreen.windows[id] 110 theScreen.mu.Unlock() 111 112 if w == nil { 113 return // closing window 114 } 115 116 sendLifecycle(id, lifecycle.StageVisible) 117 w.Send(paint.Event{External: true}) 118 <-w.drawDone 119 } 120 121 // drawLoop is the primary drawing loop. 122 // 123 // After Cocoa has created an NSWindow and called prepareOpenGL, 124 // it starts drawLoop on a locked goroutine to handle OpenGL calls. 125 // 126 // The screen is drawn every time a paint.Event is received, which can be 127 // triggered either by the user or by Cocoa via drawgl (for example, when 128 // the window is resized). 129 func drawLoop(w *windowImpl, vba uintptr) { 130 runtime.LockOSThread() 131 C.makeCurrentContext(C.uintptr_t(w.ctx)) 132 133 // Starting in OS X 10.11 (El Capitan), the vertex array is 134 // occasionally getting unbound when the context changes threads. 135 // 136 // Avoid this by binding it again. 137 C.glBindVertexArray(C.GLuint(vba)) 138 if errno := C.glGetError(); errno != 0 { 139 panic(fmt.Sprintf("gldriver: glBindVertexArray failed: %d", errno)) 140 } 141 142 workAvailable := w.worker.WorkAvailable() 143 144 // TODO(crawshaw): exit this goroutine on Release. 145 for { 146 select { 147 case <-workAvailable: 148 w.worker.DoWork() 149 case <-w.publish: 150 loop: 151 for { 152 select { 153 case <-workAvailable: 154 w.worker.DoWork() 155 default: 156 break loop 157 } 158 } 159 C.flushContext(C.uintptr_t(w.ctx)) 160 w.publishDone <- screen.PublishResult{} 161 } 162 } 163 } 164 165 //export setGeom 166 func setGeom(id uintptr, ppp float32, widthPx, heightPx int) { 167 theScreen.mu.Lock() 168 w := theScreen.windows[id] 169 theScreen.mu.Unlock() 170 171 if w == nil { 172 return // closing window 173 } 174 175 sz := size.Event{ 176 WidthPx: widthPx, 177 HeightPx: heightPx, 178 WidthPt: geom.Pt(float32(widthPx) / ppp), 179 HeightPt: geom.Pt(float32(heightPx) / ppp), 180 PixelsPerPt: ppp, 181 } 182 183 w.szMu.Lock() 184 w.sz = sz 185 w.szMu.Unlock() 186 187 w.Send(sz) 188 } 189 190 //export windowClosing 191 func windowClosing(id uintptr) { 192 sendLifecycle(id, lifecycle.StageDead) 193 theScreen.mu.Lock() 194 w := theScreen.windows[id] 195 delete(theScreen.windows, id) 196 theScreen.mu.Unlock() 197 w.releaseCleanup() 198 } 199 200 func sendWindowEvent(id uintptr, e interface{}) { 201 theScreen.mu.Lock() 202 w := theScreen.windows[id] 203 theScreen.mu.Unlock() 204 205 if w == nil { 206 return // closing window 207 } 208 w.Send(e) 209 } 210 211 var mods = [...]struct { 212 flags uint32 213 code uint16 214 mod key.Modifiers 215 }{ 216 // Left and right variants of modifier keys have their own masks, 217 // but they are not documented. These were determined empirically. 218 {1<<17 | 0x102, C.kVK_Shift, key.ModShift}, 219 {1<<17 | 0x104, C.kVK_RightShift, key.ModShift}, 220 {1<<18 | 0x101, C.kVK_Control, key.ModControl}, 221 // TODO key.ControlRight 222 {1<<19 | 0x120, C.kVK_Option, key.ModAlt}, 223 {1<<19 | 0x140, C.kVK_RightOption, key.ModAlt}, 224 {1<<20 | 0x108, C.kVK_Command, key.ModMeta}, 225 {1<<20 | 0x110, C.kVK_Command, key.ModMeta}, // TODO: missing kVK_RightCommand 226 } 227 228 func cocoaMods(flags uint32) (m key.Modifiers) { 229 for _, mod := range mods { 230 if flags&mod.flags == mod.flags { 231 m |= mod.mod 232 } 233 } 234 return m 235 } 236 237 func cocoaMouseDir(ty int32) mouse.Direction { 238 switch ty { 239 case C.NSLeftMouseDown, C.NSRightMouseDown, C.NSOtherMouseDown: 240 return mouse.DirPress 241 case C.NSLeftMouseUp, C.NSRightMouseUp, C.NSOtherMouseUp: 242 return mouse.DirRelease 243 default: // dragged 244 return mouse.DirNone 245 } 246 } 247 248 func cocoaMouseButton(button int32) mouse.Button { 249 switch button { 250 case 0: 251 return mouse.ButtonLeft 252 case 1: 253 return mouse.ButtonRight 254 case 2: 255 return mouse.ButtonMiddle 256 default: 257 return mouse.ButtonNone 258 } 259 } 260 261 //export mouseEvent 262 func mouseEvent(id uintptr, x, y float32, ty, button int32, flags uint32) { 263 sendWindowEvent(id, mouse.Event{ 264 X: x, 265 Y: y, 266 Button: cocoaMouseButton(button), 267 Direction: cocoaMouseDir(ty), 268 Modifiers: cocoaMods(flags), 269 }) 270 } 271 272 //export keyEvent 273 func keyEvent(id uintptr, runeVal rune, dir uint8, code uint16, flags uint32) { 274 sendWindowEvent(id, key.Event{ 275 Rune: cocoaRune(runeVal), 276 Direction: key.Direction(dir), 277 Code: cocoaKeyCode(code), 278 Modifiers: cocoaMods(flags), 279 }) 280 } 281 282 //export flagEvent 283 func flagEvent(id uintptr, flags uint32) { 284 for _, mod := range mods { 285 if flags&mod.flags == mod.flags && lastFlags&mod.flags != mod.flags { 286 keyEvent(id, -1, C.NSKeyDown, mod.code, flags) 287 } 288 if lastFlags&mod.flags == mod.flags && flags&mod.flags != mod.flags { 289 keyEvent(id, -1, C.NSKeyUp, mod.code, flags) 290 } 291 } 292 lastFlags = flags 293 } 294 295 var lastFlags uint32 296 297 func sendLifecycle(id uintptr, to lifecycle.Stage) { 298 theScreen.mu.Lock() 299 w := theScreen.windows[id] 300 theScreen.mu.Unlock() 301 302 if w == nil { 303 return // window closing 304 } 305 if w.lifecycleStage == to { 306 return 307 } 308 w.Send(lifecycle.Event{ 309 From: w.lifecycleStage, 310 To: to, 311 DrawContext: w.glctx, 312 }) 313 w.lifecycleStage = to 314 } 315 316 func sendLifecycleAll(to lifecycle.Stage) { 317 theScreen.mu.Lock() 318 defer theScreen.mu.Unlock() 319 for _, w := range theScreen.windows { 320 if w.lifecycleStage == to { 321 continue 322 } 323 w.Send(lifecycle.Event{ 324 From: w.lifecycleStage, 325 To: to, 326 DrawContext: w.glctx, 327 }) 328 w.lifecycleStage = to 329 } 330 } 331 332 //export lifecycleDeadAll 333 func lifecycleDeadAll() { sendLifecycleAll(lifecycle.StageDead) } 334 335 //export lifecycleAliveAll 336 func lifecycleAliveAll() { sendLifecycleAll(lifecycle.StageAlive) } 337 338 //export lifecycleVisible 339 func lifecycleVisible(id uintptr) { sendLifecycle(id, lifecycle.StageVisible) } 340 341 //export lifecycleFocused 342 func lifecycleFocused(id uintptr) { sendLifecycle(id, lifecycle.StageFocused) } 343 344 // cocoaRune marks the Carbon/Cocoa private-range unicode rune representing 345 // a non-unicode key event to -1, used for Rune in the key package. 346 // 347 // http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT 348 func cocoaRune(r rune) rune { 349 if '\uE000' <= r && r <= '\uF8FF' { 350 return -1 351 } 352 return r 353 } 354 355 // cocoaKeyCode converts a Carbon/Cocoa virtual key code number 356 // into the standard keycodes used by the key package. 357 // 358 // To get a sense of the key map, see the diagram on 359 // http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes 360 func cocoaKeyCode(vkcode uint16) key.Code { 361 switch vkcode { 362 case C.kVK_ANSI_A: 363 return key.CodeA 364 case C.kVK_ANSI_B: 365 return key.CodeB 366 case C.kVK_ANSI_C: 367 return key.CodeC 368 case C.kVK_ANSI_D: 369 return key.CodeD 370 case C.kVK_ANSI_E: 371 return key.CodeE 372 case C.kVK_ANSI_F: 373 return key.CodeF 374 case C.kVK_ANSI_G: 375 return key.CodeG 376 case C.kVK_ANSI_H: 377 return key.CodeH 378 case C.kVK_ANSI_I: 379 return key.CodeI 380 case C.kVK_ANSI_J: 381 return key.CodeJ 382 case C.kVK_ANSI_K: 383 return key.CodeK 384 case C.kVK_ANSI_L: 385 return key.CodeL 386 case C.kVK_ANSI_M: 387 return key.CodeM 388 case C.kVK_ANSI_N: 389 return key.CodeN 390 case C.kVK_ANSI_O: 391 return key.CodeO 392 case C.kVK_ANSI_P: 393 return key.CodeP 394 case C.kVK_ANSI_Q: 395 return key.CodeQ 396 case C.kVK_ANSI_R: 397 return key.CodeR 398 case C.kVK_ANSI_S: 399 return key.CodeS 400 case C.kVK_ANSI_T: 401 return key.CodeT 402 case C.kVK_ANSI_U: 403 return key.CodeU 404 case C.kVK_ANSI_V: 405 return key.CodeV 406 case C.kVK_ANSI_W: 407 return key.CodeW 408 case C.kVK_ANSI_X: 409 return key.CodeX 410 case C.kVK_ANSI_Y: 411 return key.CodeY 412 case C.kVK_ANSI_Z: 413 return key.CodeZ 414 case C.kVK_ANSI_1: 415 return key.Code1 416 case C.kVK_ANSI_2: 417 return key.Code2 418 case C.kVK_ANSI_3: 419 return key.Code3 420 case C.kVK_ANSI_4: 421 return key.Code4 422 case C.kVK_ANSI_5: 423 return key.Code5 424 case C.kVK_ANSI_6: 425 return key.Code6 426 case C.kVK_ANSI_7: 427 return key.Code7 428 case C.kVK_ANSI_8: 429 return key.Code8 430 case C.kVK_ANSI_9: 431 return key.Code9 432 case C.kVK_ANSI_0: 433 return key.Code0 434 // TODO: move the rest of these codes to constants in key.go 435 // if we are happy with them. 436 case C.kVK_Return: 437 return key.CodeReturnEnter 438 case C.kVK_Escape: 439 return key.CodeEscape 440 case C.kVK_Delete: 441 return key.CodeDeleteBackspace 442 case C.kVK_Tab: 443 return key.CodeTab 444 case C.kVK_Space: 445 return key.CodeSpacebar 446 case C.kVK_ANSI_Minus: 447 return key.CodeHyphenMinus 448 case C.kVK_ANSI_Equal: 449 return key.CodeEqualSign 450 case C.kVK_ANSI_LeftBracket: 451 return key.CodeLeftSquareBracket 452 case C.kVK_ANSI_RightBracket: 453 return key.CodeRightSquareBracket 454 case C.kVK_ANSI_Backslash: 455 return key.CodeBackslash 456 // 50: Keyboard Non-US "#" and ~ 457 case C.kVK_ANSI_Semicolon: 458 return key.CodeSemicolon 459 case C.kVK_ANSI_Quote: 460 return key.CodeApostrophe 461 case C.kVK_ANSI_Grave: 462 return key.CodeGraveAccent 463 case C.kVK_ANSI_Comma: 464 return key.CodeComma 465 case C.kVK_ANSI_Period: 466 return key.CodeFullStop 467 case C.kVK_ANSI_Slash: 468 return key.CodeSlash 469 case C.kVK_CapsLock: 470 return key.CodeCapsLock 471 case C.kVK_F1: 472 return key.CodeF1 473 case C.kVK_F2: 474 return key.CodeF2 475 case C.kVK_F3: 476 return key.CodeF3 477 case C.kVK_F4: 478 return key.CodeF4 479 case C.kVK_F5: 480 return key.CodeF5 481 case C.kVK_F6: 482 return key.CodeF6 483 case C.kVK_F7: 484 return key.CodeF7 485 case C.kVK_F8: 486 return key.CodeF8 487 case C.kVK_F9: 488 return key.CodeF9 489 case C.kVK_F10: 490 return key.CodeF10 491 case C.kVK_F11: 492 return key.CodeF11 493 case C.kVK_F12: 494 return key.CodeF12 495 // 70: PrintScreen 496 // 71: Scroll Lock 497 // 72: Pause 498 // 73: Insert 499 case C.kVK_Home: 500 return key.CodeHome 501 case C.kVK_PageUp: 502 return key.CodePageUp 503 case C.kVK_ForwardDelete: 504 return key.CodeDeleteForward 505 case C.kVK_End: 506 return key.CodeEnd 507 case C.kVK_PageDown: 508 return key.CodePageDown 509 case C.kVK_RightArrow: 510 return key.CodeRightArrow 511 case C.kVK_LeftArrow: 512 return key.CodeLeftArrow 513 case C.kVK_DownArrow: 514 return key.CodeDownArrow 515 case C.kVK_UpArrow: 516 return key.CodeUpArrow 517 case C.kVK_ANSI_KeypadClear: 518 return key.CodeKeypadNumLock 519 case C.kVK_ANSI_KeypadDivide: 520 return key.CodeKeypadSlash 521 case C.kVK_ANSI_KeypadMultiply: 522 return key.CodeKeypadAsterisk 523 case C.kVK_ANSI_KeypadMinus: 524 return key.CodeKeypadHyphenMinus 525 case C.kVK_ANSI_KeypadPlus: 526 return key.CodeKeypadPlusSign 527 case C.kVK_ANSI_KeypadEnter: 528 return key.CodeKeypadEnter 529 case C.kVK_ANSI_Keypad1: 530 return key.CodeKeypad1 531 case C.kVK_ANSI_Keypad2: 532 return key.CodeKeypad2 533 case C.kVK_ANSI_Keypad3: 534 return key.CodeKeypad3 535 case C.kVK_ANSI_Keypad4: 536 return key.CodeKeypad4 537 case C.kVK_ANSI_Keypad5: 538 return key.CodeKeypad5 539 case C.kVK_ANSI_Keypad6: 540 return key.CodeKeypad6 541 case C.kVK_ANSI_Keypad7: 542 return key.CodeKeypad7 543 case C.kVK_ANSI_Keypad8: 544 return key.CodeKeypad8 545 case C.kVK_ANSI_Keypad9: 546 return key.CodeKeypad9 547 case C.kVK_ANSI_Keypad0: 548 return key.CodeKeypad0 549 case C.kVK_ANSI_KeypadDecimal: 550 return key.CodeKeypadFullStop 551 case C.kVK_ANSI_KeypadEquals: 552 return key.CodeKeypadEqualSign 553 case C.kVK_F13: 554 return key.CodeF13 555 case C.kVK_F14: 556 return key.CodeF14 557 case C.kVK_F15: 558 return key.CodeF15 559 case C.kVK_F16: 560 return key.CodeF16 561 case C.kVK_F17: 562 return key.CodeF17 563 case C.kVK_F18: 564 return key.CodeF18 565 case C.kVK_F19: 566 return key.CodeF19 567 case C.kVK_F20: 568 return key.CodeF20 569 // 116: Keyboard Execute 570 case C.kVK_Help: 571 return key.CodeHelp 572 // 118: Keyboard Menu 573 // 119: Keyboard Select 574 // 120: Keyboard Stop 575 // 121: Keyboard Again 576 // 122: Keyboard Undo 577 // 123: Keyboard Cut 578 // 124: Keyboard Copy 579 // 125: Keyboard Paste 580 // 126: Keyboard Find 581 case C.kVK_Mute: 582 return key.CodeMute 583 case C.kVK_VolumeUp: 584 return key.CodeVolumeUp 585 case C.kVK_VolumeDown: 586 return key.CodeVolumeDown 587 // 130: Keyboard Locking Caps Lock 588 // 131: Keyboard Locking Num Lock 589 // 132: Keyboard Locking Scroll Lock 590 // 133: Keyboard Comma 591 // 134: Keyboard Equal Sign 592 // ...: Bunch of stuff 593 case C.kVK_Control: 594 return key.CodeLeftControl 595 case C.kVK_Shift: 596 return key.CodeLeftShift 597 case C.kVK_Option: 598 return key.CodeLeftAlt 599 case C.kVK_Command: 600 return key.CodeLeftGUI 601 case C.kVK_RightControl: 602 return key.CodeRightControl 603 case C.kVK_RightShift: 604 return key.CodeRightShift 605 case C.kVK_RightOption: 606 return key.CodeRightAlt 607 // TODO key.CodeRightGUI 608 default: 609 return key.CodeUnknown 610 } 611 }