This example demonstrates the basic usage of the LeapC API to get tracking data.
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 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 Interpolated Frames 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.
6 #include "ExampleConnection.h"
8 static LEAP_CONNECTION* connectionHandle;
10 /** Callback for when the connection opens. */
11 static void OnConnect(){
12 printf("Connected.\n");
15 /** Callback for when a device is found. */
16 static void OnDevice(const LEAP_DEVICE_INFO *props){
17 printf("Found device %s.\n", props->serial);
20 /** Callback for when a frame of tracking data is available. */
21 static void OnFrame(const LEAP_TRACKING_EVENT *frame){
22 printf("Frame %lli with %i hands.\n", (long long int)frame->info.frame_id, frame->nHands);
24 for(uint32_t h = 0; h < frame->nHands; h++){
25 LEAP_HAND* hand = &frame->pHands[h];
26 printf(" Hand id %i is a %s hand with position (%f, %f, %f).\n",
28 (hand->type == eLeapHandType_Left ? "left" : "right"),
29 hand->palm.position.x,
30 hand->palm.position.y,
31 hand->palm.position.z);
35 static void OnImage(const LEAP_IMAGE_EVENT *image){
36 printf("Image %lli => Left: %d x %d (bpp=%d), Right: %d x %d (bpp=%d)\n",
37 (long long int)image->info.frame_id,
38 image->image[0].properties.width,image->image[0].properties.height,image->image[0].properties.bpp*8,
39 image->image[1].properties.width,image->image[1].properties.height,image->image[1].properties.bpp*8);
42 static void OnLogMessage(const eLeapLogSeverity severity, const int64_t timestamp,
43 const char* message) {
44 const char* severity_str;
46 case eLeapLogSeverity_Critical:
47 severity_str = "Critical";
49 case eLeapLogSeverity_Warning:
50 severity_str = "Warning";
52 case eLeapLogSeverity_Information:
53 severity_str = "Info";
59 printf("[%s][%lli] %s\n", severity_str, (long long int)timestamp, message);
62 static void* allocate(uint32_t size, eLeapAllocatorType typeHint, void* state) {
63 void* ptr = malloc(size);
67 static void deallocate(void* ptr, void* state) {
73 void OnPointMappingChange(const LEAP_POINT_MAPPING_CHANGE_EVENT *change){
74 if (!connectionHandle)
78 if (LeapGetPointMappingSize(*connectionHandle, &size) != eLeapRS_Success || !size)
81 LEAP_POINT_MAPPING* pointMapping = (LEAP_POINT_MAPPING*)malloc(size);
85 if (LeapGetPointMapping(*connectionHandle, pointMapping, &size) == eLeapRS_Success &&
86 pointMapping->nPoints > 0) {
87 printf("Managing %u points as of frame %lld at %lld\n", pointMapping->nPoints, (long long int)pointMapping->frame_id, (long long int)pointMapping->timestamp);
92 void OnHeadPose(const LEAP_HEAD_POSE_EVENT *event) {
93 printf("Head pose:\n");
94 printf(" Head position (%f, %f, %f).\n",
95 event->head_position.x,
96 event->head_position.y,
97 event->head_position.z);
98 printf(" Head orientation (%f, %f, %f, %f).\n",
99 event->head_orientation.w,
100 event->head_orientation.x,
101 event->head_orientation.y,
102 event->head_orientation.z);
105 int main(int argc, char** argv) {
106 //Set callback function pointers
107 ConnectionCallbacks.on_connection = &OnConnect;
108 ConnectionCallbacks.on_device_found = &OnDevice;
109 ConnectionCallbacks.on_frame = &OnFrame;
110 ConnectionCallbacks.on_image = &OnImage;
111 ConnectionCallbacks.on_point_mapping_change = &OnPointMappingChange;
112 ConnectionCallbacks.on_log_message = &OnLogMessage;
113 ConnectionCallbacks.on_head_pose = &OnHeadPose;
115 connectionHandle = OpenConnection();
117 LEAP_ALLOCATOR allocator = { allocate, deallocate, NULL };
118 LeapSetAllocator(*connectionHandle, &allocator);
120 LeapSetPolicyFlags(*connectionHandle, eLeapPolicyFlag_Images | eLeapPolicyFlag_MapPoints, 0);
122 printf("Press Enter to exit program.\n");
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.
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().
Note that ExampleConnection is intended as a simple demonstration only – not as production-ready code.
1 #include "ExampleConnection.h"
8 #define LockMutex EnterCriticalSection
9 #define UnlockMutex LeaveCriticalSection
13 #define LockMutex pthread_mutex_lock
14 #define UnlockMutex pthread_mutex_unlock
18 //Forward declarations
20 static void serviceMessageLoop(void * unused);
22 static void* serviceMessageLoop(void * unused);
24 static void setFrame(const LEAP_TRACKING_EVENT *frame);
25 static void setDevice(const LEAP_DEVICE_INFO *deviceProps);
28 bool IsConnected = false;
31 static volatile bool _isRunning = false;
32 static LEAP_CONNECTION connectionHandle = NULL;
33 static LEAP_TRACKING_EVENT *lastFrame = NULL;
34 static LEAP_DEVICE_INFO *lastDevice = NULL;
36 //Callback function pointers
37 struct Callbacks ConnectionCallbacks;
41 static HANDLE pollingThread;
42 static CRITICAL_SECTION dataLock;
44 static pthread_t pollingThread;
45 static pthread_mutex_t dataLock;
49 * Creates the connection handle and opens a connection to the Leap Motion
50 * service. On success, creates a thread to service the LeapC message pump.
52 LEAP_CONNECTION* OpenConnection(){
54 return &connectionHandle;
56 if(connectionHandle || LeapCreateConnection(NULL, &connectionHandle) == eLeapRS_Success){
57 eLeapRS result = LeapOpenConnection(connectionHandle);
58 if(result == eLeapRS_Success){
61 InitializeCriticalSection(&dataLock);
62 pollingThread = (HANDLE)_beginthread(serviceMessageLoop, 0, NULL);
64 pthread_create(&pollingThread, NULL, serviceMessageLoop, NULL);
68 return &connectionHandle;
71 void CloseConnection(){
76 LeapCloseConnection(connectionHandle);
78 WaitForSingleObject(pollingThread, INFINITE);
79 CloseHandle(pollingThread);
81 pthread_join(pollingThread, NULL);
85 void DestroyConnection(){
87 LeapDestroyConnection(connectionHandle);
91 /** Close the connection and let message thread function end. */
92 void CloseConnectionHandle(LEAP_CONNECTION* connectionHandle){
93 LeapDestroyConnection(*connectionHandle);
97 /** Called by serviceMessageLoop() when a connection event is returned by LeapPollConnection(). */
98 static void handleConnectionEvent(const LEAP_CONNECTION_EVENT *connection_event){
100 if(ConnectionCallbacks.on_connection){
101 ConnectionCallbacks.on_connection();
105 /** Called by serviceMessageLoop() when a connection lost event is returned by LeapPollConnection(). */
106 static void handleConnectionLostEvent(const LEAP_CONNECTION_LOST_EVENT *connection_lost_event){
108 if(ConnectionCallbacks.on_connection_lost){
109 ConnectionCallbacks.on_connection_lost();
114 * Called by serviceMessageLoop() when a device event is returned by LeapPollConnection()
115 * Demonstrates how to access device properties.
117 static void handleDeviceEvent(const LEAP_DEVICE_EVENT *device_event){
118 LEAP_DEVICE deviceHandle;
119 //Open device using LEAP_DEVICE_REF from event struct.
120 eLeapRS result = LeapOpenDevice(device_event->device, &deviceHandle);
121 if(result != eLeapRS_Success){
122 printf("Could not open device %s.\n", ResultString(result));
126 //Create a struct to hold the device properties, we have to provide a buffer for the serial string
127 LEAP_DEVICE_INFO deviceProperties = { sizeof(deviceProperties) };
128 // Start with a length of 1 (pretending we don't know a priori what the length is).
129 // Currently device serial numbers are all the same length, but that could change in the future
130 deviceProperties.serial_length = 1;
131 deviceProperties.serial = malloc(deviceProperties.serial_length);
132 //This will fail since the serial buffer is only 1 character long
133 // But deviceProperties is updated to contain the required buffer length
134 result = LeapGetDeviceInfo(deviceHandle, &deviceProperties);
135 if(result == eLeapRS_InsufficientBuffer){
136 //try again with correct buffer size
137 deviceProperties.serial = realloc(deviceProperties.serial, deviceProperties.serial_length);
138 result = LeapGetDeviceInfo(deviceHandle, &deviceProperties);
139 if(result != eLeapRS_Success){
140 printf("Failed to get device info %s.\n", ResultString(result));
141 free(deviceProperties.serial);
145 setDevice(&deviceProperties);
146 if(ConnectionCallbacks.on_device_found){
147 ConnectionCallbacks.on_device_found(&deviceProperties);
150 free(deviceProperties.serial);
151 LeapCloseDevice(deviceHandle);
154 /** Called by serviceMessageLoop() when a device lost event is returned by LeapPollConnection(). */
155 static void handleDeviceLostEvent(const LEAP_DEVICE_EVENT *device_event){
156 if(ConnectionCallbacks.on_device_lost){
157 ConnectionCallbacks.on_device_lost();
161 /** Called by serviceMessageLoop() when a device failure event is returned by LeapPollConnection(). */
162 static void handleDeviceFailureEvent(const LEAP_DEVICE_FAILURE_EVENT *device_failure_event){
163 if(ConnectionCallbacks.on_device_failure){
164 ConnectionCallbacks.on_device_failure(device_failure_event->status, device_failure_event->hDevice);
168 /** Called by serviceMessageLoop() when a tracking event is returned by LeapPollConnection(). */
169 static void handleTrackingEvent(const LEAP_TRACKING_EVENT *tracking_event){
170 setFrame(tracking_event); //support polling tracking data from different thread
171 if(ConnectionCallbacks.on_frame){
172 ConnectionCallbacks.on_frame(tracking_event);
176 /** Called by serviceMessageLoop() when a log event is returned by LeapPollConnection(). */
177 static void handleLogEvent(const LEAP_LOG_EVENT *log_event){
178 if(ConnectionCallbacks.on_log_message){
179 ConnectionCallbacks.on_log_message(log_event->severity, log_event->timestamp, log_event->message);
183 /** Called by serviceMessageLoop() when a log event is returned by LeapPollConnection(). */
184 static void handleLogEvents(const LEAP_LOG_EVENTS *log_events){
185 if(ConnectionCallbacks.on_log_message){
186 for (int i = 0; i < (int)(log_events->nEvents); i++) {
187 const LEAP_LOG_EVENT* log_event = &log_events->events[i];
188 ConnectionCallbacks.on_log_message(log_event->severity, log_event->timestamp, log_event->message);
193 /** Called by serviceMessageLoop() when a policy event is returned by LeapPollConnection(). */
194 static void handlePolicyEvent(const LEAP_POLICY_EVENT *policy_event){
195 if(ConnectionCallbacks.on_policy){
196 ConnectionCallbacks.on_policy(policy_event->current_policy);
200 /** Called by serviceMessageLoop() when a config change event is returned by LeapPollConnection(). */
201 static void handleConfigChangeEvent(const LEAP_CONFIG_CHANGE_EVENT *config_change_event){
202 if(ConnectionCallbacks.on_config_change){
203 ConnectionCallbacks.on_config_change(config_change_event->requestID, config_change_event->status);
207 /** Called by serviceMessageLoop() when a config response event is returned by LeapPollConnection(). */
208 static void handleConfigResponseEvent(const LEAP_CONFIG_RESPONSE_EVENT *config_response_event){
209 if(ConnectionCallbacks.on_config_response){
210 ConnectionCallbacks.on_config_response(config_response_event->requestID, config_response_event->value);
214 /** Called by serviceMessageLoop() when a point mapping change event is returned by LeapPollConnection(). */
215 static void handleImageEvent(const LEAP_IMAGE_EVENT *image_event) {
216 if(ConnectionCallbacks.on_image){
217 ConnectionCallbacks.on_image(image_event);
221 /** Called by serviceMessageLoop() when a point mapping change event is returned by LeapPollConnection(). */
222 static void handlePointMappingChangeEvent(const LEAP_POINT_MAPPING_CHANGE_EVENT *point_mapping_change_event) {
223 if(ConnectionCallbacks.on_point_mapping_change){
224 ConnectionCallbacks.on_point_mapping_change(point_mapping_change_event);
228 /** Called by serviceMessageLoop() when a point mapping change event is returned by LeapPollConnection(). */
229 static void handleHeadPoseEvent(const LEAP_HEAD_POSE_EVENT *head_pose_event) {
230 if(ConnectionCallbacks.on_head_pose){
231 ConnectionCallbacks.on_head_pose(head_pose_event);
236 * Services the LeapC message pump by calling LeapPollConnection().
237 * The average polling time is determined by the framerate of the Leap Motion service.
239 #if defined(_MSC_VER)
240 static void serviceMessageLoop(void * unused){
242 static void* serviceMessageLoop(void * unused){
245 LEAP_CONNECTION_MESSAGE msg;
247 unsigned int timeout = 1000;
248 result = LeapPollConnection(connectionHandle, timeout, &msg);
250 if(result != eLeapRS_Success){
251 printf("LeapC PollConnection call was %s.\n", ResultString(result));
256 case eLeapEventType_Connection:
257 handleConnectionEvent(msg.connection_event);
259 case eLeapEventType_ConnectionLost:
260 handleConnectionLostEvent(msg.connection_lost_event);
262 case eLeapEventType_Device:
263 handleDeviceEvent(msg.device_event);
265 case eLeapEventType_DeviceLost:
266 handleDeviceLostEvent(msg.device_event);
268 case eLeapEventType_DeviceFailure:
269 handleDeviceFailureEvent(msg.device_failure_event);
271 case eLeapEventType_Tracking:
272 handleTrackingEvent(msg.tracking_event);
274 case eLeapEventType_ImageComplete:
277 case eLeapEventType_ImageRequestError:
280 case eLeapEventType_LogEvent:
281 handleLogEvent(msg.log_event);
283 case eLeapEventType_Policy:
284 handlePolicyEvent(msg.policy_event);
286 case eLeapEventType_ConfigChange:
287 handleConfigChangeEvent(msg.config_change_event);
289 case eLeapEventType_ConfigResponse:
290 handleConfigResponseEvent(msg.config_response_event);
292 case eLeapEventType_Image:
293 handleImageEvent(msg.image_event);
295 case eLeapEventType_PointMappingChange:
296 handlePointMappingChangeEvent(msg.point_mapping_change_event);
298 case eLeapEventType_LogEvents:
299 handleLogEvents(msg.log_events);
301 case eLeapEventType_HeadPose:
302 handleHeadPoseEvent(msg.head_pose_event);
305 //discard unknown message types
306 printf("Unhandled message type %i.\n", msg.type);
307 } //switch on msg.type
309 #if !defined(_MSC_VER)
314 /* Used in Polling Example: */
317 * Caches the newest frame by copying the tracking event struct returned by
320 void setFrame(const LEAP_TRACKING_EVENT *frame){
321 LockMutex(&dataLock);
322 if(!lastFrame) lastFrame = malloc(sizeof(*frame));
324 UnlockMutex(&dataLock);
327 /** Returns a pointer to the cached tracking frame. */
328 LEAP_TRACKING_EVENT* GetFrame(){
329 LEAP_TRACKING_EVENT *currentFrame;
331 LockMutex(&dataLock);
332 currentFrame = lastFrame;
333 UnlockMutex(&dataLock);
339 * Caches the last device found by copying the device info struct returned by
342 static void setDevice(const LEAP_DEVICE_INFO *deviceProps){
343 LockMutex(&dataLock);
345 free(lastDevice->serial);
347 lastDevice = malloc(sizeof(*deviceProps));
349 *lastDevice = *deviceProps;
350 lastDevice->serial = malloc(deviceProps->serial_length);
351 memcpy(lastDevice->serial, deviceProps->serial, deviceProps->serial_length);
352 UnlockMutex(&dataLock);
355 /** Returns a pointer to the cached device info. */
356 LEAP_DEVICE_INFO* GetDeviceProperties(){
357 LEAP_DEVICE_INFO *currentDevice;
358 LockMutex(&dataLock);
359 currentDevice = lastDevice;
360 UnlockMutex(&dataLock);
361 return currentDevice;
364 //End of polling example-specific code
366 /** Translates eLeapRS result codes into a human-readable string. */
367 const char* ResultString(eLeapRS r) {
369 case eLeapRS_Success: return "eLeapRS_Success";
370 case eLeapRS_UnknownError: return "eLeapRS_UnknownError";
371 case eLeapRS_InvalidArgument: return "eLeapRS_InvalidArgument";
372 case eLeapRS_InsufficientResources: return "eLeapRS_InsufficientResources";
373 case eLeapRS_InsufficientBuffer: return "eLeapRS_InsufficientBuffer";
374 case eLeapRS_Timeout: return "eLeapRS_Timeout";
375 case eLeapRS_NotConnected: return "eLeapRS_NotConnected";
376 case eLeapRS_HandshakeIncomplete: return "eLeapRS_HandshakeIncomplete";
377 case eLeapRS_BufferSizeOverflow: return "eLeapRS_BufferSizeOverflow";
378 case eLeapRS_ProtocolError: return "eLeapRS_ProtocolError";
379 case eLeapRS_InvalidClientID: return "eLeapRS_InvalidClientID";
380 case eLeapRS_UnexpectedClosed: return "eLeapRS_UnexpectedClosed";
381 case eLeapRS_UnknownImageFrameRequest: return "eLeapRS_UnknownImageFrameRequest";
382 case eLeapRS_UnknownTrackingFrameID: return "eLeapRS_UnknownTrackingFrameID";
383 case eLeapRS_RoutineIsNotSeer: return "eLeapRS_RoutineIsNotSeer";
384 case eLeapRS_TimestampTooEarly: return "eLeapRS_TimestampTooEarly";
385 case eLeapRS_ConcurrentPoll: return "eLeapRS_ConcurrentPoll";
386 case eLeapRS_NotAvailable: return "eLeapRS_NotAvailable";
387 case eLeapRS_NotStreaming: return "eLeapRS_NotStreaming";
388 case eLeapRS_CannotOpenDevice: return "eLeapRS_CannotOpenDevice";
389 default: return "unknown result type.";
392 /** Cross-platform sleep function */
393 void millisleep(int milliseconds){
397 usleep(milliseconds*1000);
400 //End-of-ExampleConnection.c