annotate native/PhantomOmni/uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics.cpp @ 8:ea7885bd9bff tip

fixed bug : render solid line as dotted/dashed when moving the stylus from dotted/dashed to solid
author ccmi-guest
date Thu, 03 Jul 2014 16:12:20 +0100
parents d66dd5880081
children
rev   line source
fiore@5 1 #include "uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics.h"
fiore@5 2 #include "stdafx.h"
fiore@5 3 #include "GraphicManager.h"
fiore@5 4 #include "HapticManager.h"
fiore@5 5 #include "HapticException.h"
fiore@5 6 #include <stdlib.h>
fiore@5 7 #include <math.h>
fiore@5 8 #include <assert.h>
fiore@5 9 #include <setjmp.h>
fiore@5 10 #include "utils.h"
fiore@5 11
fiore@5 12 #define CURSOR_SIZE_PIXELS 20
fiore@5 13
fiore@5 14 enum {JMP_OK =0, JMP_EXIT };
fiore@5 15
fiore@5 16 /**************************
fiore@5 17 Function prototypes.
fiore@5 18 ***************************/
fiore@5 19 /* callbacks */
fiore@5 20 void displayCallback(void);
fiore@5 21 void reshapeCallback(int width, int height);
fiore@5 22 void idleCallback(void);
fiore@5 23 void exitProcedure(void);
fiore@5 24 void hapticCommandCB(const jchar cmd, const jint ID, const jdouble x, const jdouble y,const jdouble startX, const jdouble startY);
fiore@5 25
fiore@5 26 void drawCursor();
fiore@5 27 void updateWorkspace();
fiore@5 28
fiore@5 29 void initJniVariables(void);
fiore@5 30 /*************************
fiore@5 31 global variables
fiore@5 32 **************************/
fiore@5 33 static GraphicManager *gManager;
fiore@5 34 static HapticManager *hManager;
fiore@5 35 static CollectionsManager *cManager;
fiore@5 36 JNIEnv *env;
fiore@5 37 jobject *lock;
fiore@5 38 static int width;
fiore@5 39 static int height;
fiore@5 40 static jmp_buf jmpenv;
fiore@5 41 static int jmpval = 0;
fiore@5 42 /* jni variables */
fiore@5 43 jclass hapticClass;
fiore@5 44 jclass hapticListenerClass;
fiore@5 45 jfieldID shutdownfieldId;
fiore@5 46 jfieldID newHapticIdfieldId;
fiore@5 47 jfieldID dumpHapticIdfieldId;
fiore@5 48 jfieldID currentHapticIdfieldId;
fiore@5 49 jfieldID hapticInitFailedfieldId;
fiore@5 50 jfieldID attractTofieldId;
fiore@5 51 jfieldID attractToHapticIdFieldId;
fiore@5 52 jfieldID pickUpfieldId;
fiore@5 53 jfieldID pickUpHapticIdFieldId;
fiore@5 54 jfieldID hapticListenerFieldId;
fiore@5 55 jfieldID cmdFieldId; // belongs to the haptic listener
fiore@5 56 jfieldID diagramElementFieldId; // belongs to the haptic listener
fiore@5 57 jfieldID xFieldId; // belongs to the haptic listener
fiore@5 58 jfieldID yFieldId; // belongs to the haptic listener
fiore@5 59 jfieldID startXFieldId; // belongs to the haptic listener
fiore@5 60 jfieldID startYFieldId; // belongs to the haptic listener
fiore@5 61 jobject hapticListener;
fiore@5 62 jmethodID notifyMethodId;
fiore@5 63 jmethodID notifyListenerMethodId;
fiore@5 64 jmethodID waitMethodId;
fiore@5 65
fiore@5 66 /*******************************************************************************
fiore@5 67 Initializes GLUT for displaying a simple haptic scene.
fiore@5 68 *******************************************************************************/
fiore@5 69 JNIEXPORT jint JNICALL Java_uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_initOmni
fiore@5 70 (JNIEnv *environment, jobject obj, jint w, jint h){
fiore@5 71 env = environment;
fiore@5 72 lock = &obj;
fiore@5 73 /* fake main argv and argc as this is a dll */
fiore@5 74 char *argv[1] = {"OmniHaptics"};
fiore@5 75 int argc = 1;
fiore@5 76
fiore@5 77 initJniVariables();
fiore@5 78
fiore@5 79 /* glut initialization */
fiore@5 80 glutInit(&argc, argv);
fiore@5 81 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
fiore@5 82 glutInitWindowSize(w, h);
fiore@5 83 glutCreateWindow("CCmI Diagram Haptics");
fiore@5 84
fiore@5 85 /* glut callbacks */
fiore@5 86 glutDisplayFunc(displayCallback);
fiore@5 87 glutReshapeFunc(reshapeCallback);
fiore@5 88 glutIdleFunc(idleCallback);
fiore@5 89
fiore@5 90 cManager = new CollectionsManager(env,lock);
fiore@5 91 hManager = new HapticManager(cManager,hapticCommandCB);
fiore@5 92 gManager = new GraphicManager(cManager,hManager);
fiore@5 93 cManager->init();
fiore@5 94 gManager->init();
fiore@5 95
fiore@5 96 // try to initialize the haptic, tell the java thread about the success/failure. The endeavour
fiore@5 97 // takes the lock on the haptics java object in order to access the nodes and edges concurrently
fiore@5 98 if(env->MonitorEnter(*lock) != JNI_OK){
fiore@5 99 stopExecution("Could not allocate memory for haptic thread monitor");
fiore@5 100 }
fiore@5 101 checkExceptions(env, "Could not enter monitor on Haptics");
fiore@5 102
fiore@5 103 bool mustSayGoodbye = false;
fiore@5 104 try{
fiore@5 105 hManager->init();
fiore@5 106 }catch (HapticException e){
fiore@5 107 /* set the field in the java class to comunicate the main thread (waiting *
fiore@5 108 * for the initialization to complete) that the initialization failed */
fiore@5 109 env->SetBooleanField(*lock,hapticInitFailedfieldId,JNI_TRUE);
fiore@5 110 mustSayGoodbye = true;
fiore@5 111 }
fiore@5 112
fiore@5 113 if(mustSayGoodbye)
fiore@5 114 std::cout << "Failed to initialize Omni Haptic device" << std::endl;
fiore@5 115 else
fiore@5 116 std::cout << "Omni Haptic device successfully initialized" << std::endl;
fiore@5 117
fiore@5 118 // notify the other thread
fiore@5 119 env->CallVoidMethod(*lock,notifyMethodId);
fiore@5 120 checkExceptions(env,"Could not call notify() on Haptics");
fiore@5 121
fiore@5 122 //release the lock
fiore@5 123 if(env->MonitorExit(*lock) != JNI_OK){
fiore@5 124 std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
fiore@5 125 exit(-1);
fiore@5 126 }
fiore@5 127
fiore@5 128 if(mustSayGoodbye)
fiore@5 129 /* initialization failed: return */
fiore@5 130 return -1;
fiore@5 131
fiore@5 132 /* use setjmp to be able to jump off the glutMainLoop when the user shuts the program down */
fiore@5 133 jmpval = setjmp(jmpenv);
fiore@5 134
fiore@5 135 /* star the loop*/
fiore@5 136 if(jmpval == JMP_OK){
fiore@5 137 glutMainLoop();
fiore@5 138 }
fiore@5 139
fiore@5 140 exitProcedure();
fiore@5 141
fiore@5 142 return 0;
fiore@5 143 }
fiore@5 144
fiore@5 145 /*******************************************************************************
fiore@5 146 GLUT callback for redrawing the view.
fiore@5 147 *******************************************************************************/
fiore@5 148 void displayCallback(){
fiore@5 149
fiore@5 150 // takes the lock on the haptics java obejct in order to access the nodes and edges concurrently
fiore@5 151 if(env->MonitorEnter(*lock) != JNI_OK){
fiore@5 152 stopExecution("Could not allocate memory for haptic thread monitor");
fiore@5 153 }
fiore@5 154 checkExceptions(env,"Could not enter monitor on Haptics");
fiore@5 155
fiore@5 156 // check if there is a shutdown request
fiore@5 157 if( env->GetBooleanField(*lock,shutdownfieldId) == JNI_TRUE ){
fiore@5 158 // notify the other thread that this thread is about to die
fiore@5 159 env->CallVoidMethod(*lock,notifyMethodId);
fiore@5 160 checkExceptions(env, "Could not call notify() on Haptics");
fiore@5 161 // release the lock
fiore@5 162 if(env->MonitorExit(*lock) != JNI_OK){
fiore@5 163 std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
fiore@5 164 }
fiore@5 165 longjmp(jmpenv,JMP_EXIT);
fiore@5 166 }
fiore@5 167
fiore@5 168 // check if the user asked to be attracted to a node
fiore@5 169 jboolean attractTo = env->GetBooleanField(*lock,attractTofieldId);
fiore@5 170 jint attractToDiagramId = 0;
fiore@5 171 if(attractTo == JNI_TRUE){
fiore@5 172 env->SetBooleanField(*lock,attractTofieldId,JNI_FALSE);
fiore@5 173 jint attractToHapticId = env->GetIntField(*lock,attractToHapticIdFieldId);
fiore@5 174 if(cManager->isNode(attractToHapticId)){
fiore@5 175 attractToDiagramId = cManager->getNodeDataFromID(attractToHapticId).diagramId;
fiore@5 176 }else{
fiore@5 177 attractToDiagramId = cManager->getEdgeDataFromID(attractToHapticId).diagramId;
fiore@5 178 }
fiore@5 179 hManager->setAttractTo(attractToHapticId);
fiore@5 180 }
fiore@5 181 // check if the user picked up a node
fiore@5 182 jboolean pickUp = env->GetBooleanField(*lock,pickUpfieldId);
fiore@5 183 jint pickUpDiagramId = 0;
fiore@5 184 if(pickUp == JNI_TRUE){
fiore@5 185 env->SetBooleanField(*lock,pickUpfieldId,JNI_FALSE);
fiore@5 186 jint pickUpHapticId = env->GetIntField(*lock,pickUpHapticIdFieldId);
fiore@5 187 if(cManager->isNode(pickUpHapticId)){
fiore@5 188 pickUpDiagramId = cManager->getNodeDataFromID(pickUpHapticId).diagramId;
fiore@5 189 }else{
fiore@5 190 pickUpDiagramId = cManager->getEdgeDataFromID(pickUpHapticId).diagramId;
fiore@5 191 }
fiore@5 192 hManager->pickUp(pickUpDiagramId);
fiore@5 193 }
fiore@5 194
fiore@5 195 // draw the scene graphically and haptically
fiore@5 196 hManager->draw();
fiore@5 197 gManager->draw();
fiore@5 198
fiore@5 199 // check whether the java thread needs to either create or dump an haptic id
fiore@5 200 jboolean needsNewHapticId = env->GetBooleanField(*lock,newHapticIdfieldId);
fiore@5 201 if(needsNewHapticId == JNI_TRUE){
fiore@5 202 // set int currentHapticId of class Haptics with a new generated id
fiore@5 203 jint newHapticid = hlGenShapes(1);
fiore@5 204 env->SetIntField(*lock, currentHapticIdfieldId, newHapticid);
fiore@5 205 //set the boolean field to false as the other thread now has an id
fiore@5 206 env->SetBooleanField(*lock, newHapticIdfieldId, JNI_FALSE);
fiore@5 207 //notify the other thread
fiore@5 208 env->CallVoidMethod(*lock,notifyMethodId);
fiore@5 209 checkExceptions(env, "Could not call notify() on Haptics");
fiore@5 210 }
fiore@5 211 jboolean needsDumpOldHapticId = env->GetBooleanField(*lock, dumpHapticIdfieldId);
fiore@5 212 if(needsDumpOldHapticId == JNI_TRUE){
fiore@5 213 // get the id of the deleted element from the other thread
fiore@5 214 jint oldHapticId = env->GetIntField(*lock,currentHapticIdfieldId);
fiore@5 215 // free the old haptic id
fiore@5 216 hlDeleteShapes(oldHapticId,1);
fiore@5 217 // set the boolean field as the id has been cleaned up
fiore@5 218 env->SetBooleanField(*lock,dumpHapticIdfieldId,JNI_FALSE);
fiore@5 219 // notify the other thread
fiore@5 220 env->CallVoidMethod(*lock,notifyMethodId);
fiore@5 221 checkExceptions(env, "Could not call notify() on Haptics");
fiore@5 222 }
fiore@5 223
fiore@5 224 /* release lock */
fiore@5 225 if(env->MonitorExit(*lock) != JNI_OK){
fiore@5 226 stopExecution("Could not release memory for haptic thread monitor");
fiore@5 227 }
fiore@5 228
fiore@5 229 /* it's important that this call be outside the monitors, else a deadlock occurs */
fiore@5 230 // Call any event callbacks that have been triggered.
fiore@5 231 if(attractTo == JNI_TRUE){
fiore@5 232 if(hManager->wasAlreadyTouchingAttractingElement()){
fiore@5 233 hapticCommandCB('t',attractToDiagramId,0,0,0,0);
fiore@5 234 }
fiore@5 235 }
fiore@5 236
fiore@5 237 hlCheckEvents();
fiore@5 238 /* button one is not based on events only , but also on time lapse as the single click is effective *
fiore@5 239 * after an amount of time (if another click is not issued). Therefore we need to continuosly for the *
fiore@5 240 * button status, hence the work must be done here and not in the click callbacks */
fiore@5 241 HapticManager::ClickStatus click = hManager->getButton1Status();
fiore@5 242 if(click == HapticManager::ONE_CLICK){
fiore@5 243 hManager->doButton1Click(false);
fiore@5 244 }else if(click == HapticManager::TWO_CLICK){
fiore@5 245 hManager->doButton1Click(true);
fiore@5 246 }
fiore@5 247
fiore@5 248 glutSwapBuffers();
fiore@5 249 }
fiore@5 250 /*******************************************************************************
fiore@5 251 GLUT callback for reshaping the window. This is the main place where the
fiore@5 252 viewing and workspace transforms get initialized.
fiore@5 253 *******************************************************************************/
fiore@5 254 void reshapeCallback(int w, int h){
fiore@5 255 static const double kPI = 3.1415926535897932384626433832795;
fiore@5 256 static const double kFovY = 40;
fiore@5 257
fiore@5 258 static const double nearDist = 1.0 / tan((kFovY / 2.0) * kPI / 180.0 /* radiants for 1 degree */);
fiore@5 259 static const double farDist = nearDist + 2.0;
fiore@5 260 double aspect;
fiore@5 261 width = w;
fiore@5 262 height = h;
fiore@5 263
fiore@5 264 cManager->setScreenHeight(h);
fiore@5 265 glViewport(0, 0, width, height);
fiore@5 266 // Compute the viewing parameters based on a fixed fov and viewing
fiore@5 267 // a canonical box centered at the origin.
fiore@5 268 aspect = (double) width/height ;
fiore@5 269
fiore@5 270 glMatrixMode(GL_PROJECTION);
fiore@5 271 glLoadIdentity();
fiore@5 272 gluPerspective(kFovY, aspect,nearDist, farDist);
fiore@5 273 // Place the camera down the Z axis looking at the origin.
fiore@5 274 glMatrixMode(GL_MODELVIEW);
fiore@5 275 glLoadIdentity();
fiore@5 276 gluLookAt(0, 0, nearDist + 1.0,
fiore@5 277 0, 0, 0,
fiore@5 278 0, 1, 0);
fiore@5 279
fiore@5 280 hduVector3Dd origin;
fiore@5 281 fromScreen(hduVector3Dd(0, 0, 0),origin);
fiore@5 282 glLoadIdentity();
fiore@5 283 gluLookAt(-origin[0],origin[1], nearDist + 1.0,
fiore@5 284 -origin[0],origin[1], 0,
fiore@5 285 0, 1, 0);
fiore@5 286
fiore@5 287 hduVector3Dd end;
fiore@5 288 fromScreen(hduVector3Dd(w, h, 0),end);
fiore@5 289
fiore@5 290 //glTranslatef(end[0]-origin[0],-(end[1]-origin[1]),0);
fiore@5 291
fiore@5 292 updateWorkspace();
fiore@5 293 }
fiore@5 294
fiore@5 295
fiore@5 296 /*******************************************************************************
fiore@5 297 Use the current OpenGL viewing transforms to initialize a transform for the
fiore@5 298 haptic device workspace so that it's properly mapped to world coordinates.
fiore@5 299 *******************************************************************************/
fiore@5 300 void updateWorkspace(){
fiore@5 301 GLdouble modelview[16];
fiore@5 302 GLdouble projection[16];
fiore@5 303 GLint viewport[4];
fiore@5 304
fiore@5 305 glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
fiore@5 306 glGetDoublev(GL_PROJECTION_MATRIX, projection);
fiore@5 307 glGetIntegerv(GL_VIEWPORT, viewport);
fiore@5 308
fiore@5 309 hlMatrixMode(HL_TOUCHWORKSPACE);
fiore@5 310 hlLoadIdentity();
fiore@5 311
fiore@5 312 // Fit haptic workspace to view volume.
fiore@5 313 hluFitWorkspace(projection);
fiore@5 314
fiore@5 315 // Compute cursor scale.
fiore@5 316 gManager->gCursorScale = hluScreenToModelScale(modelview, projection, viewport);
fiore@5 317 gManager->gCursorScale *= CURSOR_SIZE_PIXELS;
fiore@5 318
fiore@5 319 hduVector3Dd p0, p1;
fiore@5 320 bool bNoError;
fiore@5 321
fiore@5 322 bNoError = fromScreen(hduVector3Dd(0, 0, 0), p0);
fiore@5 323 assert(bNoError);
fiore@5 324
fiore@5 325 bNoError = fromScreen(hduVector3Dd(1, 1, 0), p1);
fiore@5 326 assert(bNoError);
fiore@5 327
fiore@5 328 double m_windowTworldScale = (p1 - p0).magnitude() / sqrt(2.0);
fiore@5 329 gManager->gWorldScale = m_windowTworldScale;
fiore@5 330 }
fiore@5 331 /*******************************************************************************
fiore@5 332 GLUT callback for idle state. Use this as an opportunity to request a redraw.
fiore@5 333 Checks for HLAPI errors that have occurred since the last idle check.
fiore@5 334 *******************************************************************************/
fiore@5 335 void idleCallback(){
fiore@5 336 HLerror error;
fiore@5 337
fiore@5 338 while (HL_ERROR(error = hlGetError())){
fiore@5 339 std::cerr << "HL Error: " << error.errorCode << std::endl <<error.errorInfo << std::endl;
fiore@5 340
fiore@5 341 if (error.errorCode == HL_DEVICE_ERROR){
fiore@5 342 hduPrintError(stderr, &error.errorInfo, "Device error\n");
fiore@5 343 std::cout << "sending error message to haptic listener" << std::endl;
fiore@5 344 hapticCommandCB('e',0,0,0,0,0);
fiore@5 345 }
fiore@5 346 }
fiore@5 347 glutPostRedisplay();
fiore@5 348 }
fiore@5 349
fiore@5 350 /*******************************************************************************
fiore@5 351 This handler is called when the application is exiting. Deallocates any state
fiore@5 352 and cleans up.
fiore@5 353 *******************************************************************************/
fiore@5 354 void exitProcedure(){
fiore@5 355
fiore@5 356 // Free up the haptic rendering context.
fiore@5 357 hlMakeCurrent(NULL);
fiore@5 358
fiore@5 359 if (HapticManager::ghHLRC != NULL){
fiore@5 360 hlDeleteContext(HapticManager::ghHLRC);
fiore@5 361 }
fiore@5 362
fiore@5 363 // Free up the haptic device.
fiore@5 364 if (HapticManager::ghHD != HD_INVALID_HANDLE){
fiore@5 365 hdDisableDevice(HapticManager::ghHD);
fiore@5 366 }
fiore@5 367 std::cout << "freeing haptic resources" << std::endl;
fiore@5 368 }
fiore@5 369
fiore@5 370 /* initialize all the variable needed for the jni access to the Haptics class from the openGL thread*/
fiore@5 371 void initJniVariables(void){
fiore@5 372 /* --- CLASSES --- */
fiore@5 373 //this class
fiore@5 374 hapticClass = env->GetObjectClass(*lock);
fiore@5 375 if(hapticClass == NULL){
fiore@5 376 stopExecution("Could not find the Haptics class");
fiore@5 377 }
fiore@5 378 // the haptic listener, member of this class
fiore@5 379 hapticListenerClass = env->FindClass("Luk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread;");
fiore@5 380 if(hapticListenerClass == NULL){
fiore@5 381 stopExecution("Could not find the haptic listener class");
fiore@5 382 }
fiore@5 383
fiore@5 384 /* --- FIELD IDS --- */
fiore@5 385 // boolean set by the java thread when an element is added and it needs a new id from the haptic library
fiore@5 386 newHapticIdfieldId = env->GetFieldID(hapticClass,"newHapticId", "Z");
fiore@5 387 if(newHapticIdfieldId == NULL){
fiore@5 388 stopExecution("failed to find the newHapticId field id");
fiore@5 389 }
fiore@5 390
fiore@5 391 // boolean set by the java thread when an element is added and it needs a new id from the haptic library
fiore@5 392 dumpHapticIdfieldId = env->GetFieldID(hapticClass,"dumpHapticId", "Z");
fiore@5 393 if(dumpHapticIdfieldId == NULL){
fiore@5 394 stopExecution("failed to find the dumpHapticId field id");
fiore@5 395 }
fiore@5 396
fiore@5 397 // boolean set to this thread to notify the java thread the unsuccessful initialization of haptic device
fiore@5 398 hapticInitFailedfieldId = env->GetFieldID(hapticClass,"hapticInitFailed", "Z");
fiore@5 399 if(hapticInitFailedfieldId == NULL){
fiore@5 400 stopExecution("failed to find the hapticInitFailedfieldId field id");
fiore@5 401 }
fiore@5 402
fiore@5 403 // boolean set by the java thread to notify this thread the program has been shut down
fiore@5 404 shutdownfieldId = env->GetFieldID(hapticClass, "shutdown", "Z");
fiore@5 405 if(shutdownfieldId == NULL){
fiore@5 406 stopExecution("failed to find the shutdownfieldId field id");
fiore@5 407 }
fiore@5 408
fiore@5 409 // boolean set by the java thread when the user asks to sna
fiore@5 410 attractTofieldId = env->GetFieldID(hapticClass, "attractTo" , "Z");
fiore@5 411 if(attractTofieldId == NULL){
fiore@5 412 stopExecution("failed to find the attractTo field id");
fiore@5 413 }
fiore@5 414
fiore@5 415 attractToHapticIdFieldId = env->GetFieldID(hapticClass, "attractToHapticId", "I");
fiore@5 416 if(attractToHapticIdFieldId == NULL){
fiore@5 417 stopExecution("failed to find the attractToHapticId field id");
fiore@5 418 }
fiore@5 419
fiore@5 420 pickUpfieldId = env->GetFieldID(hapticClass,"pickUp","Z");
fiore@5 421 if(pickUpfieldId == NULL){
fiore@5 422 stopExecution("failed to find the pickUp field id");
fiore@5 423 }
fiore@5 424
fiore@5 425 pickUpHapticIdFieldId = env->GetFieldID(hapticClass,"pickUpHapticId","I");
fiore@5 426 if(pickUpHapticIdFieldId == NULL){
fiore@5 427 stopExecution("failed to find pickUpHapticId field id");
fiore@5 428 }
fiore@5 429
fiore@5 430 hapticListenerFieldId = env->GetFieldID(hapticClass, "hapticListener", "Luk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread;");
fiore@5 431 if(hapticListenerFieldId == NULL){
fiore@5 432 stopExecution("failed to find the hapticListenerThread field id");
fiore@5 433 }
fiore@5 434
fiore@5 435 // variable to exchange values between threads
fiore@5 436 currentHapticIdfieldId = env->GetFieldID(hapticClass,"currentHapticId","I");
fiore@5 437 if(currentHapticIdfieldId == NULL){
fiore@5 438 stopExecution("failed to find the currentHapticId field");
fiore@5 439 }
fiore@5 440
fiore@5 441 cmdFieldId = env->GetFieldID(hapticListenerClass,"cmd", "C");
fiore@5 442 if(cmdFieldId == NULL){
fiore@5 443 stopExecution("failed to find the cmd field id of the hapticListener class");
fiore@5 444 }
fiore@5 445
fiore@5 446 diagramElementFieldId = env->GetFieldID(hapticListenerClass, "diagramElementID", "I");
fiore@5 447 if(diagramElementFieldId == NULL){
fiore@5 448 stopExecution("failed to find the diagramElement field id of the hapticListener class");
fiore@5 449 }
fiore@5 450
fiore@5 451 xFieldId = env->GetFieldID(hapticListenerClass, "x", "D");
fiore@5 452 if(xFieldId == NULL){
fiore@5 453 stopExecution("failed to find the x field id of the hapticListener class");
fiore@5 454 }
fiore@5 455
fiore@5 456 yFieldId = env->GetFieldID(hapticListenerClass, "y", "D");
fiore@5 457 if(yFieldId == NULL){
fiore@5 458 stopExecution("failed to find the y field id of the hapticListener class");
fiore@5 459 }
fiore@5 460
fiore@5 461 startXFieldId = env->GetFieldID(hapticListenerClass, "startX", "D");
fiore@5 462 if(startXFieldId == NULL){
fiore@5 463 stopExecution("failed to find the x field id of the hapticListener class");
fiore@5 464 }
fiore@5 465
fiore@5 466 startYFieldId = env->GetFieldID(hapticListenerClass, "startY", "D");
fiore@5 467 if(startYFieldId == NULL){
fiore@5 468 stopExecution("failed to find the y field id of the hapticListener class");
fiore@5 469 }
fiore@5 470
fiore@5 471 hapticListener = env->GetObjectField(*lock,hapticListenerFieldId);
fiore@5 472 /* --- METHOD IDs --- */
fiore@5 473 // notify method
fiore@5 474 notifyMethodId = env->GetMethodID(hapticClass,"notify","()V");
fiore@5 475 if(notifyMethodId == NULL){
fiore@5 476 stopExecution("failed to find the notify method id");
fiore@5 477 }
fiore@5 478
fiore@5 479 notifyListenerMethodId = env->GetMethodID(hapticListenerClass,"notify","()V");
fiore@5 480 if(notifyListenerMethodId == NULL){
fiore@5 481 stopExecution("failed to find the notify method id");
fiore@5 482 }
fiore@5 483
fiore@5 484 waitMethodId = env->GetMethodID(hapticListenerClass,"wait","()V");
fiore@5 485 if(waitMethodId == NULL){
fiore@5 486 stopExecution("failed to find the wait method id");
fiore@5 487 }
fiore@5 488 }
fiore@5 489
fiore@5 490 void hapticCommandCB(const jchar cmd, const jint ID, const jdouble x, const jdouble y, const jdouble startX, const jdouble startY){
fiore@5 491 /* the haptic listener java thread is waiting for commands
fiore@5 492 first set the variable, the Haptic Listener java thread will read after being notified,
fiore@5 493 then notify and get it awake. Thus wait for the java thread to notify that the command
fiore@5 494 has been accomplished. This is done as otherwise some commands might be neglected. as if the thread
fiore@5 495 scheduler decides to execute twice this routine without executing the java thread in the middle then
fiore@5 496 the former command gets overwritten by the latter.
fiore@5 497 Note the monitor is hapticListener and not haptics as for the draw function.
fiore@5 498 When in this routine, this thread does not hold the lock on haptics as if a command results in changing
fiore@5 499 the elements collections (e.g. moveNode) all those methods are synchronized and require to acquire the lock on haptics.
fiore@5 500 Since this thread would wait for the command to be executed by the java thread, which in turns would wait for this
fiore@5 501 thread to release the lock on haptics, that would result in a deadlock.
fiore@5 502 */
fiore@5 503 /* now wake up the haptic listener */
fiore@5 504 if(env->MonitorEnter(hapticListener) != JNI_OK){
fiore@5 505 stopExecution("Could not allocate memory for haptic listener thread monitor");
fiore@5 506 }
fiore@5 507 checkExceptions(env,"Could not enter monitor on the haptic listener");
fiore@5 508 env->SetCharField(hapticListener,cmdFieldId,cmd);
fiore@5 509 env->SetIntField(hapticListener,diagramElementFieldId,ID);
fiore@5 510 env->SetDoubleField(hapticListener,xFieldId,x);
fiore@5 511 env->SetDoubleField(hapticListener,yFieldId,y);
fiore@5 512 env->SetDoubleField(hapticListener,startXFieldId,startX);
fiore@5 513 env->SetDoubleField(hapticListener,startYFieldId,startY);
fiore@5 514 // wake the java thread up to execute the command
fiore@5 515 env->CallVoidMethod(hapticListener,notifyListenerMethodId);
fiore@5 516 checkExceptions(env, "Could not call notify() on HapticListener");
fiore@5 517 /* wait for the commands to be executed. Here is actually where the monitor is
fiore@5 518 * freed and the java thread starts to execute the command, having been notified */
fiore@5 519 env->CallVoidMethod(hapticListener,waitMethodId);
fiore@5 520 checkExceptions(env, "Could not call wait() on HapticListener");
fiore@5 521 if(env->MonitorExit(hapticListener) != JNI_OK){
fiore@5 522 stopExecution("Could not release memory for haptic listener thread monitor");
fiore@5 523 }
fiore@5 524 }