Callback Example

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 off 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.

  1 #undef __cplusplus
  2
  3 #include <stdio.h>
  4 #include <stdlib.h>
  5 #include "LeapC.h"
  6 #include "ExampleConnection.h"
  7
  8 static LEAP_CONNECTION* connectionHandle;
  9
 10 /** Callback for when the connection opens. */
 11 static void OnConnect(){
 12   printf("Connected.\n");
 13 }
 14
 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);
 18 }
 19
 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);
 23
 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",
 27                 hand->id,
 28                 (hand->type == eLeapHandType_Left ? "left" : "right"),
 29                 hand->palm.position.x,
 30                 hand->palm.position.y,
 31                 hand->palm.position.z);
 32   }
 33 }
 34
 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);
 40 }
 41
 42 static void OnLogMessage(const eLeapLogSeverity severity, const int64_t timestamp,
 43                          const char* message) {
 44   const char* severity_str;
 45   switch(severity) {
 46     case eLeapLogSeverity_Critical:
 47       severity_str = "Critical";
 48       break;
 49     case eLeapLogSeverity_Warning:
 50       severity_str = "Warning";
 51       break;
 52     case eLeapLogSeverity_Information:
 53       severity_str = "Info";
 54       break;
 55     default:
 56       severity_str = "";
 57       break;
 58   }
 59   printf("[%s][%lli] %s\n", severity_str, (long long int)timestamp, message);
 60 }
 61
 62 static void* allocate(uint32_t size, eLeapAllocatorType typeHint, void* state) {
 63   void* ptr = malloc(size);
 64   return ptr;
 65 }
 66
 67 static void deallocate(void* ptr, void* state) {
 68   if (!ptr)
 69     return;
 70   free(ptr);
 71 }
 72
 73 void OnPointMappingChange(const LEAP_POINT_MAPPING_CHANGE_EVENT *change){
 74   if (!connectionHandle)
 75     return;
 76
 77   uint64_t size = 0;
 78   if (LeapGetPointMappingSize(*connectionHandle, &size) != eLeapRS_Success || !size)
 79     return;
 80
 81   LEAP_POINT_MAPPING* pointMapping = (LEAP_POINT_MAPPING*)malloc(size);
 82   if (!pointMapping)
 83     return;
 84
 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);
 88   }
 89   free(pointMapping);
 90 }
 91
 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);
103  }
104
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;
114
115   connectionHandle = OpenConnection();
116   {
117     LEAP_ALLOCATOR allocator = { allocate, deallocate, NULL };
118     LeapSetAllocator(*connectionHandle, &allocator);
119   }
120   LeapSetPolicyFlags(*connectionHandle, eLeapPolicyFlag_Images | eLeapPolicyFlag_MapPoints, 0);
121
122   printf("Press Enter to exit program.\n");
123   getchar();
124
125   DestroyConnection();
126
127   return 0;
128 }
129 //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.

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 production-ready code.