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 } |