twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

darwin_ios.go (5134B)


      1 // Copyright 2015 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 // +build ios
      7 
      8 package app
      9 
     10 /*
     11 #cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION
     12 #cgo LDFLAGS: -framework Foundation -framework UIKit -framework GLKit -framework OpenGLES -framework QuartzCore
     13 #include <sys/utsname.h>
     14 #include <stdint.h>
     15 #include <pthread.h>
     16 #include <UIKit/UIDevice.h>
     17 #import <GLKit/GLKit.h>
     18 
     19 extern struct utsname sysInfo;
     20 
     21 void runApp(void);
     22 void makeCurrentContext(GLintptr ctx);
     23 void swapBuffers(GLintptr ctx);
     24 uint64_t threadID();
     25 */
     26 import "C"
     27 import (
     28 	"log"
     29 	"runtime"
     30 	"strings"
     31 	"sync"
     32 
     33 	"golang.org/x/mobile/event/lifecycle"
     34 	"golang.org/x/mobile/event/paint"
     35 	"golang.org/x/mobile/event/size"
     36 	"golang.org/x/mobile/event/touch"
     37 	"golang.org/x/mobile/geom"
     38 )
     39 
     40 var initThreadID uint64
     41 
     42 func init() {
     43 	// Lock the goroutine responsible for initialization to an OS thread.
     44 	// This means the goroutine running main (and calling the run function
     45 	// below) is locked to the OS thread that started the program. This is
     46 	// necessary for the correct delivery of UIKit events to the process.
     47 	//
     48 	// A discussion on this topic:
     49 	// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
     50 	runtime.LockOSThread()
     51 	initThreadID = uint64(C.threadID())
     52 }
     53 
     54 func main(f func(App)) {
     55 	if tid := uint64(C.threadID()); tid != initThreadID {
     56 		log.Fatalf("app.Run called on thread %d, but app.init ran on %d", tid, initThreadID)
     57 	}
     58 
     59 	go func() {
     60 		f(theApp)
     61 		// TODO(crawshaw): trigger runApp to return
     62 	}()
     63 	C.runApp()
     64 	panic("unexpected return from app.runApp")
     65 }
     66 
     67 var pixelsPerPt float32
     68 var screenScale int // [UIScreen mainScreen].scale, either 1, 2, or 3.
     69 
     70 //export setScreen
     71 func setScreen(scale int) {
     72 	C.uname(&C.sysInfo)
     73 	name := C.GoString(&C.sysInfo.machine[0])
     74 
     75 	var v float32
     76 
     77 	switch {
     78 	case strings.HasPrefix(name, "iPhone"):
     79 		v = 163
     80 	case strings.HasPrefix(name, "iPad"):
     81 		// TODO: is there a better way to distinguish the iPad Mini?
     82 		switch name {
     83 		case "iPad2,5", "iPad2,6", "iPad2,7", "iPad4,4", "iPad4,5", "iPad4,6", "iPad4,7":
     84 			v = 163 // iPad Mini
     85 		default:
     86 			v = 132
     87 		}
     88 	default:
     89 		v = 163 // names like i386 and x86_64 are the simulator
     90 	}
     91 
     92 	if v == 0 {
     93 		log.Printf("unknown machine: %s", name)
     94 		v = 163 // emergency fallback
     95 	}
     96 
     97 	pixelsPerPt = v * float32(scale) / 72
     98 	screenScale = scale
     99 }
    100 
    101 //export updateConfig
    102 func updateConfig(width, height, orientation int32) {
    103 	o := size.OrientationUnknown
    104 	switch orientation {
    105 	case C.UIDeviceOrientationPortrait, C.UIDeviceOrientationPortraitUpsideDown:
    106 		o = size.OrientationPortrait
    107 	case C.UIDeviceOrientationLandscapeLeft, C.UIDeviceOrientationLandscapeRight:
    108 		o = size.OrientationLandscape
    109 	}
    110 	widthPx := screenScale * int(width)
    111 	heightPx := screenScale * int(height)
    112 	theApp.eventsIn <- size.Event{
    113 		WidthPx:     widthPx,
    114 		HeightPx:    heightPx,
    115 		WidthPt:     geom.Pt(float32(widthPx) / pixelsPerPt),
    116 		HeightPt:    geom.Pt(float32(heightPx) / pixelsPerPt),
    117 		PixelsPerPt: pixelsPerPt,
    118 		Orientation: o,
    119 	}
    120 	theApp.eventsIn <- paint.Event{External: true}
    121 }
    122 
    123 // touchIDs is the current active touches. The position in the array
    124 // is the ID, the value is the UITouch* pointer value.
    125 //
    126 // It is widely reported that the iPhone can handle up to 5 simultaneous
    127 // touch events, while the iPad can handle 11.
    128 var touchIDs [11]uintptr
    129 
    130 var touchEvents struct {
    131 	sync.Mutex
    132 	pending []touch.Event
    133 }
    134 
    135 //export sendTouch
    136 func sendTouch(cTouch, cTouchType uintptr, x, y float32) {
    137 	id := -1
    138 	for i, val := range touchIDs {
    139 		if val == cTouch {
    140 			id = i
    141 			break
    142 		}
    143 	}
    144 	if id == -1 {
    145 		for i, val := range touchIDs {
    146 			if val == 0 {
    147 				touchIDs[i] = cTouch
    148 				id = i
    149 				break
    150 			}
    151 		}
    152 		if id == -1 {
    153 			panic("out of touchIDs")
    154 		}
    155 	}
    156 
    157 	t := touch.Type(cTouchType)
    158 	if t == touch.TypeEnd {
    159 		touchIDs[id] = 0
    160 	}
    161 
    162 	theApp.eventsIn <- touch.Event{
    163 		X:        x,
    164 		Y:        y,
    165 		Sequence: touch.Sequence(id),
    166 		Type:     t,
    167 	}
    168 }
    169 
    170 //export lifecycleDead
    171 func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
    172 
    173 //export lifecycleAlive
    174 func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
    175 
    176 //export lifecycleVisible
    177 func lifecycleVisible() { theApp.sendLifecycle(lifecycle.StageVisible) }
    178 
    179 //export lifecycleFocused
    180 func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
    181 
    182 //export startloop
    183 func startloop(ctx C.GLintptr) {
    184 	go theApp.loop(ctx)
    185 }
    186 
    187 // loop is the primary drawing loop.
    188 //
    189 // After UIKit has captured the initial OS thread for processing UIKit
    190 // events in runApp, it starts loop on another goroutine. It is locked
    191 // to an OS thread for its OpenGL context.
    192 func (a *app) loop(ctx C.GLintptr) {
    193 	runtime.LockOSThread()
    194 	C.makeCurrentContext(ctx)
    195 
    196 	workAvailable := a.worker.WorkAvailable()
    197 
    198 	for {
    199 		select {
    200 		case <-workAvailable:
    201 			a.worker.DoWork()
    202 		case <-theApp.publish:
    203 		loop1:
    204 			for {
    205 				select {
    206 				case <-workAvailable:
    207 					a.worker.DoWork()
    208 				default:
    209 					break loop1
    210 				}
    211 			}
    212 			C.swapBuffers(ctx)
    213 			theApp.publishResult <- PublishResult{}
    214 		}
    215 	}
    216 }