exp/sensor: support gyro events on darwin{arm,arm64}

Change-Id: I47aa12cc942261b8035312d0eeba0caf52b562ac
Reviewed-on: https://go-review.googlesource.com/14257
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/exp/sensor/darwin_armx.go b/exp/sensor/darwin_armx.go
index ac93737..612882e 100644
--- a/exp/sensor/darwin_armx.go
+++ b/exp/sensor/darwin_armx.go
@@ -19,6 +19,10 @@
 void GoIOS_stopAccelerometer();
 void GoIOS_readAccelerometer(int64_t* timestamp, float* vector);
 
+void GoIOS_startGyro(float interval);
+void GoIOS_stopGyro();
+void GoIOS_readGyro(int64_t* timestamp, float* vector);
+
 void GoIOS_destroyManager();
 */
 import "C"
@@ -31,7 +35,7 @@
 
 var channels struct {
 	sync.Mutex
-	acceleroDone chan struct{}
+	done [nTypes]chan struct{}
 }
 
 type manager struct {
@@ -59,25 +63,26 @@
 	channels.Lock()
 	defer channels.Unlock()
 
+	if channels.done[t] != nil {
+		return fmt.Errorf("sensor: cannot enable; %v sensor is already enabled", t)
+	}
+	channels.done[t] = make(chan struct{})
+
 	if delay < minDelay {
 		delay = minDelay
 	}
+	interval := float64(delay) / float64(time.Second)
 
 	switch t {
 	case Accelerometer:
-		if channels.acceleroDone != nil {
-			return fmt.Errorf("sensor: cannot enable; %v sensor is already enabled", t)
-		}
-		// TODO(jbd): Check if accelerometer is available.
-		interval := float64(delay) / float64(time.Second)
 		C.GoIOS_startAccelerometer(C.float(interval))
-		channels.acceleroDone = make(chan struct{})
-		go m.runAccelerometer(s, delay, channels.acceleroDone)
 	case Gyroscope:
+		C.GoIOS_startGyro(C.float(interval))
 	case Magnetometer:
-	default:
-		return fmt.Errorf("sensor: unknown sensor type: %v", t)
+		return fmt.Errorf("sensor: %v is not supported yet", t)
 	}
+
+	go m.pollSensor(s, t, delay, channels.done[t])
 	return nil
 }
 
@@ -85,15 +90,17 @@
 	channels.Lock()
 	defer channels.Unlock()
 
+	if channels.done[t] == nil {
+		return fmt.Errorf("sensor: cannot disable; %v sensor is not enabled", t)
+	}
+	close(channels.done[t])
+	channels.done[t] = nil
+
 	switch t {
 	case Accelerometer:
-		if channels.acceleroDone == nil {
-			return fmt.Errorf("sensor: cannot disable; %v sensor is not enabled", t)
-		}
-		close(channels.acceleroDone)
-		channels.acceleroDone = nil
 		C.GoIOS_stopAccelerometer()
 	case Gyroscope:
+		C.GoIOS_stopGyro()
 	case Magnetometer:
 	default:
 		return fmt.Errorf("sensor: unknown sensor type: %v", t)
@@ -101,26 +108,33 @@
 	return nil
 }
 
-func (m *manager) runAccelerometer(s Sender, d time.Duration, done chan struct{}) {
+func (m *manager) pollSensor(s Sender, t Type, d time.Duration, done chan struct{}) {
+	var lastTimestamp int64
+
 	var timestamp C.int64_t
 	var ev [3]C.float
-	var lastTimestamp int64
+
 	for {
 		select {
 		case <-done:
 			return
 		default:
-			C.GoIOS_readAccelerometer((*C.int64_t)(unsafe.Pointer(&timestamp)), (*C.float)(unsafe.Pointer(&ev[0])))
-			t := int64(timestamp)
-			if t > lastTimestamp {
+			switch t {
+			case Accelerometer:
+				C.GoIOS_readAccelerometer((*C.int64_t)(unsafe.Pointer(&timestamp)), (*C.float)(unsafe.Pointer(&ev[0])))
+			case Gyroscope:
+				C.GoIOS_readGyro((*C.int64_t)(unsafe.Pointer(&timestamp)), (*C.float)(unsafe.Pointer(&ev[0])))
+			}
+			ts := int64(timestamp)
+			if ts > lastTimestamp {
 				// TODO(jbd): Do we need to convert the values to another unit?
 				// How does iOS units compare to the Android units.
 				s.Send(Event{
-					Sensor:    Accelerometer,
-					Timestamp: t,
+					Sensor:    t,
+					Timestamp: ts,
 					Data:      []float64{float64(ev[0]), float64(ev[1]), float64(ev[2])},
 				})
-				lastTimestamp = t
+				lastTimestamp = ts
 				time.Sleep(d / 2)
 			}
 		}
diff --git a/exp/sensor/darwin_armx.m b/exp/sensor/darwin_armx.m
index 654e6ae..a775a46 100644
--- a/exp/sensor/darwin_armx.m
+++ b/exp/sensor/darwin_armx.m
@@ -30,6 +30,23 @@
   v[2] = data.acceleration.z;
 }
 
+void GoIOS_startGyro(float interval) {
+  manager.gyroUpdateInterval = interval;
+  [manager startGyroUpdates];
+}
+
+void GoIOS_stopGyro() {
+  [manager stopGyroUpdates];
+}
+
+void GoIOS_readGyro(int64_t* timestamp, float* v) {
+  CMGyroData* data = manager.gyroData;
+  *timestamp = (int64_t)(data.timestamp * 1000 * 1000);
+  v[0] = data.rotationRate.x;
+  v[1] = data.rotationRate.y;
+  v[2] = data.rotationRate.z;
+}
+
 void GoIOS_destroyManager() {
   [manager release];
   manager = nil;
diff --git a/exp/sensor/sensor.go b/exp/sensor/sensor.go
index 90ec7ed..eea4e83 100644
--- a/exp/sensor/sensor.go
+++ b/exp/sensor/sensor.go
@@ -31,6 +31,7 @@
 	Accelerometer = Type(0)
 	Gyroscope     = Type(1)
 	Magnetometer  = Type(2)
+	nTypes        = Type(3)
 )
 
 // Event represents a sensor event.