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