Tanoda
Connection.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
9namespace LeapInternal {
10 using System;
11 using System.Collections.Generic;
12 using System.Runtime.InteropServices;
13 using System.Threading;
14
15 using Leap;
16
17 public class Connection {
18 public struct Key {
19 public readonly int connectionId;
20 public readonly string serverNamespace;
21
22 public Key(int connectionId, string serverNamespace = null) {
23 this.connectionId = connectionId;
24 this.serverNamespace = serverNamespace;
25 }
26 }
27
28 private static Dictionary<Key, Connection> connectionDictionary = new Dictionary<Key, Connection>();
29
30 public static Connection GetConnection(int connectionId = 0) {
31 return GetConnection(new Key(connectionId));
32 }
33
34 public static Connection GetConnection(Key connectionKey) {
35 Connection conn;
36 if (!Connection.connectionDictionary.TryGetValue(connectionKey, out conn)) {
37 conn = new Connection(connectionKey);
38 connectionDictionary.Add(connectionKey, conn);
39 }
40 return conn;
41 }
42
43 //Left-right precalculated offsets
44 private static long _handIdOffset;
45 private static long _handPositionOffset;
46 private static long _handOrientationOffset;
47
48 static Connection() {
49 _handIdOffset = Marshal.OffsetOf(typeof(LEAP_HAND), "id").ToInt64();
50
51 long palmOffset = Marshal.OffsetOf(typeof(LEAP_HAND), "palm").ToInt64();
52 _handPositionOffset = Marshal.OffsetOf(typeof(LEAP_PALM), "position").ToInt64() + palmOffset;
53 _handOrientationOffset = Marshal.OffsetOf(typeof(LEAP_PALM), "orientation").ToInt64() + palmOffset;
54 }
55
56 public Key ConnectionKey { get; private set; }
58
59 private DeviceList _devices = new DeviceList();
60 private FailedDeviceList _failedDevices;
61
62 private DistortionData _currentLeftDistortionData = new DistortionData();
63 private DistortionData _currentRightDistortionData = new DistortionData();
64 private int _frameBufferLength = 60; //TODO, surface this value in LeapC, currently hardcoded!
65
66 private IntPtr _leapConnection;
67 private bool _isRunning = false;
68 private Thread _polster;
69
70 //Policy and enabled features
71 private UInt64 _requestedPolicies = 0;
72 private UInt64 _activePolicies = 0;
73
74 //Config change status
75 private Dictionary<uint, string> _configRequests = new Dictionary<uint, string>();
76
77 //Connection events
78 public SynchronizationContext EventContext { get; set; }
79
80 private EventHandler<LeapEventArgs> _leapInit;
81 public event EventHandler<LeapEventArgs> LeapInit {
82 add {
83 _leapInit += value;
84 if (_leapConnection != IntPtr.Zero)
85 value(this, new LeapEventArgs(LeapEvent.EVENT_INIT));
86 }
87 remove { _leapInit -= value; }
88 }
89
90 private EventHandler<ConnectionEventArgs> _leapConnectionEvent;
91 public event EventHandler<ConnectionEventArgs> LeapConnection {
92 add {
93 _leapConnectionEvent += value;
95 value(this, new ConnectionEventArgs());
96 }
97 remove { _leapConnectionEvent -= value; }
98 }
99 public EventHandler<ConnectionLostEventArgs> LeapConnectionLost;
100 public EventHandler<DeviceEventArgs> LeapDevice;
101 public EventHandler<DeviceEventArgs> LeapDeviceLost;
102 public EventHandler<DeviceFailureEventArgs> LeapDeviceFailure;
103 public EventHandler<PolicyEventArgs> LeapPolicyChange;
104 public EventHandler<FrameEventArgs> LeapFrame;
105 public EventHandler<InternalFrameEventArgs> LeapInternalFrame;
106 public EventHandler<LogEventArgs> LeapLogEvent;
107 public EventHandler<SetConfigResponseEventArgs> LeapConfigResponse;
108 public EventHandler<ConfigChangeEventArgs> LeapConfigChange;
109 public EventHandler<DistortionEventArgs> LeapDistortionChange;
110 public EventHandler<DroppedFrameEventArgs> LeapDroppedFrame;
111 public EventHandler<ImageEventArgs> LeapImage;
112 public EventHandler<PointMappingChangeEventArgs> LeapPointMappingChange;
113 public EventHandler<HeadPoseEventArgs> LeapHeadPoseChange;
114
115 public Action<BeginProfilingForThreadArgs> LeapBeginProfilingForThread;
116 public Action<EndProfilingForThreadArgs> LeapEndProfilingForThread;
117 public Action<BeginProfilingBlockArgs> LeapBeginProfilingBlock;
118 public Action<EndProfilingBlockArgs> LeapEndProfilingBlock;
119
120 private bool _disposed = false;
121
122 public void Dispose() {
123 Dispose(true);
124 GC.SuppressFinalize(this);
125 }
126
127 // Protected implementation of Dispose pattern.
128 protected virtual void Dispose(bool disposing) {
129 if (_disposed)
130 return;
131
132 if (disposing) {
133 }
134
135 Stop();
136 LeapC.DestroyConnection(_leapConnection);
137 _leapConnection = IntPtr.Zero;
138
139 _disposed = true;
140 }
141
142 ~Connection() {
143 Dispose(false);
144 }
145
146 private Connection(Key connectionKey) {
147 ConnectionKey = connectionKey;
148 _leapConnection = IntPtr.Zero;
149
150 Frames = new CircularObjectBuffer<LEAP_TRACKING_EVENT>(_frameBufferLength);
151 }
152
153 private LEAP_ALLOCATOR _pLeapAllocator = new LEAP_ALLOCATOR();
154
155 public void Start() {
156 if (_isRunning)
157 return;
158
159 eLeapRS result;
160 if (_leapConnection == IntPtr.Zero) {
161 if (ConnectionKey.serverNamespace == null) {
162 result = LeapC.CreateConnection(out _leapConnection);
163 } else {
165 config.size = (uint)Marshal.SizeOf(config);
166 config.flags = 0;
167 config.server_namespace = Marshal.StringToHGlobalAnsi(ConnectionKey.serverNamespace);
168 result = LeapC.CreateConnection(ref config, out _leapConnection);
169 Marshal.FreeHGlobal(config.server_namespace);
170 }
171
172 if (result != eLeapRS.eLeapRS_Success || _leapConnection == IntPtr.Zero) {
173 reportAbnormalResults("LeapC CreateConnection call was ", result);
174 return;
175 }
176 }
177 result = LeapC.OpenConnection(_leapConnection);
178 if (result != eLeapRS.eLeapRS_Success) {
179 reportAbnormalResults("LeapC OpenConnection call was ", result);
180 return;
181 }
182 // The Allocator must persist the lifetime of the connection
183 if (_pLeapAllocator.allocate == null) {
184 _pLeapAllocator.allocate = MemoryManager.Pin;
185 }
186 if (_pLeapAllocator.deallocate == null) {
187 _pLeapAllocator.deallocate = MemoryManager.Unpin;
188 }
189 LeapC.SetAllocator(_leapConnection, ref _pLeapAllocator);
190
191 _isRunning = true;
192 AppDomain.CurrentDomain.DomainUnload += (arg1, arg2) => Dispose(true);
193
194 _polster = new Thread(new ThreadStart(this.processMessages));
195 _polster.Name = "LeapC Worker";
196 _polster.IsBackground = true;
197 _polster.Start();
198 }
199
200 public void Stop() {
201 if (!_isRunning)
202 return;
203
204 _isRunning = false;
205
206 //Very important to close the connection before we try to join the
207 //worker thread! The call to PollConnection can sometimes block,
208 //despite the timeout, causing an attempt to join the thread waiting
209 //forever and preventing the connection from stopping.
210 //
211 //It seems that closing the connection causes PollConnection to
212 //unblock in these cases, so just make sure to close the connection
213 //before trying to join the worker thread.
214 LeapC.CloseConnection(_leapConnection);
215
216 _polster.Join();
217 }
218
219 //Run in Polster thread, fills in object queues
220 private void processMessages() {
221 //Only profiling block currently is the Handle Event block
222 const string HANDLE_EVENT_PROFILER_BLOCK = "Handle Event";
223 bool hasBegunProfilingForThread = false;
224
225 try {
226 eLeapRS result;
227 _leapInit.DispatchOnContext(this, EventContext, new LeapEventArgs(LeapEvent.EVENT_INIT));
228 while (_isRunning) {
229 if (LeapBeginProfilingForThread != null && !hasBegunProfilingForThread) {
231 HANDLE_EVENT_PROFILER_BLOCK));
232 hasBegunProfilingForThread = true;
233 }
234
235 LEAP_CONNECTION_MESSAGE _msg = new LEAP_CONNECTION_MESSAGE();
236 uint timeout = 150;
237 result = LeapC.PollConnection(_leapConnection, timeout, ref _msg);
238
239 if (result != eLeapRS.eLeapRS_Success) {
240 reportAbnormalResults("LeapC PollConnection call was ", result);
241 continue;
242 }
243
244 if (LeapBeginProfilingBlock != null && hasBegunProfilingForThread) {
245 LeapBeginProfilingBlock(new BeginProfilingBlockArgs(HANDLE_EVENT_PROFILER_BLOCK));
246 }
247
248 switch (_msg.type) {
249 case eLeapEventType.eLeapEventType_None:
250 break;
251
252 case eLeapEventType.eLeapEventType_Connection:
253 LEAP_CONNECTION_EVENT connection_evt;
254 StructMarshal<LEAP_CONNECTION_EVENT>.PtrToStruct(_msg.eventStructPtr, out connection_evt);
255 handleConnection(ref connection_evt);
256 break;
257 case eLeapEventType.eLeapEventType_ConnectionLost:
258 LEAP_CONNECTION_LOST_EVENT connection_lost_evt;
259 StructMarshal<LEAP_CONNECTION_LOST_EVENT>.PtrToStruct(_msg.eventStructPtr, out connection_lost_evt);
260 handleConnectionLost(ref connection_lost_evt);
261 break;
262
263 case eLeapEventType.eLeapEventType_Device:
264 LEAP_DEVICE_EVENT device_evt;
265 StructMarshal<LEAP_DEVICE_EVENT>.PtrToStruct(_msg.eventStructPtr, out device_evt);
266 handleDevice(ref device_evt);
267 break;
268
269 // Note that unplugging a device generates an eLeapEventType_DeviceLost event
270 // message, not a failure message. DeviceLost is further down.
271 case eLeapEventType.eLeapEventType_DeviceFailure:
272 LEAP_DEVICE_FAILURE_EVENT device_failure_evt;
273 StructMarshal<LEAP_DEVICE_FAILURE_EVENT>.PtrToStruct(_msg.eventStructPtr, out device_failure_evt);
274 handleFailedDevice(ref device_failure_evt);
275 break;
276
277 case eLeapEventType.eLeapEventType_Policy:
278 LEAP_POLICY_EVENT policy_evt;
279 StructMarshal<LEAP_POLICY_EVENT>.PtrToStruct(_msg.eventStructPtr, out policy_evt);
280 handlePolicyChange(ref policy_evt);
281 break;
282
283 case eLeapEventType.eLeapEventType_Tracking:
284 LEAP_TRACKING_EVENT tracking_evt;
285 StructMarshal<LEAP_TRACKING_EVENT>.PtrToStruct(_msg.eventStructPtr, out tracking_evt);
286 handleTrackingMessage(ref tracking_evt);
287 break;
288 case eLeapEventType.eLeapEventType_LogEvent:
289 LEAP_LOG_EVENT log_evt;
290 StructMarshal<LEAP_LOG_EVENT>.PtrToStruct(_msg.eventStructPtr, out log_evt);
291 reportLogMessage(ref log_evt);
292 break;
293 case eLeapEventType.eLeapEventType_DeviceLost:
294 LEAP_DEVICE_EVENT device_lost_evt;
295 StructMarshal<LEAP_DEVICE_EVENT>.PtrToStruct(_msg.eventStructPtr, out device_lost_evt);
296 handleLostDevice(ref device_lost_evt);
297 break;
298 case eLeapEventType.eLeapEventType_ConfigChange:
299 LEAP_CONFIG_CHANGE_EVENT config_change_evt;
300 StructMarshal<LEAP_CONFIG_CHANGE_EVENT>.PtrToStruct(_msg.eventStructPtr, out config_change_evt);
301 handleConfigChange(ref config_change_evt);
302 break;
303 case eLeapEventType.eLeapEventType_ConfigResponse:
304 handleConfigResponse(ref _msg);
305 break;
306 case eLeapEventType.eLeapEventType_DroppedFrame:
307 LEAP_DROPPED_FRAME_EVENT dropped_frame_evt;
308 StructMarshal<LEAP_DROPPED_FRAME_EVENT>.PtrToStruct(_msg.eventStructPtr, out dropped_frame_evt);
309 handleDroppedFrame(ref dropped_frame_evt);
310 break;
311 case eLeapEventType.eLeapEventType_Image:
312 LEAP_IMAGE_EVENT image_evt;
313 StructMarshal<LEAP_IMAGE_EVENT>.PtrToStruct(_msg.eventStructPtr, out image_evt);
314 handleImage(ref image_evt);
315 break;
316 case eLeapEventType.eLeapEventType_PointMappingChange:
317 LEAP_POINT_MAPPING_CHANGE_EVENT point_mapping_change_evt;
318 StructMarshal<LEAP_POINT_MAPPING_CHANGE_EVENT>.PtrToStruct(_msg.eventStructPtr, out point_mapping_change_evt);
319 handlePointMappingChange(ref point_mapping_change_evt);
320 break;
321 case eLeapEventType.eLeapEventType_HeadPose:
322 LEAP_HEAD_POSE_EVENT head_pose_event;
323 StructMarshal<LEAP_HEAD_POSE_EVENT>.PtrToStruct(_msg.eventStructPtr, out head_pose_event);
324 handleHeadPoseChange(ref head_pose_event);
325 break;
326 case eLeapEventType.eLeapEventType_DeviceStatusChange:
327 LEAP_DEVICE_STATUS_CHANGE_EVENT status_evt;
328 StructMarshal<LEAP_DEVICE_STATUS_CHANGE_EVENT>.PtrToStruct(_msg.eventStructPtr, out status_evt);
329 handleDeviceStatusEvent(ref status_evt);
330 break;
331 } //switch on _msg.type
332
333 if (LeapEndProfilingBlock != null && hasBegunProfilingForThread) {
334 LeapEndProfilingBlock(new EndProfilingBlockArgs(HANDLE_EVENT_PROFILER_BLOCK));
335 }
336 } //while running
337 } catch (Exception e) {
338 Logger.Log("Exception: " + e);
339 _isRunning = false;
340 } finally {
341 if (LeapEndProfilingForThread != null && hasBegunProfilingForThread) {
342 LeapEndProfilingForThread(new EndProfilingForThreadArgs());
343 }
344 }
345 }
346
347 private void handleTrackingMessage(ref LEAP_TRACKING_EVENT trackingMsg) {
348 Frames.Put(ref trackingMsg);
349
350 if (LeapFrame != null) {
351 LeapFrame.DispatchOnContext(this, EventContext, new FrameEventArgs(new Frame().CopyFrom(ref trackingMsg)));
352 }
353 }
354
355 public UInt64 GetInterpolatedFrameSize(Int64 time) {
356 UInt64 size = 0;
357 eLeapRS result = LeapC.GetFrameSize(_leapConnection, time, out size);
358 reportAbnormalResults("LeapC get interpolated frame call was ", result);
359 return size;
360 }
361
362 public void GetInterpolatedFrame(Frame toFill, Int64 time) {
363 UInt64 size = GetInterpolatedFrameSize(time);
364 IntPtr trackingBuffer = Marshal.AllocHGlobal((Int32)size);
365 eLeapRS result = LeapC.InterpolateFrame(_leapConnection, time, trackingBuffer, size);
366 reportAbnormalResults("LeapC get interpolated frame call was ", result);
367 if (result == eLeapRS.eLeapRS_Success) {
368 LEAP_TRACKING_EVENT tracking_evt;
369 StructMarshal<LEAP_TRACKING_EVENT>.PtrToStruct(trackingBuffer, out tracking_evt);
370 toFill.CopyFrom(ref tracking_evt);
371 }
372 Marshal.FreeHGlobal(trackingBuffer);
373 }
374
375 public void GetInterpolatedFrameFromTime(Frame toFill, Int64 time, Int64 sourceTime) {
376 UInt64 size = GetInterpolatedFrameSize(time);
377 IntPtr trackingBuffer = Marshal.AllocHGlobal((Int32)size);
378 eLeapRS result = LeapC.InterpolateFrameFromTime(_leapConnection, time, sourceTime, trackingBuffer, size);
379 reportAbnormalResults("LeapC get interpolated frame from time call was ", result);
380 if (result == eLeapRS.eLeapRS_Success) {
381 LEAP_TRACKING_EVENT tracking_evt;
382 StructMarshal<LEAP_TRACKING_EVENT>.PtrToStruct(trackingBuffer, out tracking_evt);
383 toFill.CopyFrom(ref tracking_evt);
384 }
385 Marshal.FreeHGlobal(trackingBuffer);
386 }
387
388 public Frame GetInterpolatedFrame(Int64 time) {
389 Frame frame = new Frame();
390 GetInterpolatedFrame(frame, time);
391 return frame;
392 }
393
394 public void GetInterpolatedHeadPose(ref LEAP_HEAD_POSE_EVENT toFill, Int64 time) {
395 eLeapRS result = LeapC.InterpolateHeadPose(_leapConnection, time, ref toFill);
396 reportAbnormalResults("LeapC get interpolated head pose call was ", result);
397 }
398
400 LEAP_HEAD_POSE_EVENT headPoseEvent = new LEAP_HEAD_POSE_EVENT();
401 GetInterpolatedHeadPose(ref headPoseEvent, time);
402 return headPoseEvent;
403 }
404
405 public void GetInterpolatedLeftRightTransform(Int64 time,
406 Int64 sourceTime,
407 Int64 leftId,
408 Int64 rightId,
409 out LeapTransform leftTransform,
410 out LeapTransform rightTransform) {
411 leftTransform = LeapTransform.Identity;
412 rightTransform = LeapTransform.Identity;
413
414 UInt64 size = GetInterpolatedFrameSize(time);
415 IntPtr trackingBuffer = Marshal.AllocHGlobal((Int32)size);
416 eLeapRS result = LeapC.InterpolateFrameFromTime(_leapConnection, time, sourceTime, trackingBuffer, size);
417 reportAbnormalResults("LeapC get interpolated frame from time call was ", result);
418
419 if (result == eLeapRS.eLeapRS_Success) {
420 LEAP_TRACKING_EVENT tracking_evt;
421 StructMarshal<LEAP_TRACKING_EVENT>.PtrToStruct(trackingBuffer, out tracking_evt);
422
423 int id;
424 LEAP_VECTOR position;
425 LEAP_QUATERNION orientation;
426
427 long handPtr = tracking_evt.pHands.ToInt64();
428 long idPtr = handPtr + _handIdOffset;
429 long posPtr = handPtr + _handPositionOffset;
430 long rotPtr = handPtr + _handOrientationOffset;
431 int stride = StructMarshal<LEAP_HAND>.Size;
432
433 for (uint i = tracking_evt.nHands; i-- != 0; idPtr += stride, posPtr += stride, rotPtr += stride) {
434 id = Marshal.ReadInt32(new IntPtr(idPtr));
435 StructMarshal<LEAP_VECTOR>.PtrToStruct(new IntPtr(posPtr), out position);
436 StructMarshal<LEAP_QUATERNION>.PtrToStruct(new IntPtr(rotPtr), out orientation);
437
438 LeapTransform transform = new LeapTransform(position.ToLeapVector(), orientation.ToLeapQuaternion());
439 if (id == leftId) {
440 leftTransform = transform;
441 } else if (id == rightId) {
442 rightTransform = transform;
443 }
444 }
445 }
446
447 Marshal.FreeHGlobal(trackingBuffer);
448 }
449
450 private void handleConnection(ref LEAP_CONNECTION_EVENT connectionMsg) {
451 if (_leapConnectionEvent != null) {
452 _leapConnectionEvent.DispatchOnContext(this, EventContext, new ConnectionEventArgs());
453 }
454 }
455
456 private void handleConnectionLost(ref LEAP_CONNECTION_LOST_EVENT connectionMsg) {
457 if (LeapConnectionLost != null) {
458 LeapConnectionLost.DispatchOnContext(this, EventContext, new ConnectionLostEventArgs());
459 }
460 }
461 private void handleDeviceStatusEvent(ref LEAP_DEVICE_STATUS_CHANGE_EVENT statusEvent)
462 {
463 var device = _devices.FindDeviceByHandle(statusEvent.device.handle);
464 if (device == null)
465 return;
466 device.UpdateStatus(statusEvent.status);
467 }
468
469
470 private void handleDevice(ref LEAP_DEVICE_EVENT deviceMsg) {
471 IntPtr deviceHandle = deviceMsg.device.handle;
472 if (deviceHandle == IntPtr.Zero)
473 return;
474
475 LEAP_DEVICE_INFO deviceInfo = new LEAP_DEVICE_INFO();
476 eLeapRS result;
477
478 IntPtr device;
479 result = LeapC.OpenDevice(deviceMsg.device, out device);
480 if (result != eLeapRS.eLeapRS_Success)
481 return;
482
483 deviceInfo.serial = IntPtr.Zero;
484 deviceInfo.size = (uint)Marshal.SizeOf(deviceInfo);
485 result = LeapC.GetDeviceInfo(device, ref deviceInfo); //Query the serial length
486 if (result != eLeapRS.eLeapRS_Success)
487 return;
488
489 deviceInfo.serial = Marshal.AllocCoTaskMem((int)deviceInfo.serial_length);
490 result = LeapC.GetDeviceInfo(device, ref deviceInfo); //Query the serial
491
492 if (result == eLeapRS.eLeapRS_Success) {
493 Device apiDevice = new Device(deviceHandle,
494 device,
495 deviceInfo.h_fov, //radians
496 deviceInfo.v_fov, //radians
497 deviceInfo.range / 1000.0f, //to mm
498 deviceInfo.baseline / 1000.0f, //to mm
499 (Device.DeviceType)deviceInfo.type,
500 deviceInfo.status,
501 Marshal.PtrToStringAnsi(deviceInfo.serial));
502 Marshal.FreeCoTaskMem(deviceInfo.serial);
503 _devices.AddOrUpdate(apiDevice);
504
505 if (LeapDevice != null) {
506 LeapDevice.DispatchOnContext(this, EventContext, new DeviceEventArgs(apiDevice));
507 }
508 }
509 }
510
511 private void handleLostDevice(ref LEAP_DEVICE_EVENT deviceMsg) {
512 Device lost = _devices.FindDeviceByHandle(deviceMsg.device.handle);
513 if (lost != null) {
514 _devices.Remove(lost);
515
516 if (LeapDeviceLost != null) {
517 LeapDeviceLost.DispatchOnContext(this, EventContext, new DeviceEventArgs(lost));
518 }
519 }
520 }
521
522 private void handleFailedDevice(ref LEAP_DEVICE_FAILURE_EVENT deviceMsg) {
523 string failureMessage;
524 string failedSerialNumber = "Unavailable";
525 switch (deviceMsg.status) {
526 case eLeapDeviceStatus.eLeapDeviceStatus_BadCalibration:
527 failureMessage = "Bad Calibration. Device failed because of a bad calibration record.";
528 break;
529 case eLeapDeviceStatus.eLeapDeviceStatus_BadControl:
530 failureMessage = "Bad Control Interface. Device failed because of a USB control interface error.";
531 break;
532 case eLeapDeviceStatus.eLeapDeviceStatus_BadFirmware:
533 failureMessage = "Bad Firmware. Device failed because of a firmware error.";
534 break;
535 case eLeapDeviceStatus.eLeapDeviceStatus_BadTransport:
536 failureMessage = "Bad Transport. Device failed because of a USB communication error.";
537 break;
538 default:
539 failureMessage = "Device failed for an unknown reason";
540 break;
541 }
542 Device failed = _devices.FindDeviceByHandle(deviceMsg.hDevice);
543 if (failed != null) {
544 _devices.Remove(failed);
545 }
546
547 if (LeapDeviceFailure != null) {
548 LeapDeviceFailure.DispatchOnContext(this, EventContext,
549 new DeviceFailureEventArgs((uint)deviceMsg.status, failureMessage, failedSerialNumber));
550 }
551 }
552
553 private void handleConfigChange(ref LEAP_CONFIG_CHANGE_EVENT configEvent) {
554 string config_key = "";
555 _configRequests.TryGetValue(configEvent.requestId, out config_key);
556 if (config_key != null)
557 _configRequests.Remove(configEvent.requestId);
558 if (LeapConfigChange != null) {
559 LeapConfigChange.DispatchOnContext(this, EventContext,
560 new ConfigChangeEventArgs(config_key, configEvent.status != false, configEvent.requestId));
561 }
562 }
563
564 private void handleConfigResponse(ref LEAP_CONNECTION_MESSAGE configMsg) {
565 LEAP_CONFIG_RESPONSE_EVENT config_response_evt;
566 StructMarshal<LEAP_CONFIG_RESPONSE_EVENT>.PtrToStruct(configMsg.eventStructPtr, out config_response_evt);
567 string config_key = "";
568 _configRequests.TryGetValue(config_response_evt.requestId, out config_key);
569 if (config_key != null)
570 _configRequests.Remove(config_response_evt.requestId);
571
572 Config.ValueType dataType;
573 object value;
574 uint requestId = config_response_evt.requestId;
575 if (config_response_evt.value.type != eLeapValueType.eLeapValueType_String) {
576 switch (config_response_evt.value.type) {
577 case eLeapValueType.eLeapValueType_Boolean:
578 dataType = Config.ValueType.TYPE_BOOLEAN;
579 value = config_response_evt.value.boolValue;
580 break;
581 case eLeapValueType.eLeapValueType_Int32:
582 dataType = Config.ValueType.TYPE_INT32;
583 value = config_response_evt.value.intValue;
584 break;
585 case eLeapValueType.eLeapValueType_Float:
586 dataType = Config.ValueType.TYPE_FLOAT;
587 value = config_response_evt.value.floatValue;
588 break;
589 default:
590 dataType = Config.ValueType.TYPE_UNKNOWN;
591 value = new object();
592 break;
593 }
594 } else {
595 LEAP_CONFIG_RESPONSE_EVENT_WITH_REF_TYPE config_ref_value;
596 StructMarshal<LEAP_CONFIG_RESPONSE_EVENT_WITH_REF_TYPE>.PtrToStruct(configMsg.eventStructPtr, out config_ref_value);
597 dataType = Config.ValueType.TYPE_STRING;
598 value = config_ref_value.value.stringValue;
599 }
600 SetConfigResponseEventArgs args = new SetConfigResponseEventArgs(config_key, dataType, value, requestId);
601
602 if (LeapConfigResponse != null) {
603 LeapConfigResponse.DispatchOnContext(this, EventContext, args);
604 }
605 }
606
607 private void reportLogMessage(ref LEAP_LOG_EVENT logMsg) {
608 if (LeapLogEvent != null) {
609 LeapLogEvent.DispatchOnContext(this, EventContext, new LogEventArgs(publicSeverity(logMsg.severity), logMsg.timestamp, Marshal.PtrToStringAnsi(logMsg.message)));
610 }
611 }
612
613 private MessageSeverity publicSeverity(eLeapLogSeverity leapCSeverity) {
614 switch (leapCSeverity) {
615 case eLeapLogSeverity.eLeapLogSeverity_Unknown:
616 return MessageSeverity.MESSAGE_UNKNOWN;
617 case eLeapLogSeverity.eLeapLogSeverity_Information:
618 return MessageSeverity.MESSAGE_INFORMATION;
619 case eLeapLogSeverity.eLeapLogSeverity_Warning:
620 return MessageSeverity.MESSAGE_WARNING;
621 case eLeapLogSeverity.eLeapLogSeverity_Critical:
622 return MessageSeverity.MESSAGE_CRITICAL;
623 default:
624 return MessageSeverity.MESSAGE_UNKNOWN;
625 }
626 }
627
628 private void handlePointMappingChange(ref LEAP_POINT_MAPPING_CHANGE_EVENT pointMapping) {
629 if (LeapPointMappingChange != null) {
630 LeapPointMappingChange.DispatchOnContext(this, EventContext, new PointMappingChangeEventArgs(pointMapping.frame_id, pointMapping.timestamp, pointMapping.nPoints));
631 }
632 }
633
634 private void handleDroppedFrame(ref LEAP_DROPPED_FRAME_EVENT droppedFrame) {
635 if (LeapDroppedFrame != null) {
636 LeapDroppedFrame.DispatchOnContext(this, EventContext, new DroppedFrameEventArgs(droppedFrame.frame_id, droppedFrame.reason));
637 }
638 }
639
640 private void handleHeadPoseChange(ref LEAP_HEAD_POSE_EVENT headPose) {
641 if (LeapHeadPoseChange != null) {
642 LeapHeadPoseChange.DispatchOnContext(this, EventContext, new HeadPoseEventArgs(headPose.head_position, headPose.head_orientation));
643 }
644 }
645
646 private DistortionData createDistortionData(LEAP_IMAGE image, Image.CameraType camera) {
647 DistortionData distortionData = new DistortionData();
648 distortionData.Version = image.matrix_version;
649 distortionData.Width = LeapC.DistortionSize; //fixed value for now
650 distortionData.Height = LeapC.DistortionSize; //fixed value for now
651
652 //Visit LeapC.h for more details. We need to marshal the float data manually
653 //since the distortion struct cannot be represented safely in c#
654 distortionData.Data = new float[(int)(distortionData.Width * distortionData.Height * 2)]; //2 float values per map point
655 Marshal.Copy(image.distortionMatrix, distortionData.Data, 0, distortionData.Data.Length);
656
657 if (LeapDistortionChange != null) {
658 LeapDistortionChange.DispatchOnContext(this, EventContext, new DistortionEventArgs(distortionData, camera));
659 }
660 return distortionData;
661 }
662
663 private void handleImage(ref LEAP_IMAGE_EVENT imageMsg) {
664 if (LeapImage != null) {
665 //Update distortion data, if changed
666 if ((_currentLeftDistortionData.Version != imageMsg.leftImage.matrix_version) || !_currentLeftDistortionData.IsValid) {
667 _currentLeftDistortionData = createDistortionData(imageMsg.leftImage, Image.CameraType.LEFT);
668 }
669 if ((_currentRightDistortionData.Version != imageMsg.rightImage.matrix_version) || !_currentRightDistortionData.IsValid) {
670 _currentRightDistortionData = createDistortionData(imageMsg.rightImage, Image.CameraType.RIGHT);
671 }
672 ImageData leftImage = new ImageData(Image.CameraType.LEFT, imageMsg.leftImage, _currentLeftDistortionData);
673 ImageData rightImage = new ImageData(Image.CameraType.RIGHT, imageMsg.rightImage, _currentRightDistortionData);
674 Image stereoImage = new Image(imageMsg.info.frame_id, imageMsg.info.timestamp, leftImage, rightImage);
675 LeapImage.DispatchOnContext(this, EventContext, new ImageEventArgs(stereoImage));
676 }
677 }
678
679 private void handlePolicyChange(ref LEAP_POLICY_EVENT policyMsg) {
680 if (LeapPolicyChange != null) {
681 LeapPolicyChange.DispatchOnContext(this, EventContext, new PolicyEventArgs(policyMsg.current_policy, _activePolicies));
682 }
683
684 _activePolicies = policyMsg.current_policy;
685
686 if (_activePolicies != _requestedPolicies) {
687 // This could happen when config is turned off, or
688 // this is the policy change event from the last SetPolicy, after that, the user called SetPolicy again
689 //TODO handle failure to set desired policy -- maybe a PolicyDenied event
690 }
691 }
692
693 public void SetPolicy(Controller.PolicyFlag policy) {
694 UInt64 setFlags = (ulong)flagForPolicy(policy);
695 _requestedPolicies = _requestedPolicies | setFlags;
696 setFlags = _requestedPolicies;
697 UInt64 clearFlags = ~_requestedPolicies; //inverse of desired policies
698
699 eLeapRS result = LeapC.SetPolicyFlags(_leapConnection, setFlags, clearFlags);
700 reportAbnormalResults("LeapC SetPolicyFlags call was ", result);
701 }
702
703 public void ClearPolicy(Controller.PolicyFlag policy) {
704 UInt64 clearFlags = (ulong)flagForPolicy(policy);
705 _requestedPolicies = _requestedPolicies & ~clearFlags;
706 eLeapRS result = LeapC.SetPolicyFlags(_leapConnection, _requestedPolicies, ~_requestedPolicies);
707 reportAbnormalResults("LeapC SetPolicyFlags call was ", result);
708 }
709
710 private eLeapPolicyFlag flagForPolicy(Controller.PolicyFlag singlePolicy) {
711 switch (singlePolicy) {
712 case Controller.PolicyFlag.POLICY_BACKGROUND_FRAMES:
713 return eLeapPolicyFlag.eLeapPolicyFlag_BackgroundFrames;
714 case Controller.PolicyFlag.POLICY_IMAGES:
715 return eLeapPolicyFlag.eLeapPolicyFlag_Images;
716 case Controller.PolicyFlag.POLICY_OPTIMIZE_HMD:
717 return eLeapPolicyFlag.eLeapPolicyFlag_OptimizeHMD;
718 case Controller.PolicyFlag.POLICY_ALLOW_PAUSE_RESUME:
719 return eLeapPolicyFlag.eLeapPolicyFlag_AllowPauseResume;
720 case Controller.PolicyFlag.POLICY_MAP_POINTS:
721 return eLeapPolicyFlag.eLeapPolicyFlag_MapPoints;
722 case Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP:
723 return eLeapPolicyFlag.eLeapPolicyFlag_ScreenTop;
724 case Controller.PolicyFlag.POLICY_DEFAULT:
725 return 0;
726 default:
727 return 0;
728 }
729 }
730
746 public bool IsPolicySet(Controller.PolicyFlag policy) {
747 UInt64 policyToCheck = (ulong)flagForPolicy(policy);
748 return (_activePolicies & policyToCheck) == policyToCheck;
749 }
750
751 public uint GetConfigValue(string config_key) {
752 uint requestId = 0;
753 eLeapRS result = LeapC.RequestConfigValue(_leapConnection, config_key, out requestId);
754 reportAbnormalResults("LeapC RequestConfigValue call was ", result);
755 _configRequests[requestId] = config_key;
756 return requestId;
757 }
758
759 public uint SetConfigValue<T>(string config_key, T value) where T : IConvertible {
760 uint requestId = 0;
761 eLeapRS result;
762 Type dataType = value.GetType();
763 if (dataType == typeof(bool)) {
764 result = LeapC.SaveConfigValue(_leapConnection, config_key, Convert.ToBoolean(value), out requestId);
765 } else if (dataType == typeof(Int32)) {
766 result = LeapC.SaveConfigValue(_leapConnection, config_key, Convert.ToInt32(value), out requestId);
767 } else if (dataType == typeof(float)) {
768 result = LeapC.SaveConfigValue(_leapConnection, config_key, Convert.ToSingle(value), out requestId);
769 } else if (dataType == typeof(string)) {
770 result = LeapC.SaveConfigValue(_leapConnection, config_key, Convert.ToString(value), out requestId);
771 } else {
772 throw new ArgumentException("Only boolean, Int32, float, and string types are supported.");
773 }
774 reportAbnormalResults("LeapC SaveConfigValue call was ", result);
775 _configRequests[requestId] = config_key;
776 return requestId;
777 }
778
784 public bool IsServiceConnected {
785 get {
786 if (_leapConnection == IntPtr.Zero)
787 return false;
788
790 pInfo.size = (uint)Marshal.SizeOf(pInfo);
791 eLeapRS result = LeapC.GetConnectionInfo(_leapConnection, ref pInfo);
792 reportAbnormalResults("LeapC GetConnectionInfo call was ", result);
793
794 if (pInfo.status == eLeapConnectionStatus.eLeapConnectionStatus_Connected)
795 return true;
796
797 return false;
798 }
799 }
800
815 get {
816 if (_devices == null) {
817 _devices = new DeviceList();
818 }
819
820 return _devices;
821 }
822 }
823
825 get {
826 if (_failedDevices == null) {
827 _failedDevices = new FailedDeviceList();
828 }
829
830 return _failedDevices;
831 }
832 }
833
835 LEAP_VECTOR pixelStruct = new LEAP_VECTOR(pixel);
836 LEAP_VECTOR ray = LeapC.LeapPixelToRectilinear(_leapConnection,
837 (camera == Image.CameraType.LEFT ?
838 eLeapPerspectiveType.eLeapPerspectiveType_stereo_left :
839 eLeapPerspectiveType.eLeapPerspectiveType_stereo_right),
840 pixelStruct);
841 return new Vector(ray.x, ray.y, ray.z);
842 }
843
845 LEAP_VECTOR rayStruct = new LEAP_VECTOR(ray);
846 LEAP_VECTOR pixel = LeapC.LeapRectilinearToPixel(_leapConnection,
847 (camera == Image.CameraType.LEFT ?
848 eLeapPerspectiveType.eLeapPerspectiveType_stereo_left :
849 eLeapPerspectiveType.eLeapPerspectiveType_stereo_right),
850 rayStruct);
851 return new Vector(pixel.x, pixel.y, pixel.z);
852 }
853
854 public void TelemetryProfiling(ref LEAP_TELEMETRY_DATA telemetryData) {
855 eLeapRS result = LeapC.LeapTelemetryProfiling(_leapConnection, ref telemetryData);
856 reportAbnormalResults("LeapC TelemetryProfiling call was ", result);
857 }
858
859 public void GetPointMapping(ref PointMapping pm) {
860 UInt64 size = 0;
861 IntPtr buffer = IntPtr.Zero;
862 while (true) {
863 eLeapRS result = LeapC.GetPointMapping(_leapConnection, buffer, ref size);
864 if (result == eLeapRS.eLeapRS_InsufficientBuffer) {
865 if (buffer != IntPtr.Zero)
866 Marshal.FreeHGlobal(buffer);
867 buffer = Marshal.AllocHGlobal((Int32)size);
868 continue;
869 }
870 reportAbnormalResults("LeapC get point mapping call was ", result);
871 if (result != eLeapRS.eLeapRS_Success) {
872 pm.points = null;
873 pm.ids = null;
874 return;
875 }
876 break;
877 }
879 StructMarshal<LEAP_POINT_MAPPING>.PtrToStruct(buffer, out pmi);
880 Int32 nPoints = (Int32)pmi.nPoints;
881
882 pm.frameId = pmi.frame_id;
883 pm.timestamp = pmi.timestamp;
884 pm.points = new Vector[nPoints];
885 pm.ids = new UInt32[nPoints];
886
887 float[] points = new float[3 * nPoints];
888 Int32[] ids = new Int32[nPoints];
889 Marshal.Copy(pmi.points, points, 0, 3 * nPoints);
890 Marshal.Copy(pmi.ids, ids, 0, nPoints);
891
892 int j = 0;
893 for (int i = 0; i < nPoints; i++) {
894 pm.points[i].x = points[j++];
895 pm.points[i].y = points[j++];
896 pm.points[i].z = points[j++];
897 pm.ids[i] = unchecked((UInt32)ids[i]);
898 }
899 Marshal.FreeHGlobal(buffer);
900 }
901
902 private eLeapRS _lastResult; //Used to avoid repeating the same log message, ie. for events like time out
903 private void reportAbnormalResults(string context, eLeapRS result) {
904 if (result != eLeapRS.eLeapRS_Success &&
905 result != _lastResult) {
906 string msg = context + " " + result;
907 if (LeapLogEvent != null) {
908 LeapLogEvent.DispatchOnContext(this, EventContext,
909 new LogEventArgs(MessageSeverity.MESSAGE_CRITICAL,
910 LeapC.GetNow(),
911 msg));
912 }
913 }
914 _lastResult = result;
915 }
916 }
917}
System.Drawing.Image Image
Definition: TestScript.cs:37
Dispatched when the connection is established.
Definition: Events.cs:163
The Controller class is your main interface to the Leap Motion Controller.
The DeviceList class represents a list of Device objects.
Definition: DeviceList.cs:20
Device FindDeviceByHandle(IntPtr deviceHandle)
For internal use only.
Definition: DeviceList.cs:31
void AddOrUpdate(Device device)
For internal use only.
Definition: DeviceList.cs:62
The DistortionData class contains the distortion map for correcting the lens distortion of an image.
UInt64 Version
An identifier assigned to the distortion map.
bool IsValid
Reports whether the distortion data is internally consistent.
The list of FailedDevice objects contains an entry for every failed Leap Motion hardware device conne...
The Frame class represents a set of hand and finger tracking data detected in a single frame.
Definition: Frame.cs:24
The Image class represents a stereo image pair from the Leap Motion device.
Definition: Image.cs:20
CameraType
Definition: Image.cs:386
A generic object with no arguments beyond the event type.
Definition: Events.cs:42
Dispatched when loggable events are generated by the service and the service connection code.
Definition: Events.cs:78
void GetInterpolatedLeftRightTransform(Int64 time, Int64 sourceTime, Int64 leftId, Int64 rightId, out LeapTransform leftTransform, out LeapTransform rightTransform)
Definition: Connection.cs:405
SynchronizationContext EventContext
Definition: Connection.cs:78
void TelemetryProfiling(ref LEAP_TELEMETRY_DATA telemetryData)
Definition: Connection.cs:854
Action< EndProfilingBlockArgs > LeapEndProfilingBlock
Definition: Connection.cs:118
bool IsServiceConnected
Reports whether your application has a connection to the Leap Motion daemon/service....
Definition: Connection.cs:784
uint GetConfigValue(string config_key)
Definition: Connection.cs:751
EventHandler< ConnectionLostEventArgs > LeapConnectionLost
Definition: Connection.cs:99
Action< BeginProfilingForThreadArgs > LeapBeginProfilingForThread
Definition: Connection.cs:115
Action< BeginProfilingBlockArgs > LeapBeginProfilingBlock
Definition: Connection.cs:117
EventHandler< DeviceEventArgs > LeapDeviceLost
Definition: Connection.cs:101
Action< EndProfilingForThreadArgs > LeapEndProfilingForThread
Definition: Connection.cs:116
EventHandler< LogEventArgs > LeapLogEvent
Definition: Connection.cs:106
EventHandler< LeapEventArgs > LeapInit
Definition: Connection.cs:81
static Connection GetConnection(int connectionId=0)
Definition: Connection.cs:30
void GetInterpolatedFrame(Frame toFill, Int64 time)
Definition: Connection.cs:362
virtual void Dispose(bool disposing)
Definition: Connection.cs:128
EventHandler< FrameEventArgs > LeapFrame
Definition: Connection.cs:104
bool IsPolicySet(Controller.PolicyFlag policy)
Gets the active setting for a specific policy.
Definition: Connection.cs:746
EventHandler< ConfigChangeEventArgs > LeapConfigChange
Definition: Connection.cs:108
EventHandler< ConnectionEventArgs > LeapConnection
Definition: Connection.cs:91
EventHandler< DroppedFrameEventArgs > LeapDroppedFrame
Definition: Connection.cs:110
EventHandler< ImageEventArgs > LeapImage
Definition: Connection.cs:111
void GetInterpolatedFrameFromTime(Frame toFill, Int64 time, Int64 sourceTime)
Definition: Connection.cs:375
static Connection GetConnection(Key connectionKey)
Definition: Connection.cs:34
EventHandler< SetConfigResponseEventArgs > LeapConfigResponse
Definition: Connection.cs:107
EventHandler< DeviceFailureEventArgs > LeapDeviceFailure
Definition: Connection.cs:102
void GetInterpolatedHeadPose(ref LEAP_HEAD_POSE_EVENT toFill, Int64 time)
Definition: Connection.cs:394
CircularObjectBuffer< LEAP_TRACKING_EVENT > Frames
Definition: Connection.cs:57
void SetPolicy(Controller.PolicyFlag policy)
Definition: Connection.cs:693
DeviceList Devices
The list of currently attached and recognized Leap Motion controller devices.
Definition: Connection.cs:814
LEAP_HEAD_POSE_EVENT GetInterpolatedHeadPose(Int64 time)
Definition: Connection.cs:399
UInt64 GetInterpolatedFrameSize(Int64 time)
Definition: Connection.cs:355
EventHandler< DistortionEventArgs > LeapDistortionChange
Definition: Connection.cs:109
EventHandler< PolicyEventArgs > LeapPolicyChange
Definition: Connection.cs:103
void GetPointMapping(ref PointMapping pm)
Definition: Connection.cs:859
Vector RectilinearToPixel(Image.CameraType camera, Vector ray)
Definition: Connection.cs:844
Vector PixelToRectilinear(Image.CameraType camera, Vector pixel)
Definition: Connection.cs:834
EventHandler< InternalFrameEventArgs > LeapInternalFrame
Definition: Connection.cs:105
void ClearPolicy(Controller.PolicyFlag policy)
Definition: Connection.cs:703
EventHandler< PointMappingChangeEventArgs > LeapPointMappingChange
Definition: Connection.cs:112
EventHandler< DeviceEventArgs > LeapDevice
Definition: Connection.cs:100
uint SetConfigValue< T >(string config_key, T value)
Definition: Connection.cs:759
FailedDeviceList FailedDevices
Definition: Connection.cs:824
EventHandler< HeadPoseEventArgs > LeapHeadPoseChange
Definition: Connection.cs:113
Frame GetInterpolatedFrame(Int64 time)
Definition: Connection.cs:388
static eLeapRS OpenConnection(IntPtr hConnection)
static eLeapRS CreateConnection(ref LEAP_CONNECTION_CONFIG pConfig, out IntPtr pConnection)
static eLeapRS InterpolateFrame(IntPtr hConnection, Int64 timestamp, IntPtr pEvent, UInt64 ncbEvent)
static eLeapRS GetPointMapping(IntPtr hConnection, IntPtr pointMapping, ref ulong pSize)
static eLeapRS InterpolateHeadPose(IntPtr hConnection, Int64 timestamp, ref LEAP_HEAD_POSE_EVENT headPose)
static LEAP_VECTOR LeapRectilinearToPixel(IntPtr hConnection, eLeapPerspectiveType camera, LEAP_VECTOR rectilinear)
static eLeapRS LeapTelemetryProfiling(IntPtr hConnection, ref LEAP_TELEMETRY_DATA telemetryData)
static long GetNow()
static eLeapRS RequestConfigValue(IntPtr hConnection, string name, out UInt32 request_id)
static void DestroyConnection(IntPtr connection)
static LEAP_VECTOR LeapPixelToRectilinear(IntPtr hConnection, eLeapPerspectiveType camera, LEAP_VECTOR pixel)
static eLeapRS GetConnectionInfo(IntPtr hConnection, ref LEAP_CONNECTION_INFO pInfo)
static eLeapRS InterpolateFrameFromTime(IntPtr hConnection, Int64 timestamp, Int64 sourceTimestamp, IntPtr pEvent, UInt64 ncbEvent)
static eLeapRS SetPolicyFlags(IntPtr hConnection, UInt64 set, UInt64 clear)
static eLeapRS GetFrameSize(IntPtr hConnection, Int64 timestamp, out UInt64 pncbEvent)
static eLeapRS CloseConnection(IntPtr hConnection)
static eLeapRS SetAllocator(IntPtr hConnection, ref LEAP_ALLOCATOR pAllocator)
MessageSeverity
Reports whether the message is for a severe failure, a recoverable warning, or a status change.
LeapEvent
An enumeration defining the types of Leap Motion events.
Definition: Events.cs:17
eLeapDeviceStatus
Definition: LeapC.cs:108
eLeapConnectionStatus
Definition: LeapC.cs:14
eLeapValueType
Definition: LeapC.cs:222
eLeapEventType
Definition: LeapC.cs:347
eLeapPolicyFlag
Definition: LeapC.cs:80
eLeapLogSeverity
Definition: LeapC.cs:203
eLeapPerspectiveType
Definition: LeapC.cs:179
The LeapTransform class represents a transform in three dimensional space.
static readonly LeapTransform Identity
The identity transform.
The Vector struct represents a three-component mathematical vector or point such as a direction or po...
Definition: Vector.cs:36
float x
Definition: Vector.cs:218
readonly string serverNamespace
Definition: Connection.cs:20
Key(int connectionId, string serverNamespace=null)
Definition: Connection.cs:22
eLeapConnectionStatus status
Definition: LeapC.cs:476