1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19*/ 20// in_win.c -- windows 95 mouse and joystick code 21// 02/21/97 JCB Added extended DirectInput code to support external controllers. 22 23#include <dinput.h> 24#include "quakedef.h" 25#include "winquake.h" 26//#include "dosisms.h" 27 28#define DINPUT_BUFFERSIZE 16 29#define iDirectInputCreate(a,b,c,d) pDirectInputCreate(a,b,c,d) 30 31HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, 32 LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter); 33 34// mouse variables 35cvar_t m_filter = {"m_filter","0"}; 36 37int mouse_buttons; 38int mouse_oldbuttonstate; 39POINT current_pos; 40int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum; 41 42static qboolean restore_spi; 43static int originalmouseparms[3], newmouseparms[3] = {0, 0, 1}; 44qboolean mouseinitialized; 45static qboolean mouseparmsvalid, mouseactivatetoggle; 46static qboolean mouseshowtoggle = 1; 47static qboolean dinput_acquired; 48static unsigned int mstate_di; 49unsigned int uiWheelMessage; 50 51qboolean mouseactive; 52 53// joystick defines and variables 54// where should defines be moved? 55#define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick 56#define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball 57#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V 58#define JOY_AXIS_X 0 59#define JOY_AXIS_Y 1 60#define JOY_AXIS_Z 2 61#define JOY_AXIS_R 3 62#define JOY_AXIS_U 4 63#define JOY_AXIS_V 5 64 65enum _ControlList 66{ 67 AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn 68}; 69 70DWORD dwAxisFlags[JOY_MAX_AXES] = 71{ 72 JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV 73}; 74 75DWORD dwAxisMap[JOY_MAX_AXES]; 76DWORD dwControlMap[JOY_MAX_AXES]; 77PDWORD pdwRawValue[JOY_MAX_AXES]; 78 79// none of these cvars are saved over a session 80// this means that advanced controller configuration needs to be executed 81// each time. this avoids any problems with getting back to a default usage 82// or when changing from one controller to another. this way at least something 83// works. 84cvar_t in_joystick = {"joystick","0", true}; 85cvar_t joy_name = {"joyname", "joystick"}; 86cvar_t joy_advanced = {"joyadvanced", "0"}; 87cvar_t joy_advaxisx = {"joyadvaxisx", "0"}; 88cvar_t joy_advaxisy = {"joyadvaxisy", "0"}; 89cvar_t joy_advaxisz = {"joyadvaxisz", "0"}; 90cvar_t joy_advaxisr = {"joyadvaxisr", "0"}; 91cvar_t joy_advaxisu = {"joyadvaxisu", "0"}; 92cvar_t joy_advaxisv = {"joyadvaxisv", "0"}; 93cvar_t joy_forwardthreshold = {"joyforwardthreshold", "0.15"}; 94cvar_t joy_sidethreshold = {"joysidethreshold", "0.15"}; 95cvar_t joy_pitchthreshold = {"joypitchthreshold", "0.15"}; 96cvar_t joy_yawthreshold = {"joyyawthreshold", "0.15"}; 97cvar_t joy_forwardsensitivity = {"joyforwardsensitivity", "-1.0"}; 98cvar_t joy_sidesensitivity = {"joysidesensitivity", "-1.0"}; 99cvar_t joy_pitchsensitivity = {"joypitchsensitivity", "1.0"}; 100cvar_t joy_yawsensitivity = {"joyyawsensitivity", "-1.0"}; 101cvar_t joy_wwhack1 = {"joywwhack1", "0.0"}; 102cvar_t joy_wwhack2 = {"joywwhack2", "0.0"}; 103 104qboolean joy_avail, joy_advancedinit, joy_haspov; 105DWORD joy_oldbuttonstate, joy_oldpovstate; 106 107int joy_id; 108DWORD joy_flags; 109DWORD joy_numbuttons; 110 111static LPDIRECTINPUT g_pdi; 112static LPDIRECTINPUTDEVICE g_pMouse; 113 114static JOYINFOEX ji; 115 116static HINSTANCE hInstDI; 117 118static qboolean dinput; 119 120typedef struct MYDATA { 121 LONG lX; // X axis goes here 122 LONG lY; // Y axis goes here 123 LONG lZ; // Z axis goes here 124 BYTE bButtonA; // One button goes here 125 BYTE bButtonB; // Another button goes here 126 BYTE bButtonC; // Another button goes here 127 BYTE bButtonD; // Another button goes here 128} MYDATA; 129 130static DIOBJECTDATAFORMAT rgodf[] = { 131 { &GUID_XAxis, FIELD_OFFSET(MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, 132 { &GUID_YAxis, FIELD_OFFSET(MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, 133 { &GUID_ZAxis, FIELD_OFFSET(MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, 134 { 0, FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, 135 { 0, FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, 136 { 0, FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, 137 { 0, FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, 138}; 139 140#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0])) 141 142static DIDATAFORMAT df = { 143 sizeof(DIDATAFORMAT), // this structure 144 sizeof(DIOBJECTDATAFORMAT), // size of object data format 145 DIDF_RELAXIS, // absolute axis coordinates 146 sizeof(MYDATA), // device data size 147 NUM_OBJECTS, // number of objects 148 rgodf, // and here they are 149}; 150 151// forward-referenced functions 152void IN_StartupJoystick (void); 153void Joy_AdvancedUpdate_f (void); 154void IN_JoyMove (usercmd_t *cmd); 155 156 157/* 158=========== 159Force_CenterView_f 160=========== 161*/ 162void Force_CenterView_f (void) 163{ 164 cl.viewangles[PITCH] = 0; 165} 166 167 168/* 169=========== 170IN_UpdateClipCursor 171=========== 172*/ 173void IN_UpdateClipCursor (void) 174{ 175 176 if (mouseinitialized && mouseactive && !dinput) 177 { 178 ClipCursor (&window_rect); 179 } 180} 181 182 183/* 184=========== 185IN_ShowMouse 186=========== 187*/ 188void IN_ShowMouse (void) 189{ 190 191 if (!mouseshowtoggle) 192 { 193 ShowCursor (TRUE); 194 mouseshowtoggle = 1; 195 } 196} 197 198 199/* 200=========== 201IN_HideMouse 202=========== 203*/ 204void IN_HideMouse (void) 205{ 206 207 if (mouseshowtoggle) 208 { 209 ShowCursor (FALSE); 210 mouseshowtoggle = 0; 211 } 212} 213 214 215/* 216=========== 217IN_ActivateMouse 218=========== 219*/ 220void IN_ActivateMouse (void) 221{ 222 223 mouseactivatetoggle = true; 224 225 if (mouseinitialized) 226 { 227 if (dinput) 228 { 229 if (g_pMouse) 230 { 231 if (!dinput_acquired) 232 { 233 IDirectInputDevice_Acquire(g_pMouse); 234 dinput_acquired = true; 235 } 236 } 237 else 238 { 239 return; 240 } 241 } 242 else 243 { 244 if (mouseparmsvalid) 245 restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); 246 247 SetCursorPos (window_center_x, window_center_y); 248 SetCapture (mainwindow); 249 ClipCursor (&window_rect); 250 } 251 252 mouseactive = true; 253 } 254} 255 256 257/* 258=========== 259IN_SetQuakeMouseState 260=========== 261*/ 262void IN_SetQuakeMouseState (void) 263{ 264 if (mouseactivatetoggle) 265 IN_ActivateMouse (); 266} 267 268 269/* 270=========== 271IN_DeactivateMouse 272=========== 273*/ 274void IN_DeactivateMouse (void) 275{ 276 277 mouseactivatetoggle = false; 278 279 if (mouseinitialized) 280 { 281 if (dinput) 282 { 283 if (g_pMouse) 284 { 285 if (dinput_acquired) 286 { 287 IDirectInputDevice_Unacquire(g_pMouse); 288 dinput_acquired = false; 289 } 290 } 291 } 292 else 293 { 294 if (restore_spi) 295 SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); 296 297 ClipCursor (NULL); 298 ReleaseCapture (); 299 } 300 301 mouseactive = false; 302 } 303} 304 305 306/* 307=========== 308IN_RestoreOriginalMouseState 309=========== 310*/ 311void IN_RestoreOriginalMouseState (void) 312{ 313 if (mouseactivatetoggle) 314 { 315 IN_DeactivateMouse (); 316 mouseactivatetoggle = true; 317 } 318 319// try to redraw the cursor so it gets reinitialized, because sometimes it 320// has garbage after the mode switch 321 ShowCursor (TRUE); 322 ShowCursor (FALSE); 323} 324 325 326/* 327=========== 328IN_InitDInput 329=========== 330*/ 331qboolean IN_InitDInput (void) 332{ 333 HRESULT hr; 334 DIPROPDWORD dipdw = { 335 { 336 sizeof(DIPROPDWORD), // diph.dwSize 337 sizeof(DIPROPHEADER), // diph.dwHeaderSize 338 0, // diph.dwObj 339 DIPH_DEVICE, // diph.dwHow 340 }, 341 DINPUT_BUFFERSIZE, // dwData 342 }; 343 344 if (!hInstDI) 345 { 346 hInstDI = LoadLibrary("dinput.dll"); 347 348 if (hInstDI == NULL) 349 { 350 Con_SafePrintf ("Couldn't load dinput.dll\n"); 351 return false; 352 } 353 } 354 355 if (!pDirectInputCreate) 356 { 357 pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA"); 358 359 if (!pDirectInputCreate) 360 { 361 Con_SafePrintf ("Couldn't get DI proc addr\n"); 362 return false; 363 } 364 } 365 366// register with DirectInput and get an IDirectInput to play with. 367 hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL); 368 369 if (FAILED(hr)) 370 { 371 return false; 372 } 373 374// obtain an interface to the system mouse device. 375 hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL); 376 377 if (FAILED(hr)) 378 { 379 Con_SafePrintf ("Couldn't open DI mouse device\n"); 380 return false; 381 } 382 383// set the data format to "mouse format". 384 hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df); 385 386 if (FAILED(hr)) 387 { 388 Con_SafePrintf ("Couldn't set DI mouse format\n"); 389 return false; 390 } 391 392// set the cooperativity level. 393 hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow, 394 DISCL_EXCLUSIVE | DISCL_FOREGROUND); 395 396 if (FAILED(hr)) 397 { 398 Con_SafePrintf ("Couldn't set DI coop level\n"); 399 return false; 400 } 401 402 403// set the buffer size to DINPUT_BUFFERSIZE elements. 404// the buffer size is a DWORD property associated with the device 405 hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph); 406 407 if (FAILED(hr)) 408 { 409 Con_SafePrintf ("Couldn't set DI buffersize\n"); 410 return false; 411 } 412 413 return true; 414} 415 416 417/* 418=========== 419IN_StartupMouse 420=========== 421*/ 422void IN_StartupMouse (void) 423{ 424 HDC hdc; 425 426 if ( COM_CheckParm ("-nomouse") ) 427 return; 428 429 mouseinitialized = true; 430 431 if (COM_CheckParm ("-dinput")) 432 { 433 dinput = IN_InitDInput (); 434 435 if (dinput) 436 { 437 Con_SafePrintf ("DirectInput initialized\n"); 438 } 439 else 440 { 441 Con_SafePrintf ("DirectInput not initialized\n"); 442 } 443 } 444 445 if (!dinput) 446 { 447 mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); 448 449 if (mouseparmsvalid) 450 { 451 if ( COM_CheckParm ("-noforcemspd") ) 452 newmouseparms[2] = originalmouseparms[2]; 453 454 if ( COM_CheckParm ("-noforcemaccel") ) 455 { 456 newmouseparms[0] = originalmouseparms[0]; 457 newmouseparms[1] = originalmouseparms[1]; 458 } 459 460 if ( COM_CheckParm ("-noforcemparms") ) 461 { 462 newmouseparms[0] = originalmouseparms[0]; 463 newmouseparms[1] = originalmouseparms[1]; 464 newmouseparms[2] = originalmouseparms[2]; 465 } 466 } 467 } 468 469 mouse_buttons = 3; 470 471// if a fullscreen video mode was set before the mouse was initialized, 472// set the mouse state appropriately 473 if (mouseactivatetoggle) 474 IN_ActivateMouse (); 475} 476 477 478/* 479=========== 480IN_Init 481=========== 482*/ 483void IN_Init (void) 484{ 485 // mouse variables 486 Cvar_RegisterVariable (&m_filter); 487 488 // joystick variables 489 Cvar_RegisterVariable (&in_joystick); 490 Cvar_RegisterVariable (&joy_name); 491 Cvar_RegisterVariable (&joy_advanced); 492 Cvar_RegisterVariable (&joy_advaxisx); 493 Cvar_RegisterVariable (&joy_advaxisy); 494 Cvar_RegisterVariable (&joy_advaxisz); 495 Cvar_RegisterVariable (&joy_advaxisr); 496 Cvar_RegisterVariable (&joy_advaxisu); 497 Cvar_RegisterVariable (&joy_advaxisv); 498 Cvar_RegisterVariable (&joy_forwardthreshold); 499 Cvar_RegisterVariable (&joy_sidethreshold); 500 Cvar_RegisterVariable (&joy_pitchthreshold); 501 Cvar_RegisterVariable (&joy_yawthreshold); 502 Cvar_RegisterVariable (&joy_forwardsensitivity); 503 Cvar_RegisterVariable (&joy_sidesensitivity); 504 Cvar_RegisterVariable (&joy_pitchsensitivity); 505 Cvar_RegisterVariable (&joy_yawsensitivity); 506 Cvar_RegisterVariable (&joy_wwhack1); 507 Cvar_RegisterVariable (&joy_wwhack2); 508 509 Cmd_AddCommand ("force_centerview", Force_CenterView_f); 510 Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f); 511 512 uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" ); 513 514 IN_StartupMouse (); 515 IN_StartupJoystick (); 516} 517 518/* 519=========== 520IN_Shutdown 521=========== 522*/ 523void IN_Shutdown (void) 524{ 525 526 IN_DeactivateMouse (); 527 IN_ShowMouse (); 528 529 if (g_pMouse) 530 { 531 IDirectInputDevice_Release(g_pMouse); 532 g_pMouse = NULL; 533 } 534 535 if (g_pdi) 536 { 537 IDirectInput_Release(g_pdi); 538 g_pdi = NULL; 539 } 540} 541 542 543/* 544=========== 545IN_MouseEvent 546=========== 547*/ 548void IN_MouseEvent (int mstate) 549{ 550 int i; 551 552 if (mouseactive && !dinput) 553 { 554 // perform button actions 555 for (i=0 ; i<mouse_buttons ; i++) 556 { 557 if ( (mstate & (1<<i)) && 558 !(mouse_oldbuttonstate & (1<<i)) ) 559 { 560 Key_Event (K_MOUSE1 + i, true); 561 } 562 563 if ( !(mstate & (1<<i)) && 564 (mouse_oldbuttonstate & (1<<i)) ) 565 { 566 Key_Event (K_MOUSE1 + i, false); 567 } 568 } 569 570 mouse_oldbuttonstate = mstate; 571 } 572} 573 574 575/* 576=========== 577IN_MouseMove 578=========== 579*/ 580void IN_MouseMove (usercmd_t *cmd) 581{ 582 int mx, my; 583 HDC hdc; 584 int i; 585 DIDEVICEOBJECTDATA od; 586 DWORD dwElements; 587 HRESULT hr; 588 589 if (!mouseactive) 590 return; 591 592 if (dinput) 593 { 594 mx = 0; 595 my = 0; 596 597 for (;;) 598 { 599 dwElements = 1; 600 601 hr = IDirectInputDevice_GetDeviceData(g_pMouse, 602 sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0); 603 604 if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) 605 { 606 dinput_acquired = true; 607 IDirectInputDevice_Acquire(g_pMouse); 608 break; 609 } 610 611 /* Unable to read data or no data available */ 612 if (FAILED(hr) || dwElements == 0) 613 { 614 break; 615 } 616 617 /* Look at the element to see what happened */ 618 619 switch (od.dwOfs) 620 { 621 case DIMOFS_X: 622 mx += od.dwData; 623 break; 624 625 case DIMOFS_Y: 626 my += od.dwData; 627 break; 628 629 case DIMOFS_BUTTON0: 630 if (od.dwData & 0x80) 631 mstate_di |= 1; 632 else 633 mstate_di &= ~1; 634 break; 635 636 case DIMOFS_BUTTON1: 637 if (od.dwData & 0x80) 638 mstate_di |= (1<<1); 639 else 640 mstate_di &= ~(1<<1); 641 break; 642 643 case DIMOFS_BUTTON2: 644 if (od.dwData & 0x80) 645 mstate_di |= (1<<2); 646 else 647 mstate_di &= ~(1<<2); 648 break; 649 } 650 } 651 652 // perform button actions 653 for (i=0 ; i<mouse_buttons ; i++) 654 { 655 if ( (mstate_di & (1<<i)) && 656 !(mouse_oldbuttonstate & (1<<i)) ) 657 { 658 Key_Event (K_MOUSE1 + i, true); 659 } 660 661 if ( !(mstate_di & (1<<i)) && 662 (mouse_oldbuttonstate & (1<<i)) ) 663 { 664 Key_Event (K_MOUSE1 + i, false); 665 } 666 } 667 668 mouse_oldbuttonstate = mstate_di; 669 } 670 else 671 { 672 GetCursorPos (¤t_pos); 673 mx = current_pos.x - window_center_x + mx_accum; 674 my = current_pos.y - window_center_y + my_accum; 675 mx_accum = 0; 676 my_accum = 0; 677 } 678 679 if (m_filter.value) 680 { 681 mouse_x = (mx + old_mouse_x) * 0.5; 682 mouse_y = (my + old_mouse_y) * 0.5; 683 } 684 else 685 { 686 mouse_x = mx; 687 mouse_y = my; 688 } 689 690 old_mouse_x = mx; 691 old_mouse_y = my; 692 693 mouse_x *= sensitivity.value; 694 mouse_y *= sensitivity.value; 695 696// add mouse X/Y movement to cmd 697 if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) 698 cmd->sidemove += m_side.value * mouse_x; 699 else 700 cl.viewangles[YAW] -= m_yaw.value * mouse_x; 701 702 if (in_mlook.state & 1) 703 V_StopPitchDrift (); 704 705 if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) 706 { 707 cl.viewangles[PITCH] += m_pitch.value * mouse_y; 708 if (cl.viewangles[PITCH] > 80) 709 cl.viewangles[PITCH] = 80; 710 if (cl.viewangles[PITCH] < -70) 711 cl.viewangles[PITCH] = -70; 712 } 713 else 714 { 715 if ((in_strafe.state & 1) && noclip_anglehack) 716 cmd->upmove -= m_forward.value * mouse_y; 717 else 718 cmd->forwardmove -= m_forward.value * mouse_y; 719 } 720 721// if the mouse has moved, force it to the center, so there's room to move 722 if (mx || my) 723 { 724 SetCursorPos (window_center_x, window_center_y); 725 } 726} 727 728 729/* 730=========== 731IN_Move 732=========== 733*/ 734void IN_Move (usercmd_t *cmd) 735{ 736 737 if (ActiveApp && !Minimized) 738 { 739 IN_MouseMove (cmd); 740 IN_JoyMove (cmd); 741 } 742} 743 744 745/* 746=========== 747IN_Accumulate 748=========== 749*/ 750void IN_Accumulate (void) 751{ 752 int mx, my; 753 HDC hdc; 754 755 if (mouseactive) 756 { 757 GetCursorPos (¤t_pos); 758 759 mx_accum += current_pos.x - window_center_x; 760 my_accum += current_pos.y - window_center_y; 761 762 // force the mouse to the center, so there's room to move 763 SetCursorPos (window_center_x, window_center_y); 764 } 765} 766 767 768/* 769=================== 770IN_ClearStates 771=================== 772*/ 773void IN_ClearStates (void) 774{ 775 776 if (mouseactive) 777 { 778 mx_accum = 0; 779 my_accum = 0; 780 mouse_oldbuttonstate = 0; 781 } 782} 783 784 785/* 786=============== 787IN_StartupJoystick 788=============== 789*/ 790void IN_StartupJoystick (void) 791{ 792 int i, numdevs; 793 JOYCAPS jc; 794 MMRESULT mmr; 795 796 // assume no joystick 797 joy_avail = false; 798 799 // abort startup if user requests no joystick 800 if ( COM_CheckParm ("-nojoy") ) 801 return; 802 803 // verify joystick driver is present 804 if ((numdevs = joyGetNumDevs ()) == 0) 805 { 806 Con_Printf ("\njoystick not found -- driver not present\n\n"); 807 return; 808 } 809 810 // cycle through the joystick ids for the first valid one 811 for (joy_id=0 ; joy_id<numdevs ; joy_id++) 812 { 813 memset (&ji, 0, sizeof(ji)); 814 ji.dwSize = sizeof(ji); 815 ji.dwFlags = JOY_RETURNCENTERED; 816 817 if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR) 818 break; 819 } 820 821 // abort startup if we didn't find a valid joystick 822 if (mmr != JOYERR_NOERROR) 823 { 824 Con_Printf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr); 825 return; 826 } 827 828 // get the capabilities of the selected joystick 829 // abort startup if command fails 830 memset (&jc, 0, sizeof(jc)); 831 if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR) 832 { 833 Con_Printf ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr); 834 return; 835 } 836 837 // save the joystick's number of buttons and POV status 838 joy_numbuttons = jc.wNumButtons; 839 joy_haspov = jc.wCaps & JOYCAPS_HASPOV; 840 841 // old button and POV states default to no buttons pressed 842 joy_oldbuttonstate = joy_oldpovstate = 0; 843 844 // mark the joystick as available and advanced initialization not completed 845 // this is needed as cvars are not available during initialization 846 847 joy_avail = true; 848 joy_advancedinit = false; 849 850 Con_Printf ("\njoystick detected\n\n"); 851} 852 853 854/* 855=========== 856RawValuePointer 857=========== 858*/ 859PDWORD RawValuePointer (int axis) 860{ 861 switch (axis) 862 { 863 case JOY_AXIS_X: 864 return &ji.dwXpos; 865 case JOY_AXIS_Y: 866 return &ji.dwYpos; 867 case JOY_AXIS_Z: 868 return &ji.dwZpos; 869 case JOY_AXIS_R: 870 return &ji.dwRpos; 871 case JOY_AXIS_U: 872 return &ji.dwUpos; 873 case JOY_AXIS_V: 874 return &ji.dwVpos; 875 } 876} 877 878 879/* 880=========== 881Joy_AdvancedUpdate_f 882=========== 883*/ 884void Joy_AdvancedUpdate_f (void) 885{ 886 887 // called once by IN_ReadJoystick and by user whenever an update is needed 888 // cvars are now available 889 int i; 890 DWORD dwTemp; 891 892 // initialize all the maps 893 for (i = 0; i < JOY_MAX_AXES; i++) 894 { 895 dwAxisMap[i] = AxisNada; 896 dwControlMap[i] = JOY_ABSOLUTE_AXIS; 897 pdwRawValue[i] = RawValuePointer(i); 898 } 899 900 if( joy_advanced.value == 0.0) 901 { 902 // default joystick initialization 903 // 2 axes only with joystick control 904 dwAxisMap[JOY_AXIS_X] = AxisTurn; 905 // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS; 906 dwAxisMap[JOY_AXIS_Y] = AxisForward; 907 // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; 908 } 909 else 910 { 911 if (Q_strcmp (joy_name.string, "joystick") != 0) 912 { 913 // notify user of advanced controller 914 Con_Printf ("\n%s configured\n\n", joy_name.string); 915 } 916 917 // advanced initialization here 918 // data supplied by user via joy_axisn cvars 919 dwTemp = (DWORD) joy_advaxisx.value; 920 dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; 921 dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; 922 dwTemp = (DWORD) joy_advaxisy.value; 923 dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; 924 dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; 925 dwTemp = (DWORD) joy_advaxisz.value; 926 dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; 927 dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; 928 dwTemp = (DWORD) joy_advaxisr.value; 929 dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; 930 dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; 931 dwTemp = (DWORD) joy_advaxisu.value; 932 dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; 933 dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; 934 dwTemp = (DWORD) joy_advaxisv.value; 935 dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; 936 dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; 937 } 938 939 // compute the axes to collect from DirectInput 940 joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; 941 for (i = 0; i < JOY_MAX_AXES; i++) 942 { 943 if (dwAxisMap[i] != AxisNada) 944 { 945 joy_flags |= dwAxisFlags[i]; 946 } 947 } 948} 949 950 951/* 952=========== 953IN_Commands 954=========== 955*/ 956void IN_Commands (void) 957{ 958 int i, key_index; 959 DWORD buttonstate, povstate; 960 961 if (!joy_avail) 962 { 963 return; 964 } 965 966 967 // loop through the joystick buttons 968 // key a joystick event or auxillary event for higher number buttons for each state change 969 buttonstate = ji.dwButtons; 970 for (i=0 ; i < joy_numbuttons ; i++) 971 { 972 if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) ) 973 { 974 key_index = (i < 4) ? K_JOY1 : K_AUX1; 975 Key_Event (key_index + i, true); 976 } 977 978 if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) ) 979 { 980 key_index = (i < 4) ? K_JOY1 : K_AUX1; 981 Key_Event (key_index + i, false); 982 } 983 } 984 joy_oldbuttonstate = buttonstate; 985 986 if (joy_haspov) 987 { 988 // convert POV information into 4 bits of state information 989 // this avoids any potential problems related to moving from one 990 // direction to another without going through the center position 991 povstate = 0; 992 if(ji.dwPOV != JOY_POVCENTERED) 993 { 994 if (ji.dwPOV == JOY_POVFORWARD) 995 povstate |= 0x01; 996 if (ji.dwPOV == JOY_POVRIGHT) 997 povstate |= 0x02; 998 if (ji.dwPOV == JOY_POVBACKWARD) 999 povstate |= 0x04; 1000 if (ji.dwPOV == JOY_POVLEFT) 1001 povstate |= 0x08; 1002 } 1003 // determine which bits have changed and key an auxillary event for each change 1004 for (i=0 ; i < 4 ; i++) 1005 { 1006 if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) ) 1007 { 1008 Key_Event (K_AUX29 + i, true); 1009 } 1010 1011 if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) ) 1012 { 1013 Key_Event (K_AUX29 + i, false); 1014 } 1015 } 1016 joy_oldpovstate = povstate; 1017 } 1018} 1019 1020 1021/* 1022=============== 1023IN_ReadJoystick 1024=============== 1025*/ 1026qboolean IN_ReadJoystick (void) 1027{ 1028 1029 memset (&ji, 0, sizeof(ji)); 1030 ji.dwSize = sizeof(ji); 1031 ji.dwFlags = joy_flags; 1032 1033 if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR) 1034 { 1035 // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver 1036 // rather than having 32768 be the zero point, they have the zero point at 32668 1037 // go figure -- anyway, now we get the full resolution out of the device 1038 if (joy_wwhack1.value != 0.0) 1039 { 1040 ji.dwUpos += 100; 1041 } 1042 return true; 1043 } 1044 else 1045 { 1046 // read error occurred 1047 // turning off the joystick seems too harsh for 1 read error,\ 1048 // but what should be done? 1049 // Con_Printf ("IN_ReadJoystick: no response\n"); 1050 // joy_avail = false; 1051 return false; 1052 } 1053} 1054 1055 1056/* 1057=========== 1058IN_JoyMove 1059=========== 1060*/ 1061void IN_JoyMove (usercmd_t *cmd) 1062{ 1063 float speed, aspeed; 1064 float fAxisValue, fTemp; 1065 int i; 1066 1067 // complete initialization if first time in 1068 // this is needed as cvars are not available at initialization time 1069 if( joy_advancedinit != true ) 1070 { 1071 Joy_AdvancedUpdate_f(); 1072 joy_advancedinit = true; 1073 } 1074 1075 // verify joystick is available and that the user wants to use it 1076 if (!joy_avail || !in_joystick.value) 1077 { 1078 return; 1079 } 1080 1081 // collect the joystick data, if possible 1082 if (IN_ReadJoystick () != true) 1083 { 1084 return; 1085 } 1086 1087 if (in_speed.state & 1) 1088 speed = cl_movespeedkey.value; 1089 else 1090 speed = 1; 1091 aspeed = speed * host_frametime; 1092 1093 // loop through the axes 1094 for (i = 0; i < JOY_MAX_AXES; i++) 1095 { 1096 // get the floating point zero-centered, potentially-inverted data for the current axis 1097 fAxisValue = (float) *pdwRawValue[i]; 1098 // move centerpoint to zero 1099 fAxisValue -= 32768.0; 1100 1101 if (joy_wwhack2.value != 0.0) 1102 { 1103 if (dwAxisMap[i] == AxisTurn) 1104 { 1105 // this is a special formula for the Logitech WingMan Warrior 1106 // y=ax^b; where a = 300 and b = 1.3 1107 // also x values are in increments of 800 (so this is factored out) 1108 // then bounds check result to level out excessively high spin rates 1109 fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3); 1110 if (fTemp > 14000.0) 1111 fTemp = 14000.0; 1112 // restore direction information 1113 fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; 1114 } 1115 } 1116 1117 // convert range from -32768..32767 to -1..1 1118 fAxisValue /= 32768.0; 1119 1120 switch (dwAxisMap[i]) 1121 { 1122 case AxisForward: 1123 if ((joy_advanced.value == 0.0) && (in_mlook.state & 1)) 1124 { 1125 // user wants forward control to become look control 1126 if (fabs(fAxisValue) > joy_pitchthreshold.value) 1127 { 1128 // if mouse invert is on, invert the joystick pitch value 1129 // only absolute control support here (joy_advanced is false) 1130 if (m_pitch.value < 0.0) 1131 { 1132 cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; 1133 } 1134 else 1135 { 1136 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; 1137 } 1138 V_StopPitchDrift(); 1139 } 1140 else 1141 { 1142 // no pitch movement 1143 // disable pitch return-to-center unless requested by user 1144 // *** this code can be removed when the lookspring bug is fixed 1145 // *** the bug always has the lookspring feature on 1146 if(lookspring.value == 0.0) 1147 V_StopPitchDrift(); 1148 } 1149 } 1150 else 1151 { 1152 // user wants forward control to be forward control 1153 if (fabs(fAxisValue) > joy_forwardthreshold.value) 1154 { 1155 cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value; 1156 } 1157 } 1158 break; 1159 1160 case AxisSide: 1161 if (fabs(fAxisValue) > joy_sidethreshold.value) 1162 { 1163 cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; 1164 } 1165 break; 1166 1167 case AxisTurn: 1168 if ((in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1))) 1169 { 1170 // user wants turn control to become side control 1171 if (fabs(fAxisValue) > joy_sidethreshold.value) 1172 { 1173 cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; 1174 } 1175 } 1176 else 1177 { 1178 // user wants turn control to be turn control 1179 if (fabs(fAxisValue) > joy_yawthreshold.value) 1180 { 1181 if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) 1182 { 1183 cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value; 1184 } 1185 else 1186 { 1187 cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0; 1188 } 1189 1190 } 1191 } 1192 break; 1193 1194 case AxisLook: 1195 if (in_mlook.state & 1) 1196 { 1197 if (fabs(fAxisValue) > joy_pitchthreshold.value) 1198 { 1199 // pitch movement detected and pitch movement desired by user 1200 if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) 1201 { 1202 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; 1203 } 1204 else 1205 { 1206 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0; 1207 } 1208 V_StopPitchDrift(); 1209 } 1210 else 1211 { 1212 // no pitch movement 1213 // disable pitch return-to-center unless requested by user 1214 // *** this code can be removed when the lookspring bug is fixed 1215 // *** the bug always has the lookspring feature on 1216 if(lookspring.value == 0.0) 1217 V_StopPitchDrift(); 1218 } 1219 } 1220 break; 1221 1222 default: 1223 break; 1224 } 1225 } 1226 1227 // bounds check pitch 1228 if (cl.viewangles[PITCH] > 80.0) 1229 cl.viewangles[PITCH] = 80.0; 1230 if (cl.viewangles[PITCH] < -70.0) 1231 cl.viewangles[PITCH] = -70.0; 1232} 1233