2015-02-20 04:54:42 -05:00
|
|
|
// Copyright 2015 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// +build darwin
|
2015-05-26 10:18:04 -04:00
|
|
|
// +build arm arm64
|
2015-02-20 04:54:42 -05:00
|
|
|
|
|
|
|
package app
|
|
|
|
|
|
|
|
/*
|
|
|
|
#cgo CFLAGS: -x objective-c
|
|
|
|
#cgo LDFLAGS: -framework Foundation -framework UIKit -framework GLKit -framework OpenGLES -framework QuartzCore
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <pthread.h>
|
2015-08-10 10:15:43 -04:00
|
|
|
#include <UIKit/UIDevice.h>
|
2015-02-20 04:54:42 -05:00
|
|
|
|
|
|
|
extern struct utsname sysInfo;
|
|
|
|
|
|
|
|
void runApp(void);
|
|
|
|
void setContext(void* context);
|
|
|
|
uint64_t threadID();
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"runtime"
|
2015-06-27 10:28:28 -04:00
|
|
|
"strings"
|
2015-06-04 13:51:47 -04:00
|
|
|
"sync"
|
2015-02-20 04:54:42 -05:00
|
|
|
"unsafe"
|
|
|
|
|
2015-07-15 17:17:08 +10:00
|
|
|
"golang.org/x/mobile/event/lifecycle"
|
|
|
|
"golang.org/x/mobile/event/paint"
|
2015-08-13 19:59:18 +10:00
|
|
|
"golang.org/x/mobile/event/size"
|
2015-07-15 17:17:08 +10:00
|
|
|
"golang.org/x/mobile/event/touch"
|
2015-02-20 04:54:42 -05:00
|
|
|
"golang.org/x/mobile/geom"
|
|
|
|
)
|
|
|
|
|
|
|
|
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 UIKit 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())
|
|
|
|
}
|
|
|
|
|
2015-06-27 10:28:28 -04:00
|
|
|
func main(f func(App)) {
|
2015-02-20 04:54:42 -05:00
|
|
|
if tid := uint64(C.threadID()); tid != initThreadID {
|
|
|
|
log.Fatalf("app.Run called on thread %d, but app.init ran on %d", tid, initThreadID)
|
|
|
|
}
|
2015-06-27 10:28:28 -04:00
|
|
|
|
|
|
|
go func() {
|
2015-09-22 13:19:05 -04:00
|
|
|
f(theApp)
|
2015-06-27 10:28:28 -04:00
|
|
|
// TODO(crawshaw): trigger runApp to return
|
|
|
|
}()
|
2015-02-20 04:54:42 -05:00
|
|
|
C.runApp()
|
2015-06-27 10:28:28 -04:00
|
|
|
panic("unexpected return from app.runApp")
|
2015-02-20 04:54:42 -05:00
|
|
|
}
|
|
|
|
|
2015-08-10 11:18:50 -04:00
|
|
|
var pixelsPerPt float32
|
2015-06-27 10:28:28 -04:00
|
|
|
var screenScale int // [UIScreen mainScreen].scale, either 1, 2, or 3.
|
2015-02-20 04:54:42 -05:00
|
|
|
|
2015-06-27 10:28:28 -04:00
|
|
|
//export setScreen
|
|
|
|
func setScreen(scale int) {
|
2015-03-17 12:39:21 -04:00
|
|
|
C.uname(&C.sysInfo)
|
|
|
|
name := C.GoString(&C.sysInfo.machine[0])
|
2015-06-27 10:28:28 -04:00
|
|
|
|
|
|
|
var v float32
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(name, "iPhone"):
|
|
|
|
v = 163
|
|
|
|
case strings.HasPrefix(name, "iPad"):
|
|
|
|
// TODO: is there a better way to distinguish the iPad Mini?
|
|
|
|
switch name {
|
|
|
|
case "iPad2,5", "iPad2,6", "iPad2,7", "iPad4,4", "iPad4,5", "iPad4,6", "iPad4,7":
|
|
|
|
v = 163 // iPad Mini
|
|
|
|
default:
|
|
|
|
v = 132
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
v = 163 // names like i386 and x86_64 are the simulator
|
|
|
|
}
|
|
|
|
|
|
|
|
if v == 0 {
|
2015-05-29 14:59:01 -04:00
|
|
|
log.Printf("unknown machine: %s", name)
|
|
|
|
v = 163 // emergency fallback
|
2015-03-17 12:39:21 -04:00
|
|
|
}
|
2015-06-27 10:28:28 -04:00
|
|
|
|
|
|
|
pixelsPerPt = v * float32(scale) / 72
|
|
|
|
screenScale = scale
|
2015-02-20 04:54:42 -05:00
|
|
|
}
|
|
|
|
|
2015-06-27 10:28:28 -04:00
|
|
|
//export updateConfig
|
2015-08-10 10:15:43 -04:00
|
|
|
func updateConfig(width, height, orientation int32) {
|
2015-08-13 19:59:18 +10:00
|
|
|
o := size.OrientationUnknown
|
2015-08-10 10:15:43 -04:00
|
|
|
switch orientation {
|
|
|
|
case C.UIDeviceOrientationPortrait, C.UIDeviceOrientationPortraitUpsideDown:
|
2015-08-13 19:59:18 +10:00
|
|
|
o = size.OrientationPortrait
|
2015-08-10 10:15:43 -04:00
|
|
|
case C.UIDeviceOrientationLandscapeLeft, C.UIDeviceOrientationLandscapeRight:
|
2015-08-13 19:59:18 +10:00
|
|
|
o = size.OrientationLandscape
|
2015-08-10 10:15:43 -04:00
|
|
|
}
|
|
|
|
widthPx := screenScale * int(width)
|
|
|
|
heightPx := screenScale * int(height)
|
2015-09-22 13:19:05 -04:00
|
|
|
theApp.eventsIn <- size.Event{
|
2015-07-27 16:51:44 +10:00
|
|
|
WidthPx: widthPx,
|
|
|
|
HeightPx: heightPx,
|
|
|
|
WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
|
|
|
|
HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
|
2015-06-27 10:28:28 -04:00
|
|
|
PixelsPerPt: pixelsPerPt,
|
2015-08-10 10:15:43 -04:00
|
|
|
Orientation: o,
|
2015-03-17 12:39:21 -04:00
|
|
|
}
|
2015-02-20 04:54:42 -05:00
|
|
|
}
|
|
|
|
|
2015-05-26 14:33:20 -04:00
|
|
|
// touchIDs is the current active touches. The position in the array
|
|
|
|
// is the ID, the value is the UITouch* pointer value.
|
|
|
|
//
|
|
|
|
// It is widely reported that the iPhone can handle up to 5 simultaneous
|
|
|
|
// touch events, while the iPad can handle 11.
|
|
|
|
var touchIDs [11]uintptr
|
|
|
|
|
|
|
|
var touchEvents struct {
|
|
|
|
sync.Mutex
|
2015-07-15 17:17:08 +10:00
|
|
|
pending []touch.Event
|
2015-05-26 14:33:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//export sendTouch
|
2015-07-16 13:31:21 -04:00
|
|
|
func sendTouch(cTouch, cTouchType uintptr, x, y float32) {
|
2015-05-26 14:33:20 -04:00
|
|
|
id := -1
|
|
|
|
for i, val := range touchIDs {
|
2015-07-16 13:31:21 -04:00
|
|
|
if val == cTouch {
|
2015-05-26 14:33:20 -04:00
|
|
|
id = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if id == -1 {
|
|
|
|
for i, val := range touchIDs {
|
|
|
|
if val == 0 {
|
2015-07-16 13:31:21 -04:00
|
|
|
touchIDs[i] = cTouch
|
2015-05-26 14:33:20 -04:00
|
|
|
id = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2015-05-29 09:18:33 -04:00
|
|
|
if id == -1 {
|
|
|
|
panic("out of touchIDs")
|
|
|
|
}
|
2015-05-26 14:33:20 -04:00
|
|
|
}
|
|
|
|
|
2015-07-16 13:31:21 -04:00
|
|
|
t := touch.Type(cTouchType)
|
2015-07-15 17:17:08 +10:00
|
|
|
if t == touch.TypeEnd {
|
2015-05-26 14:33:20 -04:00
|
|
|
touchIDs[id] = 0
|
|
|
|
}
|
|
|
|
|
2015-09-22 13:19:05 -04:00
|
|
|
theApp.eventsIn <- touch.Event{
|
2015-07-31 14:44:31 +10:00
|
|
|
X: x,
|
|
|
|
Y: y,
|
2015-07-15 17:17:08 +10:00
|
|
|
Sequence: touch.Sequence(id),
|
|
|
|
Type: t,
|
2015-06-27 10:28:28 -04:00
|
|
|
}
|
2015-05-26 14:33:20 -04:00
|
|
|
}
|
|
|
|
|
2015-10-06 12:17:06 -04:00
|
|
|
var workAvailable <-chan struct{}
|
2015-08-08 10:18:15 -04:00
|
|
|
|
2015-02-20 04:54:42 -05:00
|
|
|
//export drawgl
|
|
|
|
func drawgl(ctx uintptr) {
|
2015-10-06 12:17:06 -04:00
|
|
|
if workAvailable == nil {
|
2015-06-27 10:28:28 -04:00
|
|
|
C.setContext(unsafe.Pointer(ctx))
|
2015-10-06 12:17:06 -04:00
|
|
|
workAvailable = theApp.worker.WorkAvailable()
|
2015-06-27 10:28:28 -04:00
|
|
|
// TODO(crawshaw): not just on process start.
|
2015-09-22 13:19:05 -04:00
|
|
|
theApp.sendLifecycle(lifecycle.StageFocused)
|
2015-05-28 06:08:52 -04:00
|
|
|
}
|
2015-02-20 04:54:42 -05:00
|
|
|
|
app: change EndPaint to Publish.
More than a name change, the painting model changes so that the app, not
the library, is responsible for driving painting. If the app is
animating and wants paint events at 60 Hz, it has to ask for that. If
the app is not animating and doesn't need to update its screen, it
shouldn't get any paint events.
Plenty of TODOs, and this CL doesn't get us to a perfect place, but it
is a checkpoint along the way.
The darwin_*.go code changes were minimal. I don't even have a Mac or
iOS device to test that this even builds. Even so, the TODOs about not
sending paint.Events unconditionally are important TODOs. That's the
whole point of switching to this model. I'll leave the actual
implementation to you (crawshaw).
Out of all the example apps, the change to example/network/main.go is
probably the most interesting.
It seems like there ought to be some way to reduce the copy/paste
between all of the example app code, but I'll leave that for future CLs.
Change-Id: I17e11c06174110c68e17f7183b2d8af19b6a170e
Reviewed-on: https://go-review.googlesource.com/14300
Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-09-04 17:13:09 +10:00
|
|
|
// TODO(crawshaw): don't send a paint.Event unconditionally. Only send one
|
|
|
|
// if the window actually needs redrawing.
|
2015-09-22 13:19:05 -04:00
|
|
|
theApp.eventsIn <- paint.Event{}
|
2015-05-26 14:33:20 -04:00
|
|
|
|
2015-06-27 10:28:28 -04:00
|
|
|
for {
|
|
|
|
select {
|
2015-08-08 10:18:15 -04:00
|
|
|
case <-workAvailable:
|
2015-10-06 12:17:06 -04:00
|
|
|
theApp.worker.DoWork()
|
2015-09-23 19:46:12 +10:00
|
|
|
case <-theApp.publish:
|
2015-09-22 13:19:05 -04:00
|
|
|
theApp.publishResult <- PublishResult{}
|
2015-06-27 10:28:28 -04:00
|
|
|
return
|
2015-05-13 21:16:59 -07:00
|
|
|
}
|
2015-02-20 04:54:42 -05:00
|
|
|
}
|
|
|
|
}
|