00001 /*************************************************************************** 00002 *cr 00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the 00004 *cr University of Illinois 00005 *cr All Rights Reserved 00006 *cr 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * RCS INFORMATION: 00011 * 00012 * $RCSfile: Mouse.C,v $ 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.150 $ $Date: 2019年01月17日 21:21:00 $ 00015 * 00016 *************************************************************************** 00017 * DESCRIPTION: 00018 * 00019 * The Mouse UI object, which maintains the current state of the 00020 * mouse, including what it is currently used for, and how much it has moved 00021 * from one measurement to the next. This also deals with any pop-up or 00022 * pull-down menus available by using the mouse, as well as picking objects. 00023 * 00024 * A three-button mouse is assumed here, with the following usage: 00025 * 1) Buttons 1 and 2 : manipulation and picking. 00026 * 2) Button 3 (right): pop-up menu 00027 * 00028 * This is the general base class definition; specific versions for each 00029 * windowing/graphics system must be supplied. 00030 * 00031 ***************************************************************************/ 00032 00033 #include "Mouse.h" 00034 #include "DisplayDevice.h" 00035 #include "TextEvent.h" 00036 #include "CommandQueue.h" 00037 #include "Inform.h" 00038 #include "PickList.h" 00039 #include "VMDApp.h" 00040 00041 #include "VideoStream.h" // for video streaming event generation 00042 00043 // constructor 00044 Mouse::Mouse(VMDApp *vmdapp) : UIObject(vmdapp) { 00045 00046 #ifndef VMDGUI 00047 // tell the graphics display to queue events... 00048 // only if there is no GUI available 00049 app->display->queue_events(); // enable GUI events from graphics window 00050 #endif 00051 00052 // Initial detail level state would be set here 00053 00054 // set the default translation and rotation increments 00055 rocking_enabled = 1; 00056 transInc = 0.002f; 00057 rotInc = 1.0f/15.0f; 00058 scaleInc = 0.0002f; 00059 RotVelScale = 0.4f; 00060 currX = currY = oldX = oldY = 0; 00061 stop_rotation(); 00062 moveObj = 0; 00063 moveMode = ROTATION; 00064 pickInProgress = B_NONE; 00065 buttonDown = B_NONE; 00066 00067 #ifdef VMDVRJUGGLER 00068 // needed for VR Juggler patches 00069 if (app && app->display) 00070 app->display->set_cursor(DisplayDevice::NORMAL_CURSOR); // set normal cursor 00071 #else 00072 app->display->set_cursor(DisplayDevice::NORMAL_CURSOR); // set normal cursor 00073 #endif 00074 00075 reset(); 00076 } 00077 00078 00079 // destructor 00080 Mouse::~Mouse(void) { 00081 move_mode(ROTATION); 00082 } 00083 00084 const char* Mouse::get_mode_str(MoveMode mm) { 00085 const char* modestr; 00086 00087 switch (mm) { 00088 default: 00089 case ROTATION: 00090 modestr = "rotate"; break; // default mouse cursor 00091 case TRANSLATION: modestr = "translate"; break; 00092 case SCALING: modestr = "scale"; break; 00093 case LIGHT: modestr = "light"; break; 00094 case USERPOINT: modestr = "userpoint"; break; 00095 // All the picking modes follow: 00096 case PICK: modestr = "pick"; break; 00097 case QUERY: modestr = "query"; break; 00098 case CENTER: modestr = "center"; break; 00099 case LABELATOM: modestr = "labelatom"; break; 00100 case LABELBOND: modestr = "labelbond"; break; 00101 case LABELANGLE: modestr = "labelangle"; break; 00102 case LABELDIHEDRAL: modestr = "labeldihedral"; break; 00103 case MOVEATOM: modestr = "moveatom"; break; 00104 case MOVERES: modestr = "moveres"; break; 00105 case MOVEFRAG: modestr = "movefrag"; break; 00106 case MOVEMOL: modestr = "movemol"; break; 00107 case FORCEATOM: modestr = "forceatom"; break; 00108 case FORCERES: modestr = "forceres"; break; 00109 case FORCEFRAG: modestr = "forcefrag"; break; 00110 case MOVEREP: modestr = "moverep"; break; 00111 case ADDBOND: modestr = "addbond"; break; 00112 } 00113 00114 return modestr; 00115 } 00116 00118 00119 // stop rotation of object 00120 void Mouse::stop_rotation(void) { 00121 xRotVel = yRotVel = zRotVel = 0.0; // null out rotation rate 00122 } 00123 00124 // set the mouse move mode to the given state; return success 00125 int Mouse::move_mode(MoveMode mm, int mobj) { 00126 const char *modestr; 00127 00128 // we cannot change the mouse mode if an active pick is going on 00129 if (pickInProgress) 00130 return FALSE; // report failure 00131 00132 // stop rotating no matter what mode we've changed to 00133 stop_rotation(); 00134 00135 // disable light highlight if previous mode was light mode 00136 if (moveMode == LIGHT) { 00137 app->light_highlight(moveObj, FALSE); // turn off old light 00138 } 00139 00140 // special actions based on the new mode 00141 if (mm == LIGHT) { 00142 moveObj = mobj; 00143 app->light_highlight(moveObj, TRUE); // turn on new light number mobj 00144 } 00145 00146 // change the mouse mode now 00147 moveMode = mm; 00148 00149 // Tell the text interpreter the new mouse mode 00150 // Set the variable "vmd_mouse_mode" to the correct string 00151 // and set the variable "vmd_mouse_submode" to the mobj number 00152 modestr = get_mode_str(moveMode); 00153 runcommand(new MouseModeEvent(modestr, mobj)); // and set the variables 00154 00155 // set the cursor style to match the mouse mode 00156 switch (moveMode) { 00157 case ROTATION: 00158 case LIGHT: 00159 case USERPOINT: 00160 app->display->set_cursor(DisplayDevice::NORMAL_CURSOR); 00161 break; 00162 00163 case TRANSLATION: 00164 app->display->set_cursor(DisplayDevice::TRANS_CURSOR); 00165 break; 00166 00167 case SCALING: 00168 app->display->set_cursor(DisplayDevice::SCALE_CURSOR); 00169 break; 00170 00171 // all the remaining are picking modes 00172 default: 00173 app->display->set_cursor(DisplayDevice::PICK_CURSOR); 00174 break; 00175 } 00176 00177 return TRUE; // report success 00178 } 00179 00180 00181 00182 // do action when the mouse is moved 00183 // arg: which buttons are currently being pressed 00184 // return: whether the mouse moved any 00185 int Mouse::mouse_moved() { 00186 int dx, dy, mymouseMoved; 00187 int b1Down, b2Down; 00188 00189 b1Down = buttonDown == B_LEFT; 00190 00191 // in order to better support old machines, the built-in mouse 00192 // modes of VMD treat the middle and right mouse buttons identically 00193 b2Down = (buttonDown == B_MIDDLE || buttonDown == B_RIGHT); 00194 00195 if (b1Down || b2Down) { 00196 xRotVel = yRotVel = zRotVel = 0.0; // null out rotation rate 00197 } 00198 00199 // get current mouse position 00200 currX = app->display->x(); 00201 currY = app->display->y(); 00202 00203 // and calculate distance mouse has moved 00204 dx = 5 * (currX - oldX); 00205 dy = -5 * (currY - oldY); // negate Y coordinates 00206 00207 mymouseMoved = (dx != 0 || dy != 0); 00208 if (!mymouseMoved) 00209 return FALSE; // early-exit if nothing happened 00210 00211 // report the mouse location to TCL 00212 if (make_callbacks && !b1Down && !b2Down) { 00213 float r[2], oldr[2]; 00214 r[0] = (float) currX; 00215 r[1] = (float) currY; 00216 oldr[0] = (float) oldX; 00217 oldr[1] = (float) oldY; 00218 int tag; 00219 00220 app->display->rel_screen_pos(r[0], r[1]); 00221 app->display->rel_screen_pos(oldr[0], oldr[1]); 00222 if ((r[0] >= 0.0 && r[0] <= 1.0 && r[1] >= 0.0 && r[1] <= 1.0)) { 00223 // must be in the screen to do a pick! 00224 app->pickList->pick_check(2, r, tag, NULL, 0.01f, (char *)"mouse"); 00225 } else if (oldr[0] >= 0.0 && oldr[0] <= 1.0 && oldr[1] >= 0.0 && 00226 oldr[1] <= 1.0) { 00227 // but if we just moved out, inform TCL. 00228 app->commandQueue->runcommand(new PickAtomCallbackEvent(-1,-1,"mouse")); 00229 } 00230 } 00231 00232 00233 // save mouse coordinates for future reference 00234 oldX = currX; 00235 oldY = currY; 00236 00237 if (!b1Down && !b2Down) return FALSE; 00238 00239 // check if we are picking something; if so, generate pick-move command 00240 if (pickInProgress) { 00241 float mx = (float) currX; 00242 float my = (float) currY; 00243 app->display->rel_screen_pos(mx, my); 00244 if (mx >= 0.0 && mx <= 1.0 && my >= 0.0 && my <= 1.0) { 00245 float p[2]; 00246 p[0] = mx; 00247 p[1] = my; 00248 app->pickList->pick_move(p); 00249 } 00250 return TRUE; // report that the mouse has moved 00251 } 00252 00253 // Otherwise, if a button is pressed, check how far the mouse moved, 00254 // and transform the view accordingly. 00255 00256 #if defined(VMDXPLOR) 00257 // Mouse handling for VMD-XPLOR builds 00258 DisplayDevice* dispDev = app->display; 00259 // check for button 1 action 00260 if (b1Down) { 00261 if (dispDev->shift_state()==0 && 00262 (moveMode == ROTATION || moveMode == LIGHT || moveMode >= PICK)) { 00263 #else 00264 // Mouse handling for normal VMD builds 00265 // check for button 1 action 00266 if (b1Down) { 00267 if (moveMode == ROTATION || moveMode == LIGHT || moveMode >= PICK) { 00268 #endif 00269 xRotVel = rotInc * (float)dy; 00270 yRotVel = rotInc * (float)dx; 00271 if (moveMode == ROTATION || moveMode >= PICK) { 00272 // rotate the scene 00273 if (xRotVel != 0.0) { 00274 app->scene_rotate_by(xRotVel, 'x'); 00275 xRotVel *= RotVelScale; 00276 } 00277 if (yRotVel != 0.0) { 00278 app->scene_rotate_by(yRotVel, 'y'); 00279 yRotVel *= RotVelScale; 00280 } 00281 } else { 00282 // rotate a particular light 00283 if (xRotVel != 0.0) { 00284 app->light_rotate(moveObj, xRotVel, 'x'); 00285 xRotVel *= RotVelScale; 00286 } 00287 if (yRotVel != 0.0) { 00288 app->light_rotate(moveObj, yRotVel, 'y'); 00289 yRotVel *= RotVelScale; 00290 } 00291 } 00292 00293 #if defined(VMDXPLOR) 00294 // Mouse handling for VMD-XPLOR builds 00295 } else if ((dispDev->shift_state() & DisplayDevice::SHIFT || 00296 moveMode == TRANSLATION) && mymouseMoved) { 00297 app->scene_translate_by(transInc*(float)dx, -transInc*(float)dy, 0.0); 00298 } else if ((dispDev->shift_state() & DisplayDevice::CONTROL || 00299 moveMode == SCALING) && dx != 0) { 00300 #else 00301 // Mouse handling for normal VMD builds 00302 } else if (moveMode == TRANSLATION && mymouseMoved) { 00303 app->scene_translate_by(transInc*(float)dx, -transInc*(float)dy, 0.0); 00304 } else if (moveMode == SCALING && dx != 0) { 00305 #endif 00306 float scf = scaling + scaleInc * (float)dx; 00307 if(scf < 0.0) 00308 scf = 0.0; 00309 app->scene_scale_by(scf); 00310 } 00311 } 00312 00313 // check for button 2 action 00314 if (b2Down) { 00315 #if defined(VMDXPLOR) 00316 // Mouse handling for VMD-XPLOR builds 00317 if (dispDev->shift_state()==0 && 00318 (moveMode == ROTATION || moveMode == LIGHT || moveMode >= PICK)) { 00319 #else 00320 // Mouse handling for normal VMD builds 00321 if (moveMode == ROTATION || moveMode == LIGHT || moveMode >= PICK) { 00322 #endif 00323 zRotVel = rotInc * (float)dx; 00324 if (moveMode == ROTATION || moveMode >= PICK) { 00325 if (zRotVel != 0.0) { 00326 app->scene_rotate_by(zRotVel, 'z'); 00327 zRotVel *= RotVelScale; 00328 } 00329 } else { 00330 if (zRotVel != 0.0) { 00331 app->light_rotate(moveObj, zRotVel, 'z'); 00332 zRotVel *= RotVelScale; 00333 } 00334 } 00335 #if defined(VMDXPLOR) 00336 // Mouse handling for VMD-XPLOR builds 00337 } else if ((dispDev->shift_state() & DisplayDevice::SHIFT || 00338 moveMode == TRANSLATION) && dx != 0) { 00339 app->scene_translate_by(0.0, 0.0, transInc * (float)dx); 00340 } else if ((dispDev->shift_state() & DisplayDevice::CONTROL || 00341 moveMode == SCALING) && dx != 0) { 00342 #else 00343 // Mouse handling for normal VMD builds 00344 } else if(moveMode == TRANSLATION && dx != 0) { 00345 app->scene_translate_by(0.0, 0.0, transInc * (float)dx); 00346 } else if(moveMode == SCALING && dx != 0) { 00347 #endif 00348 float scf = scaling + 10.0f * scaleInc * (float)dx; 00349 if(scf < 0.0) 00350 scf = 0.0; 00351 app->scene_scale_by(scf); 00352 } 00353 } 00354 00355 return TRUE; // report that the mouse has moved 00356 } 00357 00358 00359 00360 // mouse mode for special navigation/flying plugins, 00361 // reports the mouse location and button state to TCL callbacks 00362 int Mouse::mouse_userpoint() { 00363 float mpos[2]; 00364 00365 xRotVel = yRotVel = zRotVel = 0.0; // null out rotation rate 00366 00367 // get current mouse position 00368 currX = app->display->x(); 00369 currY = app->display->y(); 00370 00371 mpos[0] = (float) currX; 00372 mpos[1] = (float) currY; 00373 00374 app->display->rel_screen_pos(mpos[0], mpos[1]); 00375 00376 // inform TCL 00377 app->commandQueue->runcommand(new MousePositionEvent(mpos[0], mpos[1], buttonDown)); 00378 00379 // save mouse coordinates for future reference 00380 oldX = currX; 00381 oldY = currY; 00382 00383 // Nothing happened for VMD to worry about for changing detail levels etc, 00384 // the user's Tcl callback will have to deal with this. 00385 return FALSE; 00386 } 00387 00388 00389 00391 00392 // reset the mouse to original settings 00393 void Mouse::reset(void) { 00394 scaling = 1.0; 00395 stop_rotation(); 00396 currX = oldX = app->display->x(); 00397 currY = oldY = app->display->y(); 00398 } 00399 00400 void Mouse::handle_winevent(long dev, long val) { 00401 switch(dev) { 00402 case DisplayDevice::WIN_WHEELUP: 00403 app->scene_scale_by(1.200f); // mouse wheel up scales up 00404 break; 00405 00406 case DisplayDevice::WIN_WHEELDOWN: 00407 app->scene_scale_by(0.833f); // mouse wheel down scales down 00408 break; 00409 00410 case DisplayDevice::WIN_LEFT: 00411 case DisplayDevice::WIN_MIDDLE: 00412 case DisplayDevice::WIN_RIGHT: 00413 if (val == 1 && buttonDown == B_NONE) { 00414 // start of a fresh button down event. 00415 xRotVel = yRotVel = zRotVel = 0.0; // null out rotation rate 00416 00417 oldX = currX = app->display->x(); // save current mouse coords 00418 oldY = currY = app->display->y(); 00419 00420 if (dev == DisplayDevice::WIN_LEFT) 00421 buttonDown = B_LEFT; 00422 else if (dev == DisplayDevice::WIN_MIDDLE) 00423 buttonDown = B_MIDDLE; 00424 else 00425 buttonDown = B_RIGHT; 00426 00427 // check for a picked item if we are in a picking mode 00428 if ( moveMode >= PICK && ! pickInProgress) { 00429 pickInProgress = buttonDown; 00430 float mx = (float) currX; 00431 float my = (float) currY; 00432 app->display->rel_screen_pos(mx, my); 00433 00434 // if picking an object fails, assume we are rotating the object 00435 float p[2]; 00436 p[0] = mx; 00437 p[1] = my; 00438 if (app->pickList->pick_start(pickInProgress, 2, p) < 0) 00439 pickInProgress = B_NONE; 00440 } 00441 } else if (val == 0 && buttonDown != B_NONE) { 00442 // we're done moving the mouse while the button is down 00443 if (pickInProgress) { 00444 pickInProgress = B_NONE; 00445 app->pickList->pick_end(); // must finish the picking process 00446 } 00447 00448 // Would return to previous detail level here 00449 buttonDown = B_NONE; 00450 } 00451 break; 00452 00453 case DisplayDevice::WIN_KBD: 00454 case DisplayDevice::WIN_KBD_ESCAPE: 00455 case DisplayDevice::WIN_KBD_UP: 00456 case DisplayDevice::WIN_KBD_DOWN: 00457 case DisplayDevice::WIN_KBD_LEFT: 00458 case DisplayDevice::WIN_KBD_RIGHT: 00459 case DisplayDevice::WIN_KBD_PAGE_UP: 00460 case DisplayDevice::WIN_KBD_PAGE_DOWN: 00461 case DisplayDevice::WIN_KBD_HOME: 00462 case DisplayDevice::WIN_KBD_END: 00463 case DisplayDevice::WIN_KBD_INSERT: 00464 case DisplayDevice::WIN_KBD_DELETE: 00465 case DisplayDevice::WIN_KBD_F1: 00466 case DisplayDevice::WIN_KBD_F2: 00467 case DisplayDevice::WIN_KBD_F3: 00468 case DisplayDevice::WIN_KBD_F4: 00469 case DisplayDevice::WIN_KBD_F5: 00470 case DisplayDevice::WIN_KBD_F6: 00471 case DisplayDevice::WIN_KBD_F7: 00472 case DisplayDevice::WIN_KBD_F8: 00473 case DisplayDevice::WIN_KBD_F9: 00474 case DisplayDevice::WIN_KBD_F10: 00475 case DisplayDevice::WIN_KBD_F11: 00476 case DisplayDevice::WIN_KBD_F12: 00477 // if we are a video streaming client, graphics window 00478 // keyboard events are sent to the server and are not 00479 // interpreted locally 00480 if (app->uivs && app->uivs->cli_connected()) { 00481 app->uivs->cli_send_keyboard(dev, (char) val, app->display->shift_state()); 00482 } else { 00483 runcommand(new UserKeyEvent((DisplayDevice::EventCodes) dev, (char) val, app->display->shift_state())); 00484 } 00485 break; 00486 00487 default: 00488 ; // ignore other events and just return 00489 } // switch 00490 } 00491 00492 void Mouse::set_rocking(int on) { 00493 rocking_enabled = on; 00494 if (!on) { 00495 xRotVel = yRotVel = zRotVel = 0; // null out rotation rate 00496 } 00497 } 00498 00499 // check for and queue events. Return TRUE if an event was generated. 00500 int Mouse::check_event(void) { 00501 int retval = FALSE; 00502 long dev=0, val=0; // we check for events ourselves 00503 00504 if ((retval = app->display->read_event(dev, val))) 00505 handle_winevent(dev, val); 00506 00507 if (moveMode == USERPOINT) { 00508 mouse_userpoint(); // user-defined mouse behavior 00509 } else if (make_callbacks || buttonDown != B_NONE) { 00510 if (mouse_moved()) { 00511 // Change to alternate detail level here... 00512 } 00513 } 00514 00515 // don't perform auto-rotation a remote video stream client at present 00516 if (rocking_enabled && !(app->uivs && app->uivs->cli_connected())) { 00517 // apply ang velocity, if necessary 00518 if (xRotVel != 0.0 || yRotVel != 0.0 || zRotVel != 0.0) { 00519 if (moveMode != LIGHT) { // (possibly) rotate app->scene 00520 if (xRotVel != 0.0) 00521 app->scene_rotate_by(xRotVel, 'x'); 00522 if (yRotVel != 0.0) 00523 app->scene_rotate_by(yRotVel, 'y'); 00524 if (zRotVel != 0.0) 00525 app->scene_rotate_by(zRotVel, 'z'); 00526 } else { // (possibly) rotate particular light 00527 if (xRotVel != 0.0) 00528 app->light_rotate(moveObj, xRotVel, 'x'); 00529 if (yRotVel != 0.0) 00530 app->light_rotate(moveObj, yRotVel, 'y'); 00531 if (zRotVel != 0.0) 00532 app->light_rotate(moveObj, zRotVel, 'z'); 00533 } 00534 } 00535 } 00536 return retval; 00537 } 00538 00539 00540