// %BANNER_BEGIN%
// ---------------------------------------------------------------------
// %COPYRIGHT_BEGIN%
// Copyright (c) (2021-2022) Magic Leap, Inc. All Rights Reserved.
// Use of this file is governed by the Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2
// Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein.
// %COPYRIGHT_END%
// ---------------------------------------------------------------------
// %BANNER_END%
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.Collections;
using UnityEngine.Scripting;
using UnityEngine.XR.InteractionSubsystems;
namespace UnityEngine.XR.MagicLeap
{
///
/// MagicLeap implementation of the XRGestureSubsystem. Do not create this directly.
/// Use XRGestureSubsystemDescriptor.RegisterDescriptor() instead.
///
[Preserve]
public sealed partial class GestureSubsystem : XRGestureSubsystem
{
///
/// A collection of all MagicLeapTouchpadGestureEvents managed by this subsystem.
/// This is cleared every frame and refreshed with new gesture events.
///
public List touchpadGestureEvents { get { return ((MagicLeapGestureProvider)provider).touchpadGestureEvents; } }
internal bool ControllerGesturesEnabled
{
get
{
return MagicLeapXrProviderNativeBindings.GetControllerTrackerHandle() != MagicLeap.Native.MagicLeapNativeBindings.InvalidHandle;
}
}
class MagicLeapGestureProvider : Provider
{
internal List touchpadGestureEvents { get { return m_TouchpadGestureEvents; } }
List m_TouchpadGestureEvents = new List();
List gestureInputDevices = new List()
{
new ControllerGestureDevice("MagicLeap Controller"),
};
public MagicLeapGestureProvider() { }
~MagicLeapGestureProvider()
{
if (m_ActivateGestureEvents.IsCreated)
m_ActivateGestureEvents.Dispose();
}
public override void Start()
{
}
public override void Stop()
{
}
public override void Update()
{
int activationGestureEvents = 0;
m_TouchpadGestureEvents.Clear();
foreach (GestureInputDevice gestureInputDevice in this.gestureInputDevices)
{
gestureInputDevice.UpdateGesture(out bool isNewGesture);
if (!isNewGesture)
continue;
ControllerGestureDevice controllerGestureDevice = gestureInputDevice as ControllerGestureDevice;
if (controllerGestureDevice != null)
{
m_TouchpadGestureEvents.Add(controllerGestureDevice.currentGestureEvent);
continue;
}
}
UpdateActivationGestures(activationGestureEvents);
}
public override void Destroy()
{
base.Destroy();
}
private void UpdateActivationGestures(int numActivationGestureEvents)
{
if (m_ActivateGestureEvents.IsCreated)
m_ActivateGestureEvents.Dispose();
m_ActivateGestureEvents = new NativeArray(numActivationGestureEvents, Allocator.Persistent);
for (int i = 0; i < m_ActivateGestureEvents.Length; ++i)
{
m_ActivateGestureEvents[i] = new ActivateGestureEvent(GetNextGUID(), GestureState.Started);
}
}
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void RegisterDescriptor()
{
XRGestureSubsystemDescriptor.RegisterDescriptor(
new XRGestureSubsystemDescriptor.Cinfo
{
id = MagicLeapXrProvider.GestureSubsystemId,
providerType = typeof(MagicLeapGestureProvider),
subsystemTypeOverride = typeof(GestureSubsystem),
}
);
}
// High GUID bits saved for common (Activate) gesture for this subsystem
static GestureId s_NextGUID = new GestureId(1, 0);
static GestureId GetNextGUID()
{
unchecked
{
s_NextGUID.subId1 += 1;
if (s_NextGUID.subId1 != 0) return s_NextGUID;
s_NextGUID.subId1 += 1;
}
return s_NextGUID;
}
class ControllerGestureDevice : GestureInputDevice
{
public ControllerGestureDevice(string name)
{
this.name = name;
this.characterstics = InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.HeldInHand;
}
public Extensions.TouchpadGestureEvent currentGestureEvent;
private byte[] stateData = new byte[Marshal.SizeOf()];
public override void UpdateGesture(out bool isNewGesture)
{
isNewGesture = false;
if (!this.device.isValid)
{
this.FindDevice();
return;
}
// only continue if the keypose has changed
if (this.device.TryGetFeatureValue(InputSubsystem.Extensions.DeviceFeatureUsages.Controller.State, this.stateData))
{
IntPtr ptr = Marshal.AllocHGlobal(this.stateData.Length);
Marshal.Copy(this.stateData, 0, ptr, this.stateData.Length);
var controllerState = Marshal.PtrToStructure(ptr);
Marshal.FreeHGlobal(ptr);
GestureState gestureState = GestureState.Canceled;
switch (controllerState.TouchpadGestureState)
{
case InputSubsystem.Extensions.TouchpadGesture.State.Start: gestureState = GestureState.Started; break;
case InputSubsystem.Extensions.TouchpadGesture.State.Continue: gestureState = GestureState.Updated; break;
case InputSubsystem.Extensions.TouchpadGesture.State.End: gestureState = GestureState.Completed; break;
}
if (gestureState == GestureState.Completed && this.currentGestureEvent.state == GestureState.Completed)
return;
this.currentGestureEvent = new Extensions.TouchpadGestureEvent(GetNextGUID(), gestureState, controllerState.HardwareIndex, controllerState.TouchpadGestureData.Angle, controllerState.TouchpadGestureData.Direction, controllerState.TouchpadGestureData.Distance, Native.MLConvert.ToUnity(controllerState.TouchpadGestureData.PositionAndForce), controllerState.TouchpadGestureData.Radius, controllerState.TouchpadGestureData.Speed, controllerState.TouchpadGestureData.Type);
isNewGesture = true;
return;
}
}
}
abstract class GestureInputDevice
{
protected string name = string.Empty;
protected InputDevice device;
protected InputDeviceCharacteristics characterstics;
public abstract void UpdateGesture(out bool isNewGesture);
protected void FindDevice()
{
List devices = new List();
InputDevices.GetDevicesWithCharacteristics(this.characterstics, devices);
foreach (InputDevice device in devices)
{
if (device.name == this.name && device.isValid)
{
this.device = device;
break;
}
}
}
}
}
}