github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/app/darwin_amd64.go (about) 1 // Copyright 2014 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 7 package app 8 9 // Simple on-screen app debugging for OS X. Not an officially supported 10 // development target for apps, as screens with mice are very different 11 // than screens with touch panels. 12 13 /* 14 #cgo CFLAGS: -x objective-c 15 #cgo LDFLAGS: -framework Cocoa -framework OpenGL -framework QuartzCore 16 #import <Carbon/Carbon.h> // for HIToolbox/Events.h 17 #import <Cocoa/Cocoa.h> 18 #include <pthread.h> 19 20 void runApp(void); 21 void stopApp(void); 22 void makeCurrentContext(GLintptr); 23 uint64 threadID(); 24 */ 25 import "C" 26 import ( 27 "log" 28 "runtime" 29 "sync" 30 31 "github.com/c-darwin/mobile/event/key" 32 "github.com/c-darwin/mobile/event/lifecycle" 33 "github.com/c-darwin/mobile/event/paint" 34 "github.com/c-darwin/mobile/event/size" 35 "github.com/c-darwin/mobile/event/touch" 36 "github.com/c-darwin/mobile/geom" 37 "github.com/c-darwin/mobile/gl" 38 ) 39 40 var initThreadID uint64 41 42 func init() { 43 // Lock the goroutine responsible for initialization to an OS thread. 44 // This means the goroutine running main (and calling runApp below) 45 // is locked to the OS thread that started the program. This is 46 // necessary for the correct delivery of Cocoa events to the process. 47 // 48 // A discussion on this topic: 49 // https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ 50 runtime.LockOSThread() 51 initThreadID = uint64(C.threadID()) 52 } 53 54 func main(f func(App)) { 55 if tid := uint64(C.threadID()); tid != initThreadID { 56 log.Fatalf("app.Main called on thread %d, but app.init ran on %d", tid, initThreadID) 57 } 58 59 go func() { 60 f(app{}) 61 C.stopApp() 62 // TODO(crawshaw): trigger runApp to return 63 }() 64 65 C.runApp() 66 } 67 68 // loop is the primary drawing loop. 69 // 70 // After Cocoa has captured the initial OS thread for processing Cocoa 71 // events in runApp, it starts loop on another goroutine. It is locked 72 // to an OS thread for its OpenGL context. 73 // 74 // Two Cocoa threads deliver draw signals to loop. The primary source of 75 // draw events is the CVDisplayLink timer, which is tied to the display 76 // vsync. Secondary draw events come from [NSView drawRect:] when the 77 // window is resized. 78 func loop(ctx C.GLintptr) { 79 runtime.LockOSThread() 80 C.makeCurrentContext(ctx) 81 82 for range draw { 83 eventsIn <- paint.Event{} 84 loop1: 85 for { 86 select { 87 case <-gl.WorkAvailable: 88 gl.DoWork() 89 case <-endPaint: 90 C.CGLFlushDrawable(C.CGLGetCurrentContext()) 91 break loop1 92 } 93 } 94 drawDone <- struct{}{} 95 } 96 } 97 98 var ( 99 draw = make(chan struct{}) 100 drawDone = make(chan struct{}) 101 ) 102 103 //export drawgl 104 func drawgl() { 105 draw <- struct{}{} 106 <-drawDone 107 } 108 109 //export startloop 110 func startloop(ctx C.GLintptr) { 111 go loop(ctx) 112 } 113 114 var windowHeightPx float32 115 116 //export setGeom 117 func setGeom(pixelsPerPt float32, widthPx, heightPx int) { 118 windowHeightPx = float32(heightPx) 119 eventsIn <- size.Event{ 120 WidthPx: widthPx, 121 HeightPx: heightPx, 122 WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt), 123 HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt), 124 PixelsPerPt: pixelsPerPt, 125 } 126 } 127 128 var touchEvents struct { 129 sync.Mutex 130 pending []touch.Event 131 } 132 133 func sendTouch(t touch.Type, x, y float32) { 134 eventsIn <- touch.Event{ 135 X: x, 136 Y: windowHeightPx - y, 137 Sequence: 0, 138 Type: t, 139 } 140 } 141 142 //export eventMouseDown 143 func eventMouseDown(x, y float32) { sendTouch(touch.TypeBegin, x, y) } 144 145 //export eventMouseDragged 146 func eventMouseDragged(x, y float32) { sendTouch(touch.TypeMove, x, y) } 147 148 //export eventMouseEnd 149 func eventMouseEnd(x, y float32) { sendTouch(touch.TypeEnd, x, y) } 150 151 //export lifecycleDead 152 func lifecycleDead() { sendLifecycle(lifecycle.StageDead) } 153 154 //export eventKey 155 func eventKey(runeVal int32, direction uint8, code uint16, flags uint32) { 156 var modifiers key.Modifiers 157 for _, mod := range mods { 158 if flags&mod.flags == mod.flags { 159 modifiers |= mod.mod 160 } 161 } 162 163 eventsIn <- key.Event{ 164 Rune: convRune(rune(runeVal)), 165 Code: convVirtualKeyCode(code), 166 Modifiers: modifiers, 167 Direction: key.Direction(direction), 168 } 169 } 170 171 //export eventFlags 172 func eventFlags(flags uint32) { 173 for _, mod := range mods { 174 if flags&mod.flags == mod.flags && lastFlags&mod.flags != mod.flags { 175 eventKey(-1, uint8(key.DirPress), mod.code, flags) 176 } 177 if lastFlags&mod.flags == mod.flags && flags&mod.flags != mod.flags { 178 eventKey(-1, uint8(key.DirRelease), mod.code, flags) 179 } 180 } 181 lastFlags = flags 182 } 183 184 var lastFlags uint32 185 186 var mods = [...]struct { 187 flags uint32 188 code uint16 189 mod key.Modifiers 190 }{ 191 // Left and right variants of modifier keys have their own masks, 192 // but they are not documented. These were determined empirically. 193 {1<<17 | 0x102, C.kVK_Shift, key.ModShift}, 194 {1<<17 | 0x104, C.kVK_RightShift, key.ModShift}, 195 {1<<18 | 0x101, C.kVK_Control, key.ModControl}, 196 // TODO key.ControlRight 197 {1<<19 | 0x120, C.kVK_Option, key.ModAlt}, 198 {1<<19 | 0x140, C.kVK_RightOption, key.ModAlt}, 199 {1<<20 | 0x108, C.kVK_Command, key.ModMeta}, 200 {1<<20 | 0x110, C.kVK_Command, key.ModMeta}, // TODO: missing kVK_RightCommand 201 } 202 203 //export lifecycleAlive 204 func lifecycleAlive() { sendLifecycle(lifecycle.StageAlive) } 205 206 //export lifecycleVisible 207 func lifecycleVisible() { sendLifecycle(lifecycle.StageVisible) } 208 209 //export lifecycleFocused 210 func lifecycleFocused() { sendLifecycle(lifecycle.StageFocused) } 211 212 // convRune marks the Carbon/Cocoa private-range unicode rune representing 213 // a non-unicode key event to -1, used for Rune in the key package. 214 // 215 // http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT 216 func convRune(r rune) rune { 217 if '\uE000' <= r && r <= '\uF8FF' { 218 return -1 219 } 220 return r 221 } 222 223 // convVirtualKeyCode converts a Carbon/Cocoa virtual key code number 224 // into the standard keycodes used by the key package. 225 // 226 // To get a sense of the key map, see the diagram on 227 // http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes 228 func convVirtualKeyCode(vkcode uint16) key.Code { 229 switch vkcode { 230 case C.kVK_ANSI_A: 231 return key.CodeA 232 case C.kVK_ANSI_B: 233 return key.CodeB 234 case C.kVK_ANSI_C: 235 return key.CodeC 236 case C.kVK_ANSI_D: 237 return key.CodeD 238 case C.kVK_ANSI_E: 239 return key.CodeE 240 case C.kVK_ANSI_F: 241 return key.CodeF 242 case C.kVK_ANSI_G: 243 return key.CodeG 244 case C.kVK_ANSI_H: 245 return key.CodeH 246 case C.kVK_ANSI_I: 247 return key.CodeI 248 case C.kVK_ANSI_J: 249 return key.CodeJ 250 case C.kVK_ANSI_K: 251 return key.CodeK 252 case C.kVK_ANSI_L: 253 return key.CodeL 254 case C.kVK_ANSI_M: 255 return key.CodeM 256 case C.kVK_ANSI_N: 257 return key.CodeN 258 case C.kVK_ANSI_O: 259 return key.CodeO 260 case C.kVK_ANSI_P: 261 return key.CodeP 262 case C.kVK_ANSI_Q: 263 return key.CodeQ 264 case C.kVK_ANSI_R: 265 return key.CodeR 266 case C.kVK_ANSI_S: 267 return key.CodeS 268 case C.kVK_ANSI_T: 269 return key.CodeT 270 case C.kVK_ANSI_U: 271 return key.CodeU 272 case C.kVK_ANSI_V: 273 return key.CodeV 274 case C.kVK_ANSI_W: 275 return key.CodeW 276 case C.kVK_ANSI_X: 277 return key.CodeX 278 case C.kVK_ANSI_Y: 279 return key.CodeY 280 case C.kVK_ANSI_Z: 281 return key.CodeZ 282 case C.kVK_ANSI_1: 283 return key.Code1 284 case C.kVK_ANSI_2: 285 return key.Code2 286 case C.kVK_ANSI_3: 287 return key.Code3 288 case C.kVK_ANSI_4: 289 return key.Code4 290 case C.kVK_ANSI_5: 291 return key.Code5 292 case C.kVK_ANSI_6: 293 return key.Code6 294 case C.kVK_ANSI_7: 295 return key.Code7 296 case C.kVK_ANSI_8: 297 return key.Code8 298 case C.kVK_ANSI_9: 299 return key.Code9 300 case C.kVK_ANSI_0: 301 return key.Code0 302 // TODO: move the rest of these codes to constants in key.go 303 // if we are happy with them. 304 case C.kVK_Return: 305 return key.CodeReturnEnter 306 case C.kVK_Escape: 307 return key.CodeEscape 308 case C.kVK_Delete: 309 return key.CodeDeleteBackspace 310 case C.kVK_Tab: 311 return key.CodeTab 312 case C.kVK_Space: 313 return key.CodeSpacebar 314 case C.kVK_ANSI_Minus: 315 return key.CodeHyphenMinus 316 case C.kVK_ANSI_Equal: 317 return key.CodeEqualSign 318 case C.kVK_ANSI_LeftBracket: 319 return key.CodeLeftSquareBracket 320 case C.kVK_ANSI_RightBracket: 321 return key.CodeRightSquareBracket 322 case C.kVK_ANSI_Backslash: 323 return key.CodeBackslash 324 // 50: Keyboard Non-US "#" and ~ 325 case C.kVK_ANSI_Semicolon: 326 return key.CodeSemicolon 327 case C.kVK_ANSI_Quote: 328 return key.CodeApostrophe 329 case C.kVK_ANSI_Grave: 330 return key.CodeGraveAccent 331 case C.kVK_ANSI_Comma: 332 return key.CodeComma 333 case C.kVK_ANSI_Period: 334 return key.CodeFullStop 335 case C.kVK_ANSI_Slash: 336 return key.CodeSlash 337 case C.kVK_CapsLock: 338 return key.CodeCapsLock 339 case C.kVK_F1: 340 return key.CodeF1 341 case C.kVK_F2: 342 return key.CodeF2 343 case C.kVK_F3: 344 return key.CodeF3 345 case C.kVK_F4: 346 return key.CodeF4 347 case C.kVK_F5: 348 return key.CodeF5 349 case C.kVK_F6: 350 return key.CodeF6 351 case C.kVK_F7: 352 return key.CodeF7 353 case C.kVK_F8: 354 return key.CodeF8 355 case C.kVK_F9: 356 return key.CodeF9 357 case C.kVK_F10: 358 return key.CodeF10 359 case C.kVK_F11: 360 return key.CodeF11 361 case C.kVK_F12: 362 return key.CodeF12 363 // 70: PrintScreen 364 // 71: Scroll Lock 365 // 72: Pause 366 // 73: Insert 367 case C.kVK_Home: 368 return key.CodeHome 369 case C.kVK_PageUp: 370 return key.CodePageUp 371 case C.kVK_ForwardDelete: 372 return key.CodeDeleteForward 373 case C.kVK_End: 374 return key.CodeEnd 375 case C.kVK_PageDown: 376 return key.CodePageDown 377 case C.kVK_RightArrow: 378 return key.CodeRightArrow 379 case C.kVK_LeftArrow: 380 return key.CodeLeftArrow 381 case C.kVK_DownArrow: 382 return key.CodeDownArrow 383 case C.kVK_UpArrow: 384 return key.CodeUpArrow 385 case C.kVK_ANSI_KeypadClear: 386 return key.CodeKeypadNumLock 387 case C.kVK_ANSI_KeypadDivide: 388 return key.CodeKeypadSlash 389 case C.kVK_ANSI_KeypadMultiply: 390 return key.CodeKeypadAsterisk 391 case C.kVK_ANSI_KeypadMinus: 392 return key.CodeKeypadHyphenMinus 393 case C.kVK_ANSI_KeypadPlus: 394 return key.CodeKeypadPlusSign 395 case C.kVK_ANSI_KeypadEnter: 396 return key.CodeKeypadEnter 397 case C.kVK_ANSI_Keypad1: 398 return key.CodeKeypad1 399 case C.kVK_ANSI_Keypad2: 400 return key.CodeKeypad2 401 case C.kVK_ANSI_Keypad3: 402 return key.CodeKeypad3 403 case C.kVK_ANSI_Keypad4: 404 return key.CodeKeypad4 405 case C.kVK_ANSI_Keypad5: 406 return key.CodeKeypad5 407 case C.kVK_ANSI_Keypad6: 408 return key.CodeKeypad6 409 case C.kVK_ANSI_Keypad7: 410 return key.CodeKeypad7 411 case C.kVK_ANSI_Keypad8: 412 return key.CodeKeypad8 413 case C.kVK_ANSI_Keypad9: 414 return key.CodeKeypad9 415 case C.kVK_ANSI_Keypad0: 416 return key.CodeKeypad0 417 case C.kVK_ANSI_KeypadDecimal: 418 return key.CodeKeypadFullStop 419 case C.kVK_ANSI_KeypadEquals: 420 return key.CodeKeypadEqualSign 421 case C.kVK_F13: 422 return key.CodeF13 423 case C.kVK_F14: 424 return key.CodeF14 425 case C.kVK_F15: 426 return key.CodeF15 427 case C.kVK_F16: 428 return key.CodeF16 429 case C.kVK_F17: 430 return key.CodeF17 431 case C.kVK_F18: 432 return key.CodeF18 433 case C.kVK_F19: 434 return key.CodeF19 435 case C.kVK_F20: 436 return key.CodeF20 437 // 116: Keyboard Execute 438 case C.kVK_Help: 439 return key.CodeHelp 440 // 118: Keyboard Menu 441 // 119: Keyboard Select 442 // 120: Keyboard Stop 443 // 121: Keyboard Again 444 // 122: Keyboard Undo 445 // 123: Keyboard Cut 446 // 124: Keyboard Copy 447 // 125: Keyboard Paste 448 // 126: Keyboard Find 449 case C.kVK_Mute: 450 return key.CodeMute 451 case C.kVK_VolumeUp: 452 return key.CodeVolumeUp 453 case C.kVK_VolumeDown: 454 return key.CodeVolumeDown 455 // 130: Keyboard Locking Caps Lock 456 // 131: Keyboard Locking Num Lock 457 // 132: Keyboard Locking Scroll Lock 458 // 133: Keyboard Comma 459 // 134: Keyboard Equal Sign 460 // ...: Bunch of stuff 461 case C.kVK_Control: 462 return key.CodeLeftControl 463 case C.kVK_Shift: 464 return key.CodeLeftShift 465 case C.kVK_Option: 466 return key.CodeLeftAlt 467 case C.kVK_Command: 468 return key.CodeLeftGUI 469 case C.kVK_RightControl: 470 return key.CodeRightControl 471 case C.kVK_RightShift: 472 return key.CodeRightShift 473 case C.kVK_RightOption: 474 return key.CodeRightAlt 475 // TODO key.CodeRightGUI 476 default: 477 return key.CodeUnknown 478 } 479 }