| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- // 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.
- //go:build darwin && ios
- // +build darwin,ios
- package app
- /*
- #cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION -DGLES_SILENCE_DEPRECATION
- #cgo LDFLAGS: -framework Foundation -framework UIKit -framework GLKit -framework OpenGLES -framework QuartzCore
- #include <sys/utsname.h>
- #include <stdint.h>
- #include <pthread.h>
- #include <UIKit/UIDevice.h>
- #import <GLKit/GLKit.h>
- extern struct utsname sysInfo;
- void runApp(void);
- void makeCurrentContext(GLintptr ctx);
- void swapBuffers(GLintptr ctx);
- uint64_t threadID();
- */
- import "C"
- import (
- "log"
- "runtime"
- "strings"
- "sync"
- "golang.org/x/mobile/event/lifecycle"
- "golang.org/x/mobile/event/paint"
- "golang.org/x/mobile/event/size"
- "golang.org/x/mobile/event/touch"
- "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())
- }
- func main(f func(App)) {
- if tid := uint64(C.threadID()); tid != initThreadID {
- log.Fatalf("app.Run called on thread %d, but app.init ran on %d", tid, initThreadID)
- }
- go func() {
- f(theApp)
- // TODO(crawshaw): trigger runApp to return
- }()
- C.runApp()
- panic("unexpected return from app.runApp")
- }
- var pixelsPerPt float32
- var screenScale int // [UIScreen mainScreen].scale, either 1, 2, or 3.
- //export setScreen
- func setScreen(scale int) {
- C.uname(&C.sysInfo)
- name := C.GoString(&C.sysInfo.machine[0])
- 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 {
- log.Printf("unknown machine: %s", name)
- v = 163 // emergency fallback
- }
- pixelsPerPt = v * float32(scale) / 72
- screenScale = scale
- }
- //export updateConfig
- func updateConfig(width, height, orientation int32) {
- o := size.OrientationUnknown
- switch orientation {
- case C.UIDeviceOrientationPortrait, C.UIDeviceOrientationPortraitUpsideDown:
- o = size.OrientationPortrait
- case C.UIDeviceOrientationLandscapeLeft, C.UIDeviceOrientationLandscapeRight:
- o = size.OrientationLandscape
- }
- widthPx := screenScale * int(width)
- heightPx := screenScale * int(height)
- theApp.eventsIn <- size.Event{
- WidthPx: widthPx,
- HeightPx: heightPx,
- WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
- HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
- PixelsPerPt: pixelsPerPt,
- Orientation: o,
- }
- theApp.eventsIn <- paint.Event{External: true}
- }
- // 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
- pending []touch.Event
- }
- //export sendTouch
- func sendTouch(cTouch, cTouchType uintptr, x, y float32) {
- id := -1
- for i, val := range touchIDs {
- if val == cTouch {
- id = i
- break
- }
- }
- if id == -1 {
- for i, val := range touchIDs {
- if val == 0 {
- touchIDs[i] = cTouch
- id = i
- break
- }
- }
- if id == -1 {
- panic("out of touchIDs")
- }
- }
- t := touch.Type(cTouchType)
- if t == touch.TypeEnd {
- touchIDs[id] = 0
- }
- theApp.eventsIn <- touch.Event{
- X: x,
- Y: y,
- Sequence: touch.Sequence(id),
- Type: t,
- }
- }
- //export lifecycleDead
- func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
- //export lifecycleAlive
- func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
- //export lifecycleVisible
- func lifecycleVisible() { theApp.sendLifecycle(lifecycle.StageVisible) }
- //export lifecycleFocused
- func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
- //export startloop
- func startloop(ctx C.GLintptr) {
- go theApp.loop(ctx)
- }
- // loop is the primary drawing loop.
- //
- // After UIKit has captured the initial OS thread for processing UIKit
- // events in runApp, it starts loop on another goroutine. It is locked
- // to an OS thread for its OpenGL context.
- func (a *app) loop(ctx C.GLintptr) {
- runtime.LockOSThread()
- C.makeCurrentContext(ctx)
- workAvailable := a.worker.WorkAvailable()
- for {
- select {
- case <-workAvailable:
- a.worker.DoWork()
- case <-theApp.publish:
- loop1:
- for {
- select {
- case <-workAvailable:
- a.worker.DoWork()
- default:
- break loop1
- }
- }
- C.swapBuffers(ctx)
- theApp.publishResult <- PublishResult{}
- }
- }
- }
|