go.mobile/app: comments from cl/147910043 and fix darwin mouse drag events

LGTM=nigeltao
R=nigeltao, hyangah
CC=golang-codereviews
https://golang.org/cl/146440043
This commit is contained in:
David Crawshaw 2014-10-22 20:24:05 -04:00
parent fab1e51609
commit 479974ec25
2 changed files with 95 additions and 29 deletions

View File

@ -15,6 +15,7 @@ package app
#cgo LDFLAGS: -framework Cocoa -framework OpenGL -framework QuartzCore
#import <Cocoa/Cocoa.h>
#import <OpenGL/gl.h>
#include <pthread.h>
void glGenVertexArrays(GLsizei n, GLuint* array);
void glBindVertexArray(GLuint array);
@ -23,9 +24,12 @@ void runApp(void);
void lockContext(GLintptr);
void unlockContext(GLintptr);
double backingScaleFactor();
uint64 threadID();
*/
import "C"
import (
"log"
"runtime"
"sync"
@ -34,26 +38,39 @@ import (
"code.google.com/p/go.mobile/gl"
)
var initThreadID uint64
func init() {
// Lock the goroutine responsible for initialization to an OS thread.
// This means the goroutine running main (and calling the run function
// 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 run(callbacks Callbacks) {
if tid := uint64(C.threadID()); tid != initThreadID {
log.Fatalf("app.Run called on thread %d, but app.init ran on %d", tid, initThreadID)
}
cb = callbacks
C.runApp()
}
//export setGeom
func setGeom(pixelsPerPt, width, height float64) {
func setGeom(pixelsPerPt float32, width, height int) {
// Macs default to 72 DPI, so scales are equivalent.
geom.PixelsPerPt = float32(pixelsPerPt)
geom.Width = geom.Pt(width)
geom.Height = geom.Pt(height)
geom.PixelsPerPt = pixelsPerPt
geom.Width = geom.Pt(float32(width) / pixelsPerPt)
geom.Height = geom.Pt(float32(height) / pixelsPerPt)
}
func initGL() {
// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
// But VBAs don't exist in ES 2. So we bind one default one.
// But VBAs don't exist in ES 2. So we bind a default one.
var id C.GLuint
C.glGenVertexArrays(1, &id)
C.glBindVertexArray(id)
@ -61,43 +78,51 @@ func initGL() {
var cb Callbacks
var initGLOnce sync.Once
var events = make(chan event.Touch, 1<<6)
var events struct {
sync.Mutex
pending []event.Touch
}
func sendTouch(ty event.TouchType, x, y float32) {
events <- event.Touch{
events.Lock()
events.pending = append(events.pending, event.Touch{
Type: ty,
Loc: geom.Point{
X: geom.Pt(x),
Y: geom.Height - geom.Pt(y),
X: geom.Pt(x / geom.PixelsPerPt),
Y: geom.Height - geom.Pt(y/geom.PixelsPerPt),
},
}
})
events.Unlock()
}
//export eventMouseDown
func eventMouseDown(x, y float32) { sendTouch(event.TouchStart, x, y) }
//export eventMouseMove
func eventMouseMove(x, y float32) { sendTouch(event.TouchMove, x, y) }
//export eventMouseDragged
func eventMouseDragged(x, y float32) { sendTouch(event.TouchMove, x, y) }
//export eventMouseEnd
func eventMouseEnd(x, y float32) { sendTouch(event.TouchEnd, x, y) }
//export drawgl
func drawgl(ctx C.GLintptr) {
// The call to lockContext loads the OpenGL context into
// thread-local storage for use by the underlying GL calls
// done in the user's Draw function. We need to stay on
// the same thread throughout Draw, so we LockOSThread.
runtime.LockOSThread()
C.lockContext(ctx)
initGLOnce.Do(initGL)
loop:
for {
select {
case e := <-events:
if cb.Touch != nil {
cb.Touch(e)
}
default:
break loop
events.Lock()
pending := events.pending
events.pending = nil
events.Unlock()
for _, e := range pending {
if cb.Touch != nil {
cb.Touch(e)
}
}
@ -109,5 +134,9 @@ loop:
}
C.unlockContext(ctx)
// This may unlock the original main thread, but that's OK,
// because by the time it does the thread has already entered
// C.runApp, which won't give the thread up.
runtime.UnlockOSThread()
}

View File

@ -5,6 +5,7 @@
// +build darwin
#include "_cgo_export.h"
#include <pthread.h>
#include <stdio.h>
#import <Cocoa/Cocoa.h>
@ -34,6 +35,14 @@ void unlockContext(GLintptr context) {
}
uint64 threadID() {
uint64 id;
if (pthread_threadid_np(pthread_self(), &id)) {
abort();
}
return id;
}
@interface MobileGLView : NSOpenGLView
{
@ -43,7 +52,7 @@ void unlockContext(GLintptr context) {
@implementation MobileGLView
- (void)prepareOpenGL {
[self setWantsBestResolutionOpenGLSurface:true];
[self setWantsBestResolutionOpenGLSurface:YES];
GLint swapInt = 1;
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
@ -57,24 +66,52 @@ void unlockContext(GLintptr context) {
}
- (void)reshape {
// Calculate screen PPI.
//
// Note that the backingScaleFactor converts from logical
// pixels to actual pixels, but both of these units vary
// independently from real world size. E.g.
//
// 13" Retina Macbook Pro, 2560x1600, 227ppi, backingScaleFactor=2, scale=3.15
// 15" Retina Macbook Pro, 2880x1800, 220ppi, backingScaleFactor=2, scale=3.06
// 27" iMac, 2560x1440, 109ppi, backingScaleFactor=1, scale=1.51
// 27" Retina iMac, 5120x2880, 218ppi, backingScaleFactor=2, scale=3.03
NSScreen *screen = [NSScreen mainScreen];
double screenPixW = [screen frame].size.width * [screen backingScaleFactor];
CGDirectDisplayID display = (CGDirectDisplayID)[[[screen deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
CGSize screenSizeMM = CGDisplayScreenSize(display); // in millimeters
float ppi = 25.4 * screenPixW / screenSizeMM.width;
float pixelsPerPt = ppi/72.0;
// The width and height reported to the geom package are the
// bounds of the OpenGL view. Several steps are necessary.
// First, [self bounds] gives us the number of logical pixels
// in the view. Multiplying this by the backingScaleFactor
// gives us the number of actual pixels.
NSRect r = [self bounds];
double scale = [[NSScreen mainScreen] backingScaleFactor];
setGeom(scale, r.size.width, r.size.height);
int w = r.size.width * [screen backingScaleFactor];
int h = r.size.height * [screen backingScaleFactor];
setGeom(pixelsPerPt, w, h);
}
- (void)mouseDown:(NSEvent *)theEvent {
double scale = [[NSScreen mainScreen] backingScaleFactor];
NSPoint p = [theEvent locationInWindow];
eventMouseDown(p.x, p.y);
eventMouseDown(p.x * scale, p.y * scale);
}
- (void)mouseUp:(NSEvent *)theEvent {
double scale = [[NSScreen mainScreen] backingScaleFactor];
NSPoint p = [theEvent locationInWindow];
eventMouseEnd(p.x, p.y);
eventMouseEnd(p.x * scale, p.y * scale);
}
- (void)mouseMoved:(NSEvent *)theEvent {
- (void)mouseDragged:(NSEvent *)theEvent {
double scale = [[NSScreen mainScreen] backingScaleFactor];
NSPoint p = [theEvent locationInWindow];
eventMouseMove(p.x, p.y);
eventMouseDragged(p.x * scale, p.y * scale);
}
@end
@ -97,7 +134,7 @@ runApp(void) {
[menu addItem:quitMenuItem];
[menuItem setSubmenu:menu];
NSRect rect = NSMakeRect(0, 0, 200, 200);
NSRect rect = NSMakeRect(0, 0, 400, 400);
id window = [[[NSWindow alloc] initWithContentRect:rect
styleMask:NSTitledWindowMask