-
-
Save ArnaudGallardo/b67dadfa481c2a9077e6a0b363ec2b7b to your computer and use it in GitHub Desktop.
Simple reproducer - LeapPollConnection deadlock
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "LeapInterface.h" | |
#include <iostream> | |
#include <mutex> | |
#include <chrono> | |
#include <string> | |
static std::mutex g_dataLock; | |
static LEAP_TRACKING_EVENT* g_lastFrame = NULL; | |
static LEAP_DEVICE_INFO* g_lastDevice = NULL; | |
static const char* | |
ResultString( eLeapRS r ) { | |
switch (r){ | |
case eLeapRS_Success: return "eLeapRS_Success"; | |
case eLeapRS_UnknownError: return "eLeapRS_UnknownError"; | |
case eLeapRS_InvalidArgument: return "eLeapRS_InvalidArgument"; | |
case eLeapRS_InsufficientResources: return "eLeapRS_InsufficientResources"; | |
case eLeapRS_InsufficientBuffer: return "eLeapRS_InsufficientBuffer"; | |
case eLeapRS_Timeout: return "eLeapRS_Timeout"; | |
case eLeapRS_NotConnected: return "eLeapRS_NotConnected"; | |
case eLeapRS_HandshakeIncomplete: return "eLeapRS_HandshakeIncomplete"; | |
case eLeapRS_BufferSizeOverflow: return "eLeapRS_BufferSizeOverflow"; | |
case eLeapRS_ProtocolError: return "eLeapRS_ProtocolError"; | |
case eLeapRS_InvalidClientID: return "eLeapRS_InvalidClientID"; | |
case eLeapRS_UnexpectedClosed: return "eLeapRS_UnexpectedClosed"; | |
case eLeapRS_UnknownImageFrameRequest: return "eLeapRS_UnknownImageFrameRequest"; | |
case eLeapRS_UnknownTrackingFrameID: return "eLeapRS_UnknownTrackingFrameID"; | |
case eLeapRS_RoutineIsNotSeer: return "eLeapRS_RoutineIsNotSeer"; | |
case eLeapRS_TimestampTooEarly: return "eLeapRS_TimestampTooEarly"; | |
case eLeapRS_ConcurrentPoll: return "eLeapRS_ConcurrentPoll"; | |
case eLeapRS_NotAvailable: return "eLeapRS_NotAvailable"; | |
case eLeapRS_NotStreaming: return "eLeapRS_NotStreaming"; | |
case eLeapRS_CannotOpenDevice: return "eLeapRS_CannotOpenDevice"; | |
default: return "unknown result type."; | |
} | |
} | |
/** | |
* Set the cached tracking frame | |
*/ | |
static void | |
setFrame( const LEAP_TRACKING_EVENT *frame ){ | |
g_dataLock.lock(); | |
if (!g_lastFrame) g_lastFrame = (LEAP_TRACKING_EVENT*)malloc( sizeof( *frame ) ); | |
*g_lastFrame = *frame; | |
g_dataLock.unlock(); | |
} | |
/** | |
* Caches the last device found by copying the device info struct returned by LeapC | |
*/ | |
static void | |
setDevice( const LEAP_DEVICE_INFO *deviceProps ){ | |
std::cout << "setDevice" << std::endl; | |
g_dataLock.lock(); | |
if (g_lastDevice){ | |
free( g_lastDevice->serial ); | |
} | |
else { | |
g_lastDevice = (LEAP_DEVICE_INFO*)malloc( sizeof( *deviceProps ) ); | |
} | |
*g_lastDevice = *deviceProps; | |
g_lastDevice->serial = (char*)malloc( deviceProps->serial_length ); | |
memcpy( g_lastDevice->serial, deviceProps->serial, deviceProps->serial_length ); | |
g_dataLock.unlock(); | |
} | |
/** | |
* Called by serviceMessageLoop() when a device lost event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleDeviceLostEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_DEVICE_EVENT *device_event ) | |
{ | |
if (connectionCallbacks.on_device_lost){ | |
connectionCallbacks.on_device_lost(); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a device failure event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleDeviceFailureEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_DEVICE_FAILURE_EVENT *device_failure_event ) | |
{ | |
if (connectionCallbacks.on_device_failure){ | |
connectionCallbacks.on_device_failure( device_failure_event->status, device_failure_event->hDevice ); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a tracking event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleTrackingEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_TRACKING_EVENT *tracking_event ) | |
{ | |
setFrame( tracking_event ); //support polling tracking data from different thread | |
if (connectionCallbacks.on_frame){ | |
connectionCallbacks.on_frame( tracking_event ); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a log event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleLogEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_LOG_EVENT *log_event ) | |
{ | |
if (connectionCallbacks.on_log_message){ | |
connectionCallbacks.on_log_message( log_event->severity, log_event->timestamp, log_event->message ); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a log event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleLogEvents( LeapInterface::Callbacks connectionCallbacks, const LEAP_LOG_EVENTS *log_events ) | |
{ | |
if (connectionCallbacks.on_log_message){ | |
for (int i = 0; i < (int)(log_events->nEvents); i++) { | |
const LEAP_LOG_EVENT* log_event = &log_events->events[i]; | |
connectionCallbacks.on_log_message( log_event->severity, log_event->timestamp, log_event->message ); | |
} | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a policy event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handlePolicyEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_POLICY_EVENT *policy_event ) | |
{ | |
if (connectionCallbacks.on_policy){ | |
connectionCallbacks.on_policy( policy_event->current_policy ); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a config change event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleConfigChangeEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_CONFIG_CHANGE_EVENT *config_change_event ) | |
{ | |
if (connectionCallbacks.on_config_change){ | |
connectionCallbacks.on_config_change( config_change_event->requestID, config_change_event->status ); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a config response event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleConfigResponseEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_CONFIG_RESPONSE_EVENT *config_response_event ) | |
{ | |
if (connectionCallbacks.on_config_response){ | |
connectionCallbacks.on_config_response( config_response_event->requestID, config_response_event->value ); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a point mapping change event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleImageEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_IMAGE_EVENT *image_event ) | |
{ | |
if (connectionCallbacks.on_image){ | |
connectionCallbacks.on_image( image_event ); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a point mapping change event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handlePointMappingChangeEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_POINT_MAPPING_CHANGE_EVENT *point_mapping_change_event ) | |
{ | |
if (connectionCallbacks.on_point_mapping_change){ | |
connectionCallbacks.on_point_mapping_change( point_mapping_change_event ); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a point mapping change event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleHeadPoseEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_HEAD_POSE_EVENT *head_pose_event ) | |
{ | |
if (connectionCallbacks.on_head_pose){ | |
connectionCallbacks.on_head_pose( head_pose_event ); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a connection event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleConnectionEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_CONNECTION_EVENT *connection_event ){ | |
if (connectionCallbacks.on_connection){ | |
connectionCallbacks.on_connection(); | |
} | |
} | |
/** | |
* Called by serviceMessageLoop() when a connection lost event is returned by LeapPollConnection(). | |
*/ | |
static void | |
handleConnectionLostEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_CONNECTION_LOST_EVENT *connection_lost_event ){ | |
if (connectionCallbacks.on_connection_lost){ | |
connectionCallbacks.on_connection_lost(); | |
} | |
} | |
static void | |
handleDeviceEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_DEVICE_EVENT *device_event ){ | |
LEAP_DEVICE deviceHandle; | |
std::cout << "device" << std::endl; | |
//Open device using LEAP_DEVICE_REF from event struct. | |
eLeapRS result = LeapOpenDevice( device_event->device, &deviceHandle ); | |
if (result != eLeapRS_Success){ | |
const std::string message = "Could not open device " + std::string( ResultString( result ) ) + "."; | |
//SoDebugError::post( "handleDeviceEvent", message.c_str() ); | |
return; | |
} | |
//Create a struct to hold the device properties, we have to provide a buffer for the serial string | |
LEAP_DEVICE_INFO deviceProperties = { sizeof( deviceProperties ) }; | |
// Start with a length of 1 (pretending we don't know a priori what the length is). | |
// Currently device serial numbers are all the same length, but that could change in the future | |
deviceProperties.serial_length = 1; | |
deviceProperties.serial = (char*)malloc( deviceProperties.serial_length ); | |
//This will fail since the serial buffer is only 1 character long | |
// But deviceProperties is updated to contain the required buffer length | |
result = LeapGetDeviceInfo( deviceHandle, &deviceProperties ); | |
if (result == eLeapRS_InsufficientBuffer){ | |
//try again with correct buffer size | |
deviceProperties.serial = (char*)realloc( deviceProperties.serial, deviceProperties.serial_length ); | |
result = LeapGetDeviceInfo( deviceHandle, &deviceProperties ); | |
if (result != eLeapRS_Success){ | |
const std::string message = "Failed to get device info " + std::string( ResultString( result ) ) + "."; | |
//SoDebugError::post( "handleDeviceEvent", message.c_str() ); | |
free( deviceProperties.serial ); | |
return; | |
} | |
} | |
setDevice( &deviceProperties ); | |
if (connectionCallbacks.on_device_found){ | |
connectionCallbacks.on_device_found( &deviceProperties ); | |
} | |
free( deviceProperties.serial ); | |
LeapCloseDevice( deviceHandle ); | |
} | |
void* | |
serviceMessageLoop( void* userData ) | |
{ | |
LeapInterface::t_userData *data = (LeapInterface::t_userData*)userData; | |
eLeapRS result; | |
LEAP_CONNECTION_MESSAGE msg; | |
std::cout << "Start thread : " << data->connectionHandle << std::endl; | |
while (data->isRunning){ | |
unsigned int timeout = 1000; | |
std::cout << "loop : " << data->isConnected << std::endl; | |
result = LeapPollConnection( data->connectionHandle, timeout, &msg ); | |
std::cout << "msg : " << msg.type << " " << result << std::endl; | |
if (result != eLeapRS_Success){ | |
const std::string message = "LeapC PollConnection call was " + std::string( ResultString( result ) ) + "."; | |
//SoDebugError::post( "serviceMessageLoop", message.c_str() ); | |
continue; | |
} | |
switch (msg.type){ | |
case eLeapEventType_Connection: | |
data->isConnected = true; | |
handleConnectionEvent( data->connectionCallbacks, msg.connection_event ); | |
break; | |
case eLeapEventType_ConnectionLost: | |
data->isConnected = false; | |
handleConnectionLostEvent( data->connectionCallbacks, msg.connection_lost_event ); | |
break; | |
case eLeapEventType_Device: | |
handleDeviceEvent( data->connectionCallbacks, msg.device_event ); | |
break; | |
case eLeapEventType_DeviceLost: | |
handleDeviceLostEvent( data->connectionCallbacks, msg.device_event ); | |
break; | |
case eLeapEventType_DeviceFailure: | |
handleDeviceFailureEvent( data->connectionCallbacks, msg.device_failure_event ); | |
break; | |
case eLeapEventType_Tracking: | |
handleTrackingEvent( data->connectionCallbacks, msg.tracking_event ); | |
break; | |
case eLeapEventType_ImageComplete: | |
// Ignore | |
break; | |
case eLeapEventType_ImageRequestError: | |
// Ignore | |
break; | |
case eLeapEventType_LogEvent: | |
handleLogEvent( data->connectionCallbacks, msg.log_event ); | |
break; | |
case eLeapEventType_Policy: | |
handlePolicyEvent( data->connectionCallbacks, msg.policy_event ); | |
break; | |
case eLeapEventType_ConfigChange: | |
handleConfigChangeEvent( data->connectionCallbacks, msg.config_change_event ); | |
break; | |
case eLeapEventType_ConfigResponse: | |
handleConfigResponseEvent( data->connectionCallbacks, msg.config_response_event ); | |
break; | |
case eLeapEventType_Image: | |
handleImageEvent( data->connectionCallbacks, msg.image_event ); | |
break; | |
case eLeapEventType_PointMappingChange: | |
handlePointMappingChangeEvent( data->connectionCallbacks, msg.point_mapping_change_event ); | |
break; | |
case eLeapEventType_LogEvents: | |
handleLogEvents( data->connectionCallbacks, msg.log_events ); | |
break; | |
case eLeapEventType_HeadPose: | |
handleHeadPoseEvent( data->connectionCallbacks, msg.head_pose_event ); | |
break; | |
default: | |
//discard unknown message types | |
const std::string message = "Unhandled message type " + std::to_string( msg.type ) + "."; | |
//SoDebugError::postInfo( "serviceMessageLoop", message.c_str() ); | |
break; | |
} | |
} | |
return NULL; | |
} | |
LeapInterface::LeapInterface() | |
{ | |
m_threadData.isRunning = false; | |
m_threadData.isConnected = false; | |
m_threadData.connectionHandle = NULL; | |
m_threadData.connectionCallbacks.on_connection = NULL; | |
m_threadData.connectionCallbacks.on_connection_lost = NULL; | |
m_threadData.connectionCallbacks.on_device_found = NULL; | |
m_threadData.connectionCallbacks.on_device_lost = NULL; | |
m_threadData.connectionCallbacks.on_device_failure = NULL; | |
m_threadData.connectionCallbacks.on_policy = NULL; | |
m_threadData.connectionCallbacks.on_frame = NULL; | |
m_threadData.connectionCallbacks.on_log_message = NULL; | |
m_threadData.connectionCallbacks.on_config_change = NULL; | |
m_threadData.connectionCallbacks.on_config_response = NULL; | |
m_threadData.connectionCallbacks.on_image = NULL; | |
m_threadData.connectionCallbacks.on_point_mapping_change = NULL; | |
m_threadData.connectionCallbacks.on_head_pose = NULL; | |
} | |
LeapInterface::~LeapInterface() | |
{ | |
this->DestroyConnection(); | |
} | |
LEAP_CONNECTION* | |
LeapInterface::OpenConnection(){ | |
if (m_threadData.isRunning){ | |
return &m_threadData.connectionHandle; | |
} | |
std::cout << "test : " << m_threadData.connectionHandle << std::endl; | |
if (m_threadData.connectionHandle || LeapCreateConnection( NULL, &m_threadData.connectionHandle ) == eLeapRS_Success){ | |
eLeapRS result = LeapOpenConnection( m_threadData.connectionHandle ); | |
if (result == eLeapRS_Success){ | |
m_threadData.isRunning = true; | |
std::cout << "Connection OK : " << m_threadData.connectionHandle << std::endl; | |
m_pollingThread = std::thread( serviceMessageLoop, (void*)&m_threadData ); | |
} | |
else | |
{ | |
std::cout << "error open : " << result << std::endl; | |
} | |
} | |
return &m_threadData.connectionHandle; | |
} | |
void | |
LeapInterface::CloseConnection(){ | |
if (!m_threadData.isRunning) | |
return; | |
g_dataLock.lock(); | |
m_threadData.isRunning = false; | |
g_dataLock.unlock(); | |
m_pollingThread.join(); | |
LeapCloseConnection( m_threadData.connectionHandle ); | |
} | |
void | |
LeapInterface::DestroyConnection(){ | |
CloseConnection(); | |
LeapDestroyConnection( m_threadData.connectionHandle ); | |
} | |
bool | |
LeapInterface::IsConnected() | |
{ | |
return m_threadData.isConnected; | |
} | |
LEAP_TRACKING_EVENT* | |
LeapInterface::GetFrame(){ | |
LEAP_TRACKING_EVENT *currentFrame; | |
g_dataLock.lock(); | |
currentFrame = g_lastFrame; | |
g_dataLock.unlock(); | |
return currentFrame; | |
} | |
LEAP_DEVICE_INFO* | |
LeapInterface::GetDeviceProperties(){ | |
std::cout << "test" << std::endl; | |
LEAP_DEVICE_INFO *currentDevice; | |
g_dataLock.lock(); | |
currentDevice = g_lastDevice; | |
g_dataLock.unlock(); | |
return currentDevice; | |
} | |
void | |
LeapInterface::millisleep( int milliseconds ){ | |
std::chrono::milliseconds dura( milliseconds ); | |
std::this_thread::sleep_for( dura ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef LeapInterface_h | |
#define LeapInterface_h | |
#include <thread> | |
#include <LeapC.h> | |
/** | |
* @class LeapInterface | |
* @brief LeapMotion SDK interface | |
* | |
* This class is mostly inspired by the example code found on LeapMotion's website. | |
* https://developer.leapmotion.com/documentation/v4/examples.html | |
* | |
* Transform unit is millimeters. | |
*/ | |
class LeapInterface | |
{ | |
public: | |
/** | |
* Default Constructor | |
*/ | |
LeapInterface(); | |
/** | |
* Destructor | |
*/ | |
~LeapInterface(); | |
/** | |
* Initialize the connection with the LeapMotion and create the message loop thread | |
*/ | |
LEAP_CONNECTION* OpenConnection(); | |
/** | |
* Release the connection with the LeapMotion and stop the message loop thread | |
*/ | |
void CloseConnection(); | |
/** | |
* Close and destroy the connection | |
*/ | |
void DestroyConnection(); | |
/** | |
* Returns a pointer to the cached tracking frame | |
*/ | |
LEAP_TRACKING_EVENT* GetFrame(); | |
/** | |
* Returns a pointer to the cached device info | |
*/ | |
LEAP_DEVICE_INFO* GetDeviceProperties(); | |
/** | |
* Translates eLeapRS result codes into a human-readable string | |
*/ | |
const char* ResultString( eLeapRS r ); | |
/** | |
* Returns the current connection status | |
*/ | |
bool IsConnected(); | |
typedef void( *connection_callback ) (); | |
typedef void( *device_callback ) (const LEAP_DEVICE_INFO *device); | |
typedef void( *device_lost_callback ) (); | |
typedef void( *device_failure_callback ) (const eLeapDeviceStatus failure_code, | |
const LEAP_DEVICE failed_device); | |
typedef void( *policy_callback ) (const uint32_t current_policies); | |
typedef void( *tracking_callback ) (const LEAP_TRACKING_EVENT *tracking_event); | |
typedef void( *log_callback ) (const eLeapLogSeverity severity, | |
const int64_t timestamp, | |
const char* message); | |
typedef void( *config_change_callback ) (const uint32_t requestID, const bool success); | |
typedef void( *config_response_callback )(const uint32_t requestID, LEAP_VARIANT value); | |
typedef void( *image_callback ) (const LEAP_IMAGE_EVENT *image_event); | |
typedef void( *point_mapping_change_callback )(const LEAP_POINT_MAPPING_CHANGE_EVENT *point_mapping_change_event); | |
typedef void( *head_pose_callback )(const LEAP_HEAD_POSE_EVENT *head_pose_event); | |
struct Callbacks{ | |
connection_callback on_connection; | |
connection_callback on_connection_lost; | |
device_callback on_device_found; | |
device_lost_callback on_device_lost; | |
device_failure_callback on_device_failure; | |
policy_callback on_policy; | |
tracking_callback on_frame; | |
log_callback on_log_message; | |
config_change_callback on_config_change; | |
config_response_callback on_config_response; | |
image_callback on_image; | |
point_mapping_change_callback on_point_mapping_change; | |
head_pose_callback on_head_pose; | |
}; | |
typedef struct s_userData | |
{ | |
bool isRunning; | |
bool isConnected; | |
LEAP_CONNECTION connectionHandle; | |
struct Callbacks connectionCallbacks; | |
} t_userData; | |
/** | |
* Sleep function | |
*/ | |
static void millisleep( int milliseconds ); | |
private: | |
std::thread m_pollingThread; | |
t_userData m_threadData; | |
}; | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <windows.h> | |
#include <iostream> | |
#include "LeapInterface.h" | |
#define CreateWindowA(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)\ | |
CreateWindowExA(0L, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam) | |
#define CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)\ | |
CreateWindowExW(0L, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam) | |
#ifdef UNICODE | |
#define CreateWindow CreateWindowW | |
#else | |
#define CreateWindow CreateWindowA | |
#endif | |
const char g_szClassName[] = "myWindowClass"; | |
// Step 4: the Window Procedure | |
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) | |
{ | |
switch (msg) | |
{ | |
case WM_CLOSE: | |
DestroyWindow( hwnd ); | |
break; | |
case WM_DESTROY: | |
PostQuitMessage( 0 ); | |
break; | |
default: | |
return DefWindowProc( hwnd, msg, wParam, lParam ); | |
} | |
return 0; | |
} | |
int | |
main( int argc, char** argv ) | |
{ | |
WNDCLASSEX wc; | |
HWND hwnd; | |
MSG Msg; | |
//Step 1: Registering the Window Class | |
wc.cbSize = sizeof( WNDCLASSEX ); | |
wc.style = 0; | |
wc.lpfnWndProc = WndProc; | |
wc.cbClsExtra = 0; | |
wc.cbWndExtra = 0; | |
wc.hInstance = GetModuleHandle( NULL ); | |
wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); | |
wc.hCursor = LoadCursor( NULL, IDC_ARROW ); | |
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); | |
wc.lpszMenuName = NULL; | |
wc.lpszClassName = g_szClassName; | |
wc.hIconSm = LoadIcon( NULL, IDI_APPLICATION ); | |
if (!RegisterClassEx( &wc )) | |
{ | |
MessageBox( NULL, "Window Registration Failed!", "Error!", | |
MB_ICONEXCLAMATION | MB_OK ); | |
return 0; | |
} | |
// Step 2: Creating the Window | |
hwnd = CreateWindowEx( | |
WS_EX_CLIENTEDGE, | |
g_szClassName, | |
"The title of my window", | |
WS_OVERLAPPEDWINDOW, | |
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, | |
NULL, NULL, wc.hInstance, NULL ); | |
if (hwnd == NULL) | |
{ | |
MessageBox( NULL, "Window Creation Failed!", "Error!", | |
MB_ICONEXCLAMATION | MB_OK ); | |
return 0; | |
} | |
ShowWindow( hwnd, SW_SHOWNORMAL ); | |
UpdateWindow( hwnd ); | |
// ------------ Enable a connection with the LeapMotion ------------- | |
LeapInterface* leapController = new LeapInterface; | |
LEAP_CONNECTION* connHandle = leapController->OpenConnection(); | |
std::cout << "Connecting to Leap Motion Service..." << std::endl; | |
while (!leapController->IsConnected()){ | |
LeapInterface::millisleep( 250 ); | |
} | |
std::cout << "Connected!\nConnecting to device..." << std::endl; | |
LEAP_DEVICE_INFO* deviceProps = leapController->GetDeviceProperties(); | |
while (!deviceProps) | |
{ | |
deviceProps = leapController->GetDeviceProperties(); | |
LeapInterface::millisleep( 250 ); | |
} | |
std::cout << "Using device : " << deviceProps->serial << std::endl; | |
// ------------ Enable a connection with the LeapMotion ------------- | |
// Step 3: The Message Loop | |
while (GetMessage( &Msg, NULL, 0, 0 ) > 0) | |
{ | |
TranslateMessage( &Msg ); | |
DispatchMessage( &Msg ); | |
LEAP_TRACKING_EVENT *frame = leapController->GetFrame(); | |
if (frame) | |
std::cout << frame->nHands << std::endl; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment