| OLD | NEW |
| (Empty) |
| 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 <Cocoa/Cocoa.h> | |
| 17 #import <OpenGL/gl.h> | |
| 18 #include <pthread.h> | |
| 19 | |
| 20 void glGenVertexArrays(GLsizei n, GLuint* array); | |
| 21 void glBindVertexArray(GLuint array); | |
| 22 | |
| 23 void runApp(void); | |
| 24 void lockContext(GLintptr); | |
| 25 void unlockContext(GLintptr); | |
| 26 double backingScaleFactor(); | |
| 27 uint64 threadID(); | |
| 28 | |
| 29 */ | |
| 30 import "C" | |
| 31 import ( | |
| 32 "log" | |
| 33 "runtime" | |
| 34 "sync" | |
| 35 | |
| 36 "golang.org/x/mobile/event" | |
| 37 "golang.org/x/mobile/geom" | |
| 38 "golang.org/x/mobile/gl" | |
| 39 ) | |
| 40 | |
| 41 var initThreadID uint64 | |
| 42 | |
| 43 func init() { | |
| 44 // Lock the goroutine responsible for initialization to an OS thread. | |
| 45 // This means the goroutine running main (and calling the run function | |
| 46 // below) is locked to the OS thread that started the program. This is | |
| 47 // necessary for the correct delivery of Cocoa events to the process. | |
| 48 // | |
| 49 // A discussion on this topic: | |
| 50 // https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZB
elsYJ | |
| 51 runtime.LockOSThread() | |
| 52 initThreadID = uint64(C.threadID()) | |
| 53 } | |
| 54 | |
| 55 func run(callbacks Callbacks) { | |
| 56 if tid := uint64(C.threadID()); tid != initThreadID { | |
| 57 log.Fatalf("app.Run called on thread %d, but app.init ran on %d"
, tid, initThreadID) | |
| 58 } | |
| 59 cb = callbacks | |
| 60 C.runApp() | |
| 61 } | |
| 62 | |
| 63 //export setGeom | |
| 64 func setGeom(pixelsPerPt float32, width, height int) { | |
| 65 // Macs default to 72 DPI, so scales are equivalent. | |
| 66 geom.PixelsPerPt = pixelsPerPt | |
| 67 geom.Width = geom.Pt(float32(width) / pixelsPerPt) | |
| 68 geom.Height = geom.Pt(float32(height) / pixelsPerPt) | |
| 69 } | |
| 70 | |
| 71 func initGL() { | |
| 72 // Using attribute arrays in OpenGL 3.3 requires the use of a VBA. | |
| 73 // But VBAs don't exist in ES 2. So we bind a default one. | |
| 74 var id C.GLuint | |
| 75 C.glGenVertexArrays(1, &id) | |
| 76 C.glBindVertexArray(id) | |
| 77 if cb.Start != nil { | |
| 78 cb.Start() | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 var cb Callbacks | |
| 83 var initGLOnce sync.Once | |
| 84 | |
| 85 var events struct { | |
| 86 sync.Mutex | |
| 87 pending []event.Touch | |
| 88 } | |
| 89 | |
| 90 func sendTouch(ty event.TouchType, x, y float32) { | |
| 91 events.Lock() | |
| 92 events.pending = append(events.pending, event.Touch{ | |
| 93 Type: ty, | |
| 94 Loc: geom.Point{ | |
| 95 X: geom.Pt(x / geom.PixelsPerPt), | |
| 96 Y: geom.Height - geom.Pt(y/geom.PixelsPerPt), | |
| 97 }, | |
| 98 }) | |
| 99 events.Unlock() | |
| 100 } | |
| 101 | |
| 102 //export eventMouseDown | |
| 103 func eventMouseDown(x, y float32) { sendTouch(event.TouchStart, x, y) } | |
| 104 | |
| 105 //export eventMouseDragged | |
| 106 func eventMouseDragged(x, y float32) { sendTouch(event.TouchMove, x, y) } | |
| 107 | |
| 108 //export eventMouseEnd | |
| 109 func eventMouseEnd(x, y float32) { sendTouch(event.TouchEnd, x, y) } | |
| 110 | |
| 111 //export drawgl | |
| 112 func drawgl(ctx C.GLintptr) { | |
| 113 // The call to lockContext loads the OpenGL context into | |
| 114 // thread-local storage for use by the underlying GL calls | |
| 115 // done in the user's Draw function. We need to stay on | |
| 116 // the same thread throughout Draw, so we LockOSThread. | |
| 117 runtime.LockOSThread() | |
| 118 C.lockContext(ctx) | |
| 119 | |
| 120 initGLOnce.Do(initGL) | |
| 121 | |
| 122 events.Lock() | |
| 123 pending := events.pending | |
| 124 events.pending = nil | |
| 125 events.Unlock() | |
| 126 for _, e := range pending { | |
| 127 if cb.Touch != nil { | |
| 128 cb.Touch(e) | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 // TODO: is the library or the app responsible for clearing the buffers? | |
| 133 gl.ClearColor(0, 0, 0, 1) | |
| 134 gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) | |
| 135 if cb.Draw != nil { | |
| 136 cb.Draw() | |
| 137 } | |
| 138 | |
| 139 C.unlockContext(ctx) | |
| 140 | |
| 141 // This may unlock the original main thread, but that's OK, | |
| 142 // because by the time it does the thread has already entered | |
| 143 // C.runApp, which won't give the thread up. | |
| 144 runtime.UnlockOSThread() | |
| 145 } | |
| OLD | NEW |