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)[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.
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
19 const char* ResultString(eLeapRS r);
21 void serviceMessageLoop(void * unused);
23 void* serviceMessageLoop(void * unused);
25 void setFrame(const LEAP_TRACKING_EVENT *frame);
26 void setDevice(const LEAP_DEVICE_INFO *deviceProps);
29 bool IsConnected = false;
32 bool _isRunning = false;
33 LEAP_CONNECTION connectionHandle;
34 LEAP_TRACKING_EVENT *lastFrame = NULL;
35 LEAP_DEVICE_INFO *lastDevice = NULL;
37 //Callback function pointers
38 struct Callbacks ConnectionCallbacks;
42 uintptr_t pollingThread;
43 CRITICAL_SECTION dataLock;
45 pthread_t pollingThread;
46 pthread_mutex_t dataLock;
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.
53 LEAP_CONNECTION* OpenConnection(){
54 eLeapRS result = LeapCreateConnection(NULL, &connectionHandle);
55 if(result == eLeapRS_Success){
56 result = LeapOpenConnection(connectionHandle);
57 if(result == eLeapRS_Success){
60 InitializeCriticalSection(&dataLock);
61 pollingThread = _beginthread(serviceMessageLoop, 0, NULL);
63 pthread_create(&pollingThread, NULL, serviceMessageLoop, NULL);
67 return &connectionHandle;
70 void CloseConnection(){
71 CloseConnectionHandle(&connectionHandle);
74 /** Close the connection and let message thread function end. */
75 void CloseConnectionHandle(LEAP_CONNECTION* connectionHandle){
76 LeapDestroyConnection(*connectionHandle);
80 /** Called by serviceMessageLoop() when a connection event is returned by LeapPollConnection(). */
81 void handleConnectionEvent(const LEAP_CONNECTION_EVENT *connection_event){
83 if(ConnectionCallbacks.on_connection){
84 ConnectionCallbacks.on_connection();
88 /** Called by serviceMessageLoop() when a connection lost event is returned by LeapPollConnection(). */
89 void handleConnectionLostEvent(const LEAP_CONNECTION_LOST_EVENT *connection_lost_event){
91 if(ConnectionCallbacks.on_connection_lost){
92 ConnectionCallbacks.on_connection_lost();
97 * Called by serviceMessageLoop() when a device event is returned by LeapPollConnection()
98 * Demonstrates how to access device properties.
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));
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);
128 setDevice(&deviceProperties);
129 if(ConnectionCallbacks.on_device_found){
130 ConnectionCallbacks.on_device_found(&deviceProperties);
133 free(deviceProperties.serial);
134 LeapCloseDevice(deviceHandle);
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();
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);
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);
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);
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);
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);
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);
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);
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);
202 * Services the LeapC message pump by calling LeapPollConnection().
203 * The average polling time is determined by the framerate of the Leap Motion service.
205 #if defined(_MSC_VER)
206 void serviceMessageLoop(void * unused){
208 void* serviceMessageLoop(void * unused){
211 LEAP_CONNECTION_MESSAGE msg;
213 unsigned int timeout = 1000;
214 result = LeapPollConnection(connectionHandle, timeout, &msg);
216 if (result != eLeapRS_Success) {
217 printf("LeapC PollConnection call was %s.\n", ResultString(result));
222 case eLeapEventType_Connection:
223 handleConnectionEvent(msg.connection_event);
225 case eLeapEventType_ConnectionLost:
226 handleConnectionLostEvent(msg.connection_lost_event);
228 case eLeapEventType_Device:
229 handleDeviceEvent(msg.device_event);
231 case eLeapEventType_DeviceLost:
232 handleDeviceLostEvent(msg.device_event);
234 case eLeapEventType_DeviceFailure:
235 handleDeviceFailureEvent(msg.device_failure_event);
237 case eLeapEventType_Tracking:
238 handleTrackingEvent(msg.tracking_event);
240 case eLeapEventType_ImageComplete:
241 handleImageCompleteEvent(msg.image_complete_event);
243 case eLeapEventType_ImageRequestError:
244 handleImageRequestErrorEvent(msg.image_request_error_event);
246 case eLeapEventType_LogEvent:
247 handleLogEvent(msg.log_event);
249 case eLeapEventType_Policy:
250 handlePolicyEvent(msg.policy_event);
252 case eLeapEventType_ConfigChange:
253 handleConfigChangeEvent(msg.config_change_event);
255 case eLeapEventType_ConfigResponse:
256 handleConfigResponseEvent(msg.config_response_event);
259 //discard unknown message types
260 printf("Unhandled message type %i.\n", msg.type);
261 } //switch on msg.type
263 #if !defined(_MSC_VER)
268 /* Used in Polling Example: */
271 * Caches the newest frame by copying the tracking event struct returned by
274 void setFrame(const LEAP_TRACKING_EVENT *frame){
275 LockMutex(&dataLock);
276 if(!lastFrame) lastFrame = malloc(sizeof(*frame));
278 UnlockMutex(&dataLock);
281 /** Returns a pointer to the cached tracking frame. */
282 LEAP_TRACKING_EVENT* GetFrame(){
283 LEAP_TRACKING_EVENT *currentFrame;
285 LockMutex(&dataLock);
286 currentFrame = lastFrame;
287 UnlockMutex(&dataLock);
293 * Caches the last device found by copying the device info struct returned by
296 void setDevice(const LEAP_DEVICE_INFO *deviceProps){
297 LockMutex(&dataLock);
299 free(lastDevice->serial);
301 lastDevice = malloc(sizeof(*deviceProps));
303 *lastDevice = *deviceProps;
304 lastDevice->serial = malloc(deviceProps->serial_length);
305 memcpy(lastDevice->serial, deviceProps->serial, deviceProps->serial_length);
306 UnlockMutex(&dataLock);
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;
318 //End of polling example-specific code
320 /** Translates eLeapRS result codes into a human-readable string. */
321 const char* ResultString(eLeapRS 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.";
346 /** Cross-platform sleep function */
347 void millisleep(int milliseconds){
351 usleep(milliseconds*1000);
354 //End-of-ExampleConnection.c