Leap Motion C API  3.1
The API to the LeapC library.
Callback Example

This example demonstrates the basic usage of the LeapC API to get tracking data.

Sample.c

Sample.c is an example client application that consumes the tracking data – in this case, by simply printing basic data to stdout.

Sample.C defines three callback functions, one each for OnConnect, OnDevice, and OnFrame. Each of these callback functions are called by ExampleConnection when the LeapC library returns the associated event. The OnConnect callback doesn't have any associated data, but you can use the event to initialize parts of your application that depend on a connection. OnDevice provides the LEAP_DEVICE_INFO struct describing the connected device. OnFrame provides the LEAP_TRACKING_EVENT struct that contains all the tracking data (except images) for the latest frame.

Callback functions are invoked directly from the ExampleConnection thread created to service the LeapC message pump. This can be problematic for applications that use UI or graphics libraries that must be called on a particular thread. For such applications, you must implement a means for one thread to hand of data to the other. The best approach is platform dependent. For example, the Windows API provides a SynchronizationContext construct that can be used in some cases. The (Polling Example)[polling-example] demonstrates an approach in which the latest data objects are cached by the servicing thread and the application can access these objects when ready. The (Frame Interpolation Example)[interpolation-example] demonstrates another way around the problem for tracking data, which works because interpolated frames are returned directly to the calling function rather than passing throught the LeapC message pump.

1 #undef __cplusplus
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "LeapC.h"
6 #include "ExampleConnection.h"
7 
8 /** Callback for when the connection opens. */
9 void OnConnect(){
10  printf("Connected.\n");
11 }
12 
13 /** Callback for when a device is found. */
14 void OnDevice(const LEAP_DEVICE_INFO *props){
15  printf("Found device %s.\n", props->serial);
16 }
17 
18 /** Callback for when a frame of tracking data is available. */
19 void OnFrame(const LEAP_TRACKING_EVENT *frame){
20  printf("Frame %lli with %i hands.\n", (long long int)frame->info.frame_id, frame->nHands);
21  for(uint32_t h = 0; h < frame->nHands; h++){
22  LEAP_HAND* hand = &frame->pHands[h];
23  printf(" Hand id %i is a %s hand with position (%f, %f, %f).\n",
24  hand->id,
25  (hand->type == eLeapHandType_Left ? "left" : "right"),
26  hand->palm.position.x,
27  hand->palm.position.y,
28  hand->palm.position.z);
29  }
30 }
31 
32 
33 int main(int argc, char** argv) {
34  //Set callback function pointers
35  ConnectionCallbacks.on_connection = &OnConnect;
36  ConnectionCallbacks.on_device_found = &OnDevice;
37  ConnectionCallbacks.on_frame = &OnFrame;
38 
39  OpenConnection();
40 
41  printf("Press Enter to exit program.\n");
42  getchar();
43  return 0;
44 }
45 //End-of-Sample.c

ExampleConnection.h

ExampleConnection.h defines the public functions that should be called by applications. Any other functions should be considered private.

Typedefs and function pointers for the supported callback functions are also defined here. Sample.c only uses three of the callbacks, but the complete set are defined in this example.

1 #ifndef ExampleConnection_h
2 #define ExampleConnection_h
3 
4 #include "LeapC.h"
5 
6 /* Client functions */
7 LEAP_CONNECTION* OpenConnection();
8 void CloseConnection();
9 void CloseConnectionHandle(LEAP_CONNECTION* connectionHandle);
10 LEAP_TRACKING_EVENT* GetFrame(); //Used in polling example
11 LEAP_DEVICE_INFO* GetDeviceProperties(); //Used in polling example
12 const char* ResultString(eLeapRS r);
13 
14 /* State */
15 extern bool IsConnected;
16 
17 /* Callback function pointers */
18 typedef void (*connection_callback) ();
19 typedef void (*device_callback) (const LEAP_DEVICE_INFO *device);
20 typedef void (*device_lost_callback) ();
21 typedef void (*device_failure_callback) (const eLeapDeviceStatus failure_code,
22  const LEAP_DEVICE failed_device);
23 typedef void (*policy_callback) (const uint32_t current_policies);
24 typedef void (*tracking_callback) (const LEAP_TRACKING_EVENT *tracking_event);
25 typedef void (*image_callback) (const LEAP_IMAGE_COMPLETE_EVENT *image_complete_event);
26 typedef void (*image_error_callback) (const LEAP_IMAGE_FRAME_REQUEST_ERROR_EVENT *image_error_event);
27 typedef void (*log_callback) (const eLeapLogSeverity severity,
28  const int64_t timestamp,
29  const char* message);
30 typedef void (*config_change_callback) (const uint32_t requestID, const bool success);
31 typedef void (*config_response_callback)(const uint32_t requestID, LEAP_VARIANT value);
32 
33 struct Callbacks{
34  connection_callback on_connection;
35  connection_callback on_connection_lost;
36  device_callback on_device_found;
37  device_lost_callback on_device_lost;
38  device_failure_callback on_device_failure;
39  policy_callback on_policy;
40  tracking_callback on_frame;
41  image_callback on_image_complete;
42  image_error_callback on_image_request_error;
43  log_callback on_log_message;
44  config_change_callback on_config_change;
45  config_response_callback on_config_response;
46 };
47 extern struct Callbacks ConnectionCallbacks;
48 extern void millisleep(int milliseconds);
49 #endif /* ExampleConnection_h */

ExampleConnection.c

ExampleConnection.c demonstrates how to service the LeapC message pump. LeapC gathers events from the Leap Motion service and places them in a queues. Client applications must service this queue by calling LeapPollConnection(). A good way to do this is in a dedicated thread – otherwise servicing the message pump may block the application thread, or conversely, fall behind and lose event messages.

The client application calls the OpenConnection() function in ExampleConnection, which calls LeapC functions to establish the connection with the Leap Motion service. If the connection is successful, a new thread is started to run serviceMessageLoop().

ServiceMessageLoop() calls the LeapC LeapPollConnection() in a tight loop. When tracking frames are being produced, the average time elapsed per loop iteration will equal the service frame rate (about 9ms). A separate handler function is defined for each possible LeapC event, which, in turn, invoke the relevant callback funtions, if the main application has provided one.

Note that ExampleConnection is intended as a simple demonstration only – not as production-ready code.

1 #include "ExampleConnection.h"
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #if defined(_MSC_VER)
6  #include <Windows.h>
7  #include <process.h>
8  #define LockMutex EnterCriticalSection
9  #define UnlockMutex LeaveCriticalSection
10 #else
11  #include <unistd.h>
12  #include <pthread.h>
13  #define LockMutex pthread_mutex_lock
14  #define UnlockMutex pthread_mutex_unlock
15 #endif
16 
17 
18 //Forward declarations
19 const char* ResultString(eLeapRS r);
20 #if defined(_MSC_VER)
21 void serviceMessageLoop(void * unused);
22 #else
23 void* serviceMessageLoop(void * unused);
24 #endif
25 void setFrame(const LEAP_TRACKING_EVENT *frame);
26 void setDevice(const LEAP_DEVICE_INFO *deviceProps);
27 
28 //External state
29 bool IsConnected = false;
30 
31 //Internal state
32 bool _isRunning = false;
33 LEAP_CONNECTION connectionHandle;
34 LEAP_TRACKING_EVENT *lastFrame = NULL;
35 LEAP_DEVICE_INFO *lastDevice = NULL;
36 
37 //Callback function pointers
38 struct Callbacks ConnectionCallbacks;
39 
40 //Threading variables
41 #if defined(_MSC_VER)
42 uintptr_t pollingThread;
43 CRITICAL_SECTION dataLock;
44 #else
45 pthread_t pollingThread;
46 pthread_mutex_t dataLock;
47 #endif
48 
49 /**
50  * Creates the connection handle and opens a connection to the Leap Motion
51  * service. On success, creates a thread to service the LeapC message pump.
52  */
53 LEAP_CONNECTION* OpenConnection(){
54  eLeapRS result = LeapCreateConnection(NULL, &connectionHandle);
55  if(result == eLeapRS_Success){
56  result = LeapOpenConnection(connectionHandle);
57  if(result == eLeapRS_Success){
58  _isRunning = true;
59 #if defined(_MSC_VER)
60  InitializeCriticalSection(&dataLock);
61  pollingThread = _beginthread(serviceMessageLoop, 0, NULL);
62 #else
63  pthread_create(&pollingThread, NULL, serviceMessageLoop, NULL);
64 #endif
65  }
66  }
67  return &connectionHandle;
68 }
69 
70 void CloseConnection(){
71  CloseConnectionHandle(&connectionHandle);
72 }
73 
74 /** Close the connection and let message thread function end. */
75 void CloseConnectionHandle(LEAP_CONNECTION* connectionHandle){
76  LeapDestroyConnection(*connectionHandle);
77  _isRunning = false;
78 }
79 
80 /** Called by serviceMessageLoop() when a connection event is returned by LeapPollConnection(). */
81 void handleConnectionEvent(const LEAP_CONNECTION_EVENT *connection_event){
82  IsConnected = true;
83  if(ConnectionCallbacks.on_connection){
84  ConnectionCallbacks.on_connection();
85  }
86 }
87 
88 /** Called by serviceMessageLoop() when a connection lost event is returned by LeapPollConnection(). */
89 void handleConnectionLostEvent(const LEAP_CONNECTION_LOST_EVENT *connection_lost_event){
90  IsConnected = false;
91  if(ConnectionCallbacks.on_connection_lost){
92  ConnectionCallbacks.on_connection_lost();
93  }
94 }
95 
96 /**
97  * Called by serviceMessageLoop() when a device event is returned by LeapPollConnection()
98  * Demonstrates how to access device properties.
99  */
100 void handleDeviceEvent(const LEAP_DEVICE_EVENT *device_event){
101  LEAP_DEVICE deviceHandle;
102  //Open device using LEAP_DEVICE_REF from event struct.
103  eLeapRS result = LeapOpenDevice(device_event->device, &deviceHandle);
104  if(result != eLeapRS_Success){
105  printf("Could not open device %s.\n", ResultString(result));
106  return;
107  }
108 
109  //Create a struct to hold the device properties, we have to provide a buffer for the serial string
110  LEAP_DEVICE_INFO deviceProperties = { sizeof(deviceProperties) };
111  // Start with a length of 1 (pretending we don't know a priori what the length is).
112  // Currently device serial numbers are all the same length, but that could change in the future
113  deviceProperties.serial_length = 1;
114  deviceProperties.serial = malloc(deviceProperties.serial_length);
115  //This will fail since the serial buffer is only 1 character long
116  // But deviceProperties is updated to contain the required buffer length
117  result = LeapGetDeviceInfo(deviceHandle, &deviceProperties);
118  if(result == eLeapRS_InsufficientBuffer){
119  //try again with correct buffer size
120  deviceProperties.serial = realloc(deviceProperties.serial, deviceProperties.serial_length);
121  result = LeapGetDeviceInfo(deviceHandle, &deviceProperties);
122  if(result != eLeapRS_Success){
123  printf("Failed to get device info %s.\n", ResultString(result));
124  free(deviceProperties.serial);
125  return;
126  }
127  }
128  setDevice(&deviceProperties);
129  if(ConnectionCallbacks.on_device_found){
130  ConnectionCallbacks.on_device_found(&deviceProperties);
131  }
132 
133  free(deviceProperties.serial);
134  LeapCloseDevice(deviceHandle);
135 }
136 
137 /** Called by serviceMessageLoop() when a device lost event is returned by LeapPollConnection(). */
138 void handleDeviceLostEvent(const LEAP_DEVICE_EVENT *device_event){
139  if(ConnectionCallbacks.on_device_lost){
140  ConnectionCallbacks.on_device_lost();
141  }
142 }
143 
144 /** Called by serviceMessageLoop() when a device failure event is returned by LeapPollConnection(). */
145 void handleDeviceFailureEvent(const LEAP_DEVICE_FAILURE_EVENT *device_failure_event){
146  if(ConnectionCallbacks.on_device_failure){
147  ConnectionCallbacks.on_device_failure(device_failure_event->status, device_failure_event->hDevice);
148  }
149 }
150 
151 /** Called by serviceMessageLoop() when a tracking event is returned by LeapPollConnection(). */
152 void handleTrackingEvent(const LEAP_TRACKING_EVENT *tracking_event){
153  setFrame(tracking_event); //support polling tracking data from different thread
154  if(ConnectionCallbacks.on_frame){
155  ConnectionCallbacks.on_frame(tracking_event);
156  }
157 }
158 
159 /** Called by serviceMessageLoop() when an image complete event is returned by LeapPollConnection(). */
160 void handleImageCompleteEvent(const LEAP_IMAGE_COMPLETE_EVENT *image_complete_event){
161  if(ConnectionCallbacks.on_image_complete){
162  ConnectionCallbacks.on_image_complete(image_complete_event);
163  }
164 }
165 
166 /** Called by serviceMessageLoop() when an image reuest error event is returned by LeapPollConnection(). */
167 void handleImageRequestErrorEvent(const LEAP_IMAGE_FRAME_REQUEST_ERROR_EVENT *image_request_error_event){
168  if(ConnectionCallbacks.on_image_request_error){
169  ConnectionCallbacks.on_image_request_error(image_request_error_event);
170  }
171 }
172 
173 /** Called by serviceMessageLoop() when a log event is returned by LeapPollConnection(). */
174 void handleLogEvent(const LEAP_LOG_EVENT *log_event){
175  if(ConnectionCallbacks.on_log_message){
176  ConnectionCallbacks.on_log_message(log_event->Severity, log_event->Timestamp, log_event->Message);
177  }
178 }
179 
180 /** Called by serviceMessageLoop() when a policy event is returned by LeapPollConnection(). */
181 void handlePolicyEvent(const LEAP_POLICY_EVENT *policy_event){
182  if(ConnectionCallbacks.on_policy){
183  ConnectionCallbacks.on_policy(policy_event->current_policy);
184  }
185 }
186 
187 /** Called by serviceMessageLoop() when a config change event is returned by LeapPollConnection(). */
188 void handleConfigChangeEvent(const LEAP_CONFIG_CHANGE_EVENT *config_change_event){
189  if(ConnectionCallbacks.on_config_change){
190  ConnectionCallbacks.on_config_change(config_change_event->requestID, config_change_event->status);
191  }
192 }
193 
194 /** Called by serviceMessageLoop() when a config response event is returned by LeapPollConnection(). */
195 void handleConfigResponseEvent(const LEAP_CONFIG_RESPONSE_EVENT *config_response_event){
196  if(ConnectionCallbacks.on_config_response){
197  ConnectionCallbacks.on_config_response(config_response_event->requestID, config_response_event->value);
198  }
199 }
200 
201 /**
202  * Services the LeapC message pump by calling LeapPollConnection().
203  * The average polling time is determined by the framerate of the Leap Motion service.
204  */
205 #if defined(_MSC_VER)
206 void serviceMessageLoop(void * unused){
207 #else
208 void* serviceMessageLoop(void * unused){
209 #endif
210  eLeapRS result;
211  LEAP_CONNECTION_MESSAGE msg;
212  while(_isRunning){
213  unsigned int timeout = 1000;
214  result = LeapPollConnection(connectionHandle, timeout, &msg);
215 
216  if (result != eLeapRS_Success) {
217  printf("LeapC PollConnection call was %s.\n", ResultString(result));
218  continue;
219  }
220 
221  switch (msg.type){
222  case eLeapEventType_Connection:
223  handleConnectionEvent(msg.connection_event);
224  break;
225  case eLeapEventType_ConnectionLost:
226  handleConnectionLostEvent(msg.connection_lost_event);
227  break;
228  case eLeapEventType_Device:
229  handleDeviceEvent(msg.device_event);
230  break;
231  case eLeapEventType_DeviceLost:
232  handleDeviceLostEvent(msg.device_event);
233  break;
234  case eLeapEventType_DeviceFailure:
235  handleDeviceFailureEvent(msg.device_failure_event);
236  break;
237  case eLeapEventType_Tracking:
238  handleTrackingEvent(msg.tracking_event);
239  break;
240  case eLeapEventType_ImageComplete:
241  handleImageCompleteEvent(msg.image_complete_event);
242  break;
243  case eLeapEventType_ImageRequestError:
244  handleImageRequestErrorEvent(msg.image_request_error_event);
245  break;
246  case eLeapEventType_LogEvent:
247  handleLogEvent(msg.log_event);
248  break;
249  case eLeapEventType_Policy:
250  handlePolicyEvent(msg.policy_event);
251  break;
252  case eLeapEventType_ConfigChange:
253  handleConfigChangeEvent(msg.config_change_event);
254  break;
255  case eLeapEventType_ConfigResponse:
256  handleConfigResponseEvent(msg.config_response_event);
257  break;
258  default:
259  //discard unknown message types
260  printf("Unhandled message type %i.\n", msg.type);
261  } //switch on msg.type
262  }
263 #if !defined(_MSC_VER)
264  return NULL;
265 #endif
266 }
267 
268 /* Used in Polling Example: */
269 
270 /**
271  * Caches the newest frame by copying the tracking event struct returned by
272  * LeapC.
273  */
274 void setFrame(const LEAP_TRACKING_EVENT *frame){
275  LockMutex(&dataLock);
276  if(!lastFrame) lastFrame = malloc(sizeof(*frame));
277  *lastFrame = *frame;
278  UnlockMutex(&dataLock);
279 }
280 
281 /** Returns a pointer to the cached tracking frame. */
282 LEAP_TRACKING_EVENT* GetFrame(){
283  LEAP_TRACKING_EVENT *currentFrame;
284 
285  LockMutex(&dataLock);
286  currentFrame = lastFrame;
287  UnlockMutex(&dataLock);
288 
289  return currentFrame;
290 }
291 
292 /**
293  * Caches the last device found by copying the device info struct returned by
294  * LeapC.
295  */
296 void setDevice(const LEAP_DEVICE_INFO *deviceProps){
297  LockMutex(&dataLock);
298  if(lastDevice){
299  free(lastDevice->serial);
300  } else {
301  lastDevice = malloc(sizeof(*deviceProps));
302  }
303  *lastDevice = *deviceProps;
304  lastDevice->serial = malloc(deviceProps->serial_length);
305  memcpy(lastDevice->serial, deviceProps->serial, deviceProps->serial_length);
306  UnlockMutex(&dataLock);
307 }
308 
309 /** Returns a pointer to the cached device info. */
310 LEAP_DEVICE_INFO* GetDeviceProperties(){
311  LEAP_DEVICE_INFO *currentDevice;
312  LockMutex(&dataLock);
313  currentDevice = lastDevice;
314  UnlockMutex(&dataLock);
315  return currentDevice;
316 }
317 
318 //End of polling example-specific code
319 
320 /** Translates eLeapRS result codes into a human-readable string. */
321 const char* ResultString(eLeapRS r) {
322  switch(r){
323  case eLeapRS_Success: return "eLeapRS_Success";
324  case eLeapRS_UnknownError: return "eLeapRS_UnknownError";
325  case eLeapRS_InvalidArgument: return "eLeapRS_InvalidArgument";
326  case eLeapRS_InsufficientResources: return "eLeapRS_InsufficientResources";
327  case eLeapRS_InsufficientBuffer: return "eLeapRS_InsufficientBuffer";
328  case eLeapRS_Timeout: return "eLeapRS_Timeout";
329  case eLeapRS_NotConnected: return "eLeapRS_NotConnected";
330  case eLeapRS_HandshakeIncomplete: return "eLeapRS_HandshakeIncomplete";
331  case eLeapRS_BufferSizeOverflow: return "eLeapRS_BufferSizeOverflow";
332  case eLeapRS_ProtocolError: return "eLeapRS_ProtocolError";
333  case eLeapRS_InvalidClientID: return "eLeapRS_InvalidClientID";
334  case eLeapRS_UnexpectedClosed: return "eLeapRS_UnexpectedClosed";
335  case eLeapRS_UnknownImageFrameRequest: return "eLeapRS_UnknownImageFrameRequest";
336  case eLeapRS_UnknownTrackingFrameID: return "eLeapRS_UnknownTrackingFrameID";
337  case eLeapRS_RoutineIsNotSeer: return "eLeapRS_RoutineIsNotSeer";
338  case eLeapRS_TimestampTooEarly: return "eLeapRS_TimestampTooEarly";
339  case eLeapRS_ConcurrentPoll: return "eLeapRS_ConcurrentPoll";
340  case eLeapRS_NotAvailable: return "eLeapRS_NotAvailable";
341  case eLeapRS_NotStreaming: return "eLeapRS_NotStreaming";
342  case eLeapRS_CannotOpenDevice: return "eLeapRS_CannotOpenDevice";
343  default: return "unknown result type.";
344  }
345 }
346 /** Cross-platform sleep function */
347 void millisleep(int milliseconds){
348 #ifdef _WIN32
349  Sleep(milliseconds);
350 #else
351  usleep(milliseconds*1000);
352 #endif
353  }
354 //End-of-ExampleConnection.c