Using LeapC¶
To get tracking and status information, the typical tasks a client application performs include:
-
Setting up a message pump to handle tracking and status events.
-
Requesting images.
Getting and setting policy settings.
Getting and setting configuration settings.
Synchronizing app and Ultraleap Hand Tracking System clocks and getting interpolated frames.
Getting Tracking Frames and Other Event Messages¶
You can use a message pump to efficiently monitor the LeapC message queue. The message queue provides tracking data and status information from the Ultraleap Tracking Service.
A typical message pump is a function that runs in its own thread and calls
LeapPollConnection()
in a tight loop:
void serviceMessageLoop(){
eLeapRS result;
LEAP_CONNECTION_MESSAGE msg;
while(_isRunning){
unsigned int timeout = 1000;
result = LeapPollConnection(connectionHandle, timeout, &msg);
//Handle message
}
}
The types of messages that can be returned by LeapPollConnection()
are defined in the
eLeapEventType
enumeration. Your application should handle each event type appropriately and
synchronize any data that needs to be used by other threads. LeapPollConnection()
blocks
until a message is available or until the specified timeout period has elapsed.
To start the message pump, you must first open a connection and start the message pump thread:
LEAP_CONNECTION* OpenConnection(){
eLeapRS result = LeapCreateConnection(NULL, &connectionHandle);
if(result == eLeapRS_Success){
result = LeapOpenConnection(connectionHandle);
if(result == eLeapRS_Success){
_isRunning = true;
pthread_create(&pollingThread, NULL, serviceMessageLoop, NULL);
}
}
return &connectionHandle;
}
The LEAP_CONNECTION
struct returned by
LeapCreateConnection()
becomes your handle for referencing this connection in
future LeapC API calls. (You can maintain more than one connection, but typically there is no reason to do so.) Pass a
NULL value for the LEAP_CONNECTION_CONFIG
struct. There aren’t currently any
config options to set.
LeapOpenConnection()
performs client-side verification and starts any operations needed
to connect to the service. However, any operations that may block are deferred until the first call to
LeapOpenConnection()
.
Note
You do not need to open a device to get tracking data. The device-related functions are only needed to get device properties or to change device state. See Reading Device Properties.
See the Callback Example.
Interpolating Frames¶
Instead of taking frames at whatever rate the Ultraleap camera device happens to be producing them, you can request each frame at a particular time. LeapC will then interpolate the nearest measured frames to synthesize a frame at the desired time. In general, requesting frames further in the past will provide improve the perception of smoothness in the tracking data, but at a cost of increased latency. Conversely, requesting frames with a shorter delay will improve perceived responsiveness, but at a cost of increased jitteryness. You should tune the value as best suits your application; if you use the same code on desktop and mobile platforms (when mobile is supported), you will probably need different delay values for each.
A timestamp that is more recent than the timestamp most recently returned by
LeapPollConnection()
(e.g. for a tracking or image complete event) is considered to be
in the future. If an interpolated frame is requested for a timestamp in the future, LeapC will use extrapolation instead
of interpolation. If the same call was delayed so that the same timestamp value is no longer in the future, LeapC will
choose to interpolate instead of extrapolate.
The LeapGetNow()
function returns the current value of the clock used to generate frame
timestamps. On Windows, this value is equal to QueryPerformanceCounter divided by QueryPerformanceFrequency, represented
in microseconds. On all other platforms, it is the time since epoch of std::chrono::high_resolution_clock::now(), in
microseconds. If your application time is derived from the same source as LeapGetNow()
, you can
calculate the desired frame timestamps directly and do not need to use the
LEAP_CLOCK_REBASER
object.
To get interpolated frames:
Open the connection.
Call
LeapPollConnection()
at least once to complete opening the connection.Create a
LEAP_CLOCK_REBASER
struct.Pass this struct to
LeapCreateClockRebaser()
, which initializes the struct.Every time you render a frame of graphics, call
LeapUpdateRebase()
to synchronize the application and Ultraleap hand tracking system clocks. The clocks will drift apart rapidly, otherwise.Translate the target timestamp of the interpolated frame by passing the application time to
LeapRebaseClock()
. LeapC writes the corresponding Leap clock time to the pLeapClock parameter.Call
LeapGetFrameSize()
to calculate the size of the buffer needed to require the tracking data at the desired point in time. (Frame size varies with the number of hands in view.)Allocate a buffer of the required size.
Call
LeapInterpolateFrame()
, passing in the frame time and your buffer. LeapC writes the frame data to your buffer. This buffer can be treated as aLEAP_TRACKING_EVENT
object to access the tracking data.Continue calling
LeapUpdateRebase()
andLeapInterpolateFrame()
at your application frame rate.
Note that if you have an alternative means of converting your local timestamp units into the same clock domain as that
used by LeapGetNow()
, you do not need to use the clock rebasing functions and can call
LeapInterpolateFrame()
using the converted timestamp directly.
When getting interpolated frames, you can safely ignore tracking messages from
LeapPollConnection()
– but you should still service the message loop to get status and
image completion messages.
See the Interpolated Frames Example.
Reading Device Properties¶
To read the properties of a device, you must get the list of known devices from LeapC using
LeapGetDeviceList()
. You can then get the specific properties of the devices in the
returned list using LeapOpenDevice()
and LeapGetDeviceInfo()
.
You must first open it with LeapOpenDevice()
. For that you need a
LEAP_DEVICE_REF
struct, which you can get from
LeapGetDeviceList()
. However, you must pass an array to
LeapGetDeviceList()
to receive the device reference structs. So first, you must get the
number of attached devices. You get the device count by calling LeapGetDeviceList()
passing in a NULL pointer in place of the device array.
To get device information:
Open the connection.
Call
LeapPollConnection()
at least once to complete opening the connection.Get the device count by calling
LeapGetDeviceList()
, passing a NULL pointer instead of an array of device reference structs. LeapC writes the device count to the pnArray parameter.Create an array of
LEAP_DEVICE_REF
structs large enough to hold the number of attached devices.Call
LeapGetDeviceList()
again, passing in the correct size array and setting pnArray to the number of elements in your array. LeapC fills in the device reference struct for each attached device and updates pnArray to the number of records it actually wrote.Because
LeapGetDeviceList()
does not block, the returned list contains references for any devices known to LeapC at the time of the call. This local list can be different than the list of devices know to the Leap Motion service.
Once you have a LEAP_DEVICE_REF
struct for a device, you can read its properties:
Create a
LEAP_DEVICE
struct, which will become your device handle.Call
LeapOpenDevice()
, passing in theLEAP_DEVICE_REF
andLEAP_DEVICE
structs.Create a
LEAP_DEVICE_INFO
struct.Allocate a buffer large enough to hold the serial number string.
Call
LeapGetDeviceInfo()
to fill in the device info struct.If the serial number buffer is too small, then
LeapGetDeviceInfo()
returns a value ofeLeapRS_InsufficientBuffer
. It also sets theLEAP_DEVICE_INFO::serial_length
field to the required buffer length. You can use this value to allocate a properly sized buffer and call the function again.Call
LeapCloseDevice()
when done.
LEAP_DEVICE_INFO deviceProperties;
deviceProperties.serial_length = 1;
deviceProperties.serial = malloc(deviceProperties.serial_length);
result = LeapGetDeviceInfo(deviceHandle, &deviceProperties);
if(result == eLeapRS_InsufficientBuffer){
free(deviceProperties.serial);
deviceProperties.serial = malloc(deviceProperties.serial_length);
result = LeapGetDeviceInfo(deviceHandle, &deviceProperties);
}
Controlling Policies¶
Policies control whether certain features should be used by an application. Generally, these features affect general usability or change service behavior that will affect all applications on a system.
See Policy for a description of the current policies.
Reading and writing policies¶
To get and set policies:
Open the connection.
Call
LeapPollConnection()
at least once.Combine the flags that you want to set by bitwise ANDing elements of the
eLeapPolicyFlag
enumeration.Likewise, combine the flags that you want to clear by bitwise ANDing elements of the
eLeapPolicyFlag
enumeration.Call
LeapSetPolicyFlags()
, passing in your set and clear policy combinations.When the policy change is processed by the service, LeapC adds a
LEAP_POLICY_EVENT
message to the queue. You can compareLEAP_POLICY_EVENT::current_policy
with the desired policy to verify that the policy change was “approved.”
To read the current policies without making changes, set both the set and the clear parameters to 0 when calling
LeapSetPolicyFlags()
. This will generate a
LEAP_POLICY_EVENT
message containing the current policies.