Mercurial > hg > haptic-xypad
view native/OpenHaptics/XYPad/uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.cpp @ 1:46671fc7d649 tip
fixed "window" message bug and brought the message outside the haptic device monitor
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Fri, 13 Mar 2015 13:02:16 +0000 |
parents | 011caca7515a |
children |
line wrap: on
line source
/* XYPad - a haptic xy-pad that uses the jHapticGUI library Copyright (C) 2015 Queen Mary University of London (http://depic.eecs.qmul.ac.uk/) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.h" #include "HapticScene.h" #include "HapticSceneFactory.h" #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include <windows.h> #include <iostream> #include <GL/gl.h> #include <GL/glu.h> #include <GL/glut.h> using namespace jhapticgui; /* Function declarations */ void display(void); void reshape(int width, int height); void idle(void); void send(const Message &m); // haptic scene callback function void checkExceptions(JNIEnv *env, char* what); void initJniVariables(void); void die(char* msg); void Notify(jobject *monitor); // macro for the java object.notify() void MonitorEnter(jobject *monitor); // macro for the entering a syncrhronized block in java void MonitorExit(jobject *monitor); // macro for the exiting a syncrhronized block in java /* Variables */ static JNIEnv *env; static jobject *thisObj; static jobject hapticListener; static std::unique_ptr<HapticScene> scene; static Message message; /* JNI Variables */ static jfieldID disposeFieldID; static jfieldID deviceInitFailedFieldID; static jfieldID deviceInitDoneFieldID; static jfieldID messageReadyFieldID; static jfieldID commandFieldID; static jfieldID argsFieldID; static jfieldID IDFieldID; /* synchronization methods ids */ static jmethodID notifyMethodID; static jmethodID waitListenerMethodID; static jmethodID messageToListenerMethodID; JNIEXPORT void JNICALL Java_uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice_init (JNIEnv *environment, jobject obj, jint w, jint h){ env = environment; thisObj = &obj; /* JNI stuff */ initJniVariables(); scene = makeHapticScene(send); /* try to initialize the haptic, tell the java thread about the success/failure. The endeavour * * takes the thisObj on the haptics java object in order to access the nodes and edges concurrently */ MonitorEnter(thisObj); bool mustSayGoodbye = false; if(!scene->initHaptics()){ /* set the field in the java class to comunicate the main thread (waiting * * for the initialization to complete) that the initialization failed */ env->SetBooleanField(*thisObj, deviceInitFailedFieldID, JNI_TRUE); mustSayGoodbye = true; } if(mustSayGoodbye) std::cout << "Failed to initialize haptic device" << std::endl; else std::cout << "Haptic device successfully initialized" << std::endl; /* initialization is done, set the flag for the java thread*/ env->SetBooleanField(*thisObj, deviceInitDoneFieldID, JNI_TRUE); /* notify the java thread */ Notify(thisObj); /* release the lock */ MonitorExit(thisObj); if(mustSayGoodbye){ /* stops the haptic listener */ send(Message("stop","",0)); /* initialization failed: return */ return; } /* haptic device is initialized, now build the openGL window */ /* fake main argv and argc as this is a dll */ char *argv[1] = {"Haptics"}; int argc = 1; /* glut initialization */ glutInit(&argc, argv); glutInitWindowSize( w, h ); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutCreateWindow( "Haptic Device Window" ); /* set up GLUT callback functions */ glutDisplayFunc ( display ); glutReshapeFunc ( reshape ); glutIdleFunc( idle ); scene->initGL(); #ifdef FREEGLUT /* with this option set freeglut allows to stop the main loop without teminating the whole application */ glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); #endif glutMainLoop(); return; } /* GLUT display callback */ void display(){ /* takes the lock on thisObj */ MonitorEnter(thisObj); /* check if there is a dispose request */ if( env->GetBooleanField(*thisObj,disposeFieldID) == JNI_TRUE ){ /* set back to false to prevent the java thread from waiting again */ env->SetBooleanField(*thisObj,disposeFieldID,JNI_FALSE); /* stops the haptic listener */ send(Message("stop","",0)); /* notify the java thread that this thread is about to die */ Notify(thisObj); /* release the lock on this object */ MonitorExit(thisObj); #ifdef FREEGLUT /* onlu FREEGLUT allows to exit the main loop */ glutLeaveMainLoop (); return; #else /* if freeglut is not used, the whole application must be terminated */ die("Haptic device disposed"); #endif } /* check if there is a message ready for the java thread */ bool messageReady = (env->GetBooleanField(*thisObj,messageReadyFieldID) == JNI_TRUE); if(messageReady){ /* get the id of the field */ jint ID = env->GetIntField(*thisObj, IDFieldID); jstring command = static_cast<jstring>(env->GetObjectField(*thisObj, commandFieldID)); jstring args = static_cast<jstring>(env->GetObjectField(*thisObj, argsFieldID)); /* get lenght of the strings */ int commandLen = env->GetStringLength(command); int argsLen = env->GetStringLength(args); /* read the command fields into the command struct */ env->GetStringUTFRegion(command, 0, commandLen, message.command); env->GetStringUTFRegion(args, 0, argsLen, message.args); message.ID = ID; /* set the flag back to false */ env->SetBooleanField(*thisObj, messageReadyFieldID, JNI_FALSE); /* clean up references */ env->DeleteLocalRef(command); env->DeleteLocalRef(args); /* notify the java thread, waiting for the command to be read */ Notify(thisObj); } /* release the lock. HapticScene methods from now on can execute commands without deadlock risk */ MonitorExit(thisObj); static bool once = false; if (!once){ once = true; send(Message("window", "", 0)); } unsigned int messageUpdate = 0; if(messageReady) messageUpdate = scene->processMessage(message); /* --- start drawing --- */ scene->beginFrame(); /* draw the diagram graphicly and, if the collection has changed, haptically too */ scene->drawCursor(); /* the drawing can be affected by a message from the Java thread but also by the user actions (move, push button etc.). That's why callbacksUpdate value from the previous loop is passed as an arg as well */ static unsigned int callbacksUpdate = 0; scene->drawScene(messageUpdate, callbacksUpdate); scene->endFrame(); /* check if any callback has been issued : */ callbacksUpdate = scene->checkCallbacks(); /* swap graphic buffers. */ glutSwapBuffers(); } /* GLUT reshape callback */ void reshape (int w, int h){ scene->setSize(w, h); } /* GLUT idle callback */ void idle(){ glutPostRedisplay(); } /* initialize all the variables needed for the jni access to java fields */ void initJniVariables(void){ /* --- CLASSES --- */ /* this class */ jclass hapticDeviceClass = env->GetObjectClass(*thisObj); if(hapticDeviceClass == NULL){ die("Could not find the Haptics class"); } /* the haptic listener, member of this class */ jclass hapticListenerClass = env->FindClass("Luk/ac/qmul/eecs/depic/jhapticgui/HapticListener;"); if(hapticListenerClass == NULL){ die("Could not find the haptic listener class"); } /* the Object class for wait and notify methods */ jclass objectClass = env->FindClass("Ljava/lang/Object;"); if(objectClass == NULL ){ die("Could not find te Object class"); } /* --- FIELD ID's --- */ /* boolean set by this thread to notify the java thread the unsuccessful initialization of haptic device */ deviceInitFailedFieldID = env->GetFieldID(hapticDeviceClass,"initFailed", "Z"); if(deviceInitFailedFieldID == NULL){ die("failed to find the initFailed field id"); } /* boolean set by this thread to notify the java thread the initialization of haptic device is done */ deviceInitDoneFieldID = env->GetFieldID(hapticDeviceClass,"initDone", "Z"); if(deviceInitDoneFieldID == NULL){ die("failed to find initDone field id"); } /* boolean set by the java thread to notify this thread the program has been shut down */ disposeFieldID = env->GetFieldID(hapticDeviceClass, "dispose", "Z"); if(disposeFieldID == NULL){ die("failed to find the disposeFieldID field id"); } messageReadyFieldID = env->GetFieldID(hapticDeviceClass,"messageReady","Z"); if(messageReadyFieldID == NULL){ die("failed to find the messageReadyFieldID field id"); } commandFieldID = env->GetFieldID(hapticDeviceClass,"command","Ljava/lang/String;"); if(commandFieldID == NULL){ die("failed to find the commandFieldID field id"); } argsFieldID = env->GetFieldID(hapticDeviceClass,"args","Ljava/lang/String;"); if(argsFieldID == NULL){ die("failed to find the argsFieldID field id"); } IDFieldID = env->GetFieldID(hapticDeviceClass,"ID","I"); if(IDFieldID == NULL){ die("failed to find the IDFieldID field id"); } jfieldID hapticListenerFieldID = env->GetFieldID(hapticDeviceClass,"hapticListener","Luk/ac/qmul/eecs/depic/jhapticgui/HapticListener;"); hapticListener = env->GetObjectField(*thisObj,hapticListenerFieldID); if(hapticListener == NULL){ die("failed to find hapticListener field id"); } /* --- SYNCHRONIZATION METHODS ID --- */ /* this.notify() */ notifyMethodID = env->GetMethodID(objectClass,"notify","()V"); if(notifyMethodID == NULL){ die("failed to find the notify method id"); } /* listener.wait() */ waitListenerMethodID = env->GetMethodID(objectClass,"wait","()V"); if(waitListenerMethodID == NULL){ die("failed to find the wait method id"); } messageToListenerMethodID = env->GetMethodID(hapticListenerClass,"addMessage","(Ljava/lang/String;Ljava/lang/String;I)V"); if(messageToListenerMethodID == NULL){ die("failed to find the addMessage method id"); } /* clean up local references */ env->DeleteLocalRef(hapticDeviceClass); env->DeleteLocalRef(hapticListenerClass); env->DeleteLocalRef(objectClass); } /* sends a message to the java thread */ void send(const Message &m){ jstring jstrCommand = env->NewStringUTF(m.command); jstring jstrArgs = env->NewStringUTF(m.args); env->CallVoidMethod(hapticListener,messageToListenerMethodID,jstrCommand,jstrArgs,m.ID); checkExceptions(env,"Could not call hapticListener.send()"); env->DeleteLocalRef(jstrCommand); env->DeleteLocalRef(jstrArgs); } void checkExceptions(JNIEnv *env, char* what){ if(env->ExceptionOccurred()){ std::cerr << "Exception occurred!!!" << std::endl; std::cerr << what << std::endl; env->ExceptionDescribe(); exit(-1); } } void die(char* msg){ std::cerr << msg << std::endl; exit(-1); } void Notify(jobject *monitor){ env->CallVoidMethod(*monitor,notifyMethodID); checkExceptions(env, "Could not call notify()"); } void MonitorEnter(jobject *monitor){ if(env->MonitorEnter(*monitor) != JNI_OK){ die("Could not allocate memory for monitor enter"); } } void MonitorExit(jobject *monitor){ if(env->MonitorExit(*monitor) != JNI_OK){ die("Could not release memory for monitor exit "); } }