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:
Родитель
fab1e51609
Коммит
479974ec25
|
@ -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()
|
||||
}
|
||||
|
|
53
app/darwin.m
53
app/darwin.m
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче