/* | |
Copyright (C) 1996-1997 Id Software, Inc. | |
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 2 | |
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, write to the Free Software | |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
*/ | |
// in_win.c -- windows 95 mouse and joystick code | |
// 02/21/97 JCB Added extended DirectInput code to support external controllers. | |
#include <dinput.h> | |
#include "quakedef.h" | |
#include "winquake.h" | |
#include "dosisms.h" | |
#define DINPUT_BUFFERSIZE 16 | |
#define iDirectInputCreate(a,b,c,d) pDirectInputCreate(a,b,c,d) | |
HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, | |
LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter); | |
// mouse variables | |
cvar_t m_filter = {"m_filter","0"}; | |
int mouse_buttons; | |
int mouse_oldbuttonstate; | |
POINT current_pos; | |
int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum; | |
static qboolean restore_spi; | |
static int originalmouseparms[3], newmouseparms[3] = {0, 0, 1}; | |
unsigned int uiWheelMessage; | |
qboolean mouseactive; | |
qboolean mouseinitialized; | |
static qboolean mouseparmsvalid, mouseactivatetoggle; | |
static qboolean mouseshowtoggle = 1; | |
static qboolean dinput_acquired; | |
static unsigned int mstate_di; | |
// joystick defines and variables | |
// where should defines be moved? | |
#define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick | |
#define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball | |
#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V | |
#define JOY_AXIS_X 0 | |
#define JOY_AXIS_Y 1 | |
#define JOY_AXIS_Z 2 | |
#define JOY_AXIS_R 3 | |
#define JOY_AXIS_U 4 | |
#define JOY_AXIS_V 5 | |
enum _ControlList | |
{ | |
AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn | |
}; | |
DWORD dwAxisFlags[JOY_MAX_AXES] = | |
{ | |
JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV | |
}; | |
DWORD dwAxisMap[JOY_MAX_AXES]; | |
DWORD dwControlMap[JOY_MAX_AXES]; | |
PDWORD pdwRawValue[JOY_MAX_AXES]; | |
// none of these cvars are saved over a session | |
// this means that advanced controller configuration needs to be executed | |
// each time. this avoids any problems with getting back to a default usage | |
// or when changing from one controller to another. this way at least something | |
// works. | |
cvar_t in_joystick = {"joystick","0", true}; | |
cvar_t joy_name = {"joyname", "joystick"}; | |
cvar_t joy_advanced = {"joyadvanced", "0"}; | |
cvar_t joy_advaxisx = {"joyadvaxisx", "0"}; | |
cvar_t joy_advaxisy = {"joyadvaxisy", "0"}; | |
cvar_t joy_advaxisz = {"joyadvaxisz", "0"}; | |
cvar_t joy_advaxisr = {"joyadvaxisr", "0"}; | |
cvar_t joy_advaxisu = {"joyadvaxisu", "0"}; | |
cvar_t joy_advaxisv = {"joyadvaxisv", "0"}; | |
cvar_t joy_forwardthreshold = {"joyforwardthreshold", "0.15"}; | |
cvar_t joy_sidethreshold = {"joysidethreshold", "0.15"}; | |
cvar_t joy_pitchthreshold = {"joypitchthreshold", "0.15"}; | |
cvar_t joy_yawthreshold = {"joyyawthreshold", "0.15"}; | |
cvar_t joy_forwardsensitivity = {"joyforwardsensitivity", "-1.0"}; | |
cvar_t joy_sidesensitivity = {"joysidesensitivity", "-1.0"}; | |
cvar_t joy_pitchsensitivity = {"joypitchsensitivity", "1.0"}; | |
cvar_t joy_yawsensitivity = {"joyyawsensitivity", "-1.0"}; | |
cvar_t joy_wwhack1 = {"joywwhack1", "0.0"}; | |
cvar_t joy_wwhack2 = {"joywwhack2", "0.0"}; | |
qboolean joy_avail, joy_advancedinit, joy_haspov; | |
DWORD joy_oldbuttonstate, joy_oldpovstate; | |
int joy_id; | |
DWORD joy_flags; | |
DWORD joy_numbuttons; | |
static LPDIRECTINPUT g_pdi; | |
static LPDIRECTINPUTDEVICE g_pMouse; | |
static JOYINFOEX ji; | |
static HINSTANCE hInstDI; | |
static qboolean dinput; | |
typedef struct MYDATA { | |
LONG lX; // X axis goes here | |
LONG lY; // Y axis goes here | |
LONG lZ; // Z axis goes here | |
BYTE bButtonA; // One button goes here | |
BYTE bButtonB; // Another button goes here | |
BYTE bButtonC; // Another button goes here | |
BYTE bButtonD; // Another button goes here | |
} MYDATA; | |
static DIOBJECTDATAFORMAT rgodf[] = { | |
{ &GUID_XAxis, FIELD_OFFSET(MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, | |
{ &GUID_YAxis, FIELD_OFFSET(MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, | |
{ &GUID_ZAxis, FIELD_OFFSET(MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, | |
{ 0, FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, | |
{ 0, FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, | |
{ 0, FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, | |
{ 0, FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, | |
}; | |
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0])) | |
static DIDATAFORMAT df = { | |
sizeof(DIDATAFORMAT), // this structure | |
sizeof(DIOBJECTDATAFORMAT), // size of object data format | |
DIDF_RELAXIS, // absolute axis coordinates | |
sizeof(MYDATA), // device data size | |
NUM_OBJECTS, // number of objects | |
rgodf, // and here they are | |
}; | |
// forward-referenced functions | |
void IN_StartupJoystick (void); | |
void Joy_AdvancedUpdate_f (void); | |
void IN_JoyMove (usercmd_t *cmd); | |
/* | |
=========== | |
Force_CenterView_f | |
=========== | |
*/ | |
void Force_CenterView_f (void) | |
{ | |
cl.viewangles[PITCH] = 0; | |
} | |
/* | |
=========== | |
IN_UpdateClipCursor | |
=========== | |
*/ | |
void IN_UpdateClipCursor (void) | |
{ | |
if (mouseinitialized && mouseactive && !dinput) | |
{ | |
ClipCursor (&window_rect); | |
} | |
} | |
/* | |
=========== | |
IN_ShowMouse | |
=========== | |
*/ | |
void IN_ShowMouse (void) | |
{ | |
if (!mouseshowtoggle) | |
{ | |
ShowCursor (TRUE); | |
mouseshowtoggle = 1; | |
} | |
} | |
/* | |
=========== | |
IN_HideMouse | |
=========== | |
*/ | |
void IN_HideMouse (void) | |
{ | |
if (mouseshowtoggle) | |
{ | |
ShowCursor (FALSE); | |
mouseshowtoggle = 0; | |
} | |
} | |
/* | |
=========== | |
IN_ActivateMouse | |
=========== | |
*/ | |
void IN_ActivateMouse (void) | |
{ | |
mouseactivatetoggle = true; | |
if (mouseinitialized) | |
{ | |
if (dinput) | |
{ | |
if (g_pMouse) | |
{ | |
if (!dinput_acquired) | |
{ | |
IDirectInputDevice_Acquire(g_pMouse); | |
dinput_acquired = true; | |
} | |
} | |
else | |
{ | |
return; | |
} | |
} | |
else | |
{ | |
if (mouseparmsvalid) | |
restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); | |
SetCursorPos (window_center_x, window_center_y); | |
SetCapture (mainwindow); | |
ClipCursor (&window_rect); | |
} | |
mouseactive = true; | |
} | |
} | |
/* | |
=========== | |
IN_SetQuakeMouseState | |
=========== | |
*/ | |
void IN_SetQuakeMouseState (void) | |
{ | |
if (mouseactivatetoggle) | |
IN_ActivateMouse (); | |
} | |
/* | |
=========== | |
IN_DeactivateMouse | |
=========== | |
*/ | |
void IN_DeactivateMouse (void) | |
{ | |
mouseactivatetoggle = false; | |
if (mouseinitialized) | |
{ | |
if (dinput) | |
{ | |
if (g_pMouse) | |
{ | |
if (dinput_acquired) | |
{ | |
IDirectInputDevice_Unacquire(g_pMouse); | |
dinput_acquired = false; | |
} | |
} | |
} | |
else | |
{ | |
if (restore_spi) | |
SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); | |
ClipCursor (NULL); | |
ReleaseCapture (); | |
} | |
mouseactive = false; | |
} | |
} | |
/* | |
=========== | |
IN_RestoreOriginalMouseState | |
=========== | |
*/ | |
void IN_RestoreOriginalMouseState (void) | |
{ | |
if (mouseactivatetoggle) | |
{ | |
IN_DeactivateMouse (); | |
mouseactivatetoggle = true; | |
} | |
// try to redraw the cursor so it gets reinitialized, because sometimes it | |
// has garbage after the mode switch | |
ShowCursor (TRUE); | |
ShowCursor (FALSE); | |
} | |
/* | |
=========== | |
IN_InitDInput | |
=========== | |
*/ | |
qboolean IN_InitDInput (void) | |
{ | |
HRESULT hr; | |
DIPROPDWORD dipdw = { | |
{ | |
sizeof(DIPROPDWORD), // diph.dwSize | |
sizeof(DIPROPHEADER), // diph.dwHeaderSize | |
0, // diph.dwObj | |
DIPH_DEVICE, // diph.dwHow | |
}, | |
DINPUT_BUFFERSIZE, // dwData | |
}; | |
if (!hInstDI) | |
{ | |
hInstDI = LoadLibrary("dinput.dll"); | |
if (hInstDI == NULL) | |
{ | |
Con_SafePrintf ("Couldn't load dinput.dll\n"); | |
return false; | |
} | |
} | |
if (!pDirectInputCreate) | |
{ | |
pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA"); | |
if (!pDirectInputCreate) | |
{ | |
Con_SafePrintf ("Couldn't get DI proc addr\n"); | |
return false; | |
} | |
} | |
// register with DirectInput and get an IDirectInput to play with. | |
hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL); | |
if (FAILED(hr)) | |
{ | |
return false; | |
} | |
// obtain an interface to the system mouse device. | |
hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL); | |
if (FAILED(hr)) | |
{ | |
Con_SafePrintf ("Couldn't open DI mouse device\n"); | |
return false; | |
} | |
// set the data format to "mouse format". | |
hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df); | |
if (FAILED(hr)) | |
{ | |
Con_SafePrintf ("Couldn't set DI mouse format\n"); | |
return false; | |
} | |
// set the cooperativity level. | |
hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow, | |
DISCL_EXCLUSIVE | DISCL_FOREGROUND); | |
if (FAILED(hr)) | |
{ | |
Con_SafePrintf ("Couldn't set DI coop level\n"); | |
return false; | |
} | |
// set the buffer size to DINPUT_BUFFERSIZE elements. | |
// the buffer size is a DWORD property associated with the device | |
hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph); | |
if (FAILED(hr)) | |
{ | |
Con_SafePrintf ("Couldn't set DI buffersize\n"); | |
return false; | |
} | |
return true; | |
} | |
/* | |
=========== | |
IN_StartupMouse | |
=========== | |
*/ | |
void IN_StartupMouse (void) | |
{ | |
HDC hdc; | |
if ( COM_CheckParm ("-nomouse") ) | |
return; | |
mouseinitialized = true; | |
if (COM_CheckParm ("-dinput")) | |
{ | |
dinput = IN_InitDInput (); | |
if (dinput) | |
{ | |
Con_SafePrintf ("DirectInput initialized\n"); | |
} | |
else | |
{ | |
Con_SafePrintf ("DirectInput not initialized\n"); | |
} | |
} | |
if (!dinput) | |
{ | |
mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); | |
if (mouseparmsvalid) | |
{ | |
if ( COM_CheckParm ("-noforcemspd") ) | |
newmouseparms[2] = originalmouseparms[2]; | |
if ( COM_CheckParm ("-noforcemaccel") ) | |
{ | |
newmouseparms[0] = originalmouseparms[0]; | |
newmouseparms[1] = originalmouseparms[1]; | |
} | |
if ( COM_CheckParm ("-noforcemparms") ) | |
{ | |
newmouseparms[0] = originalmouseparms[0]; | |
newmouseparms[1] = originalmouseparms[1]; | |
newmouseparms[2] = originalmouseparms[2]; | |
} | |
} | |
} | |
mouse_buttons = 3; | |
// if a fullscreen video mode was set before the mouse was initialized, | |
// set the mouse state appropriately | |
if (mouseactivatetoggle) | |
IN_ActivateMouse (); | |
} | |
/* | |
=========== | |
IN_Init | |
=========== | |
*/ | |
void IN_Init (void) | |
{ | |
// mouse variables | |
Cvar_RegisterVariable (&m_filter); | |
// joystick variables | |
Cvar_RegisterVariable (&in_joystick); | |
Cvar_RegisterVariable (&joy_name); | |
Cvar_RegisterVariable (&joy_advanced); | |
Cvar_RegisterVariable (&joy_advaxisx); | |
Cvar_RegisterVariable (&joy_advaxisy); | |
Cvar_RegisterVariable (&joy_advaxisz); | |
Cvar_RegisterVariable (&joy_advaxisr); | |
Cvar_RegisterVariable (&joy_advaxisu); | |
Cvar_RegisterVariable (&joy_advaxisv); | |
Cvar_RegisterVariable (&joy_forwardthreshold); | |
Cvar_RegisterVariable (&joy_sidethreshold); | |
Cvar_RegisterVariable (&joy_pitchthreshold); | |
Cvar_RegisterVariable (&joy_yawthreshold); | |
Cvar_RegisterVariable (&joy_forwardsensitivity); | |
Cvar_RegisterVariable (&joy_sidesensitivity); | |
Cvar_RegisterVariable (&joy_pitchsensitivity); | |
Cvar_RegisterVariable (&joy_yawsensitivity); | |
Cvar_RegisterVariable (&joy_wwhack1); | |
Cvar_RegisterVariable (&joy_wwhack2); | |
Cmd_AddCommand ("force_centerview", Force_CenterView_f); | |
Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f); | |
uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" ); | |
IN_StartupMouse (); | |
IN_StartupJoystick (); | |
} | |
/* | |
=========== | |
IN_Shutdown | |
=========== | |
*/ | |
void IN_Shutdown (void) | |
{ | |
IN_DeactivateMouse (); | |
IN_ShowMouse (); | |
if (g_pMouse) | |
{ | |
IDirectInputDevice_Release(g_pMouse); | |
g_pMouse = NULL; | |
} | |
if (g_pdi) | |
{ | |
IDirectInput_Release(g_pdi); | |
g_pdi = NULL; | |
} | |
} | |
/* | |
=========== | |
IN_MouseEvent | |
=========== | |
*/ | |
void IN_MouseEvent (int mstate) | |
{ | |
int i; | |
if (mouseactive && !dinput) | |
{ | |
// perform button actions | |
for (i=0 ; i<mouse_buttons ; i++) | |
{ | |
if ( (mstate & (1<<i)) && | |
!(mouse_oldbuttonstate & (1<<i)) ) | |
{ | |
Key_Event (K_MOUSE1 + i, true); | |
} | |
if ( !(mstate & (1<<i)) && | |
(mouse_oldbuttonstate & (1<<i)) ) | |
{ | |
Key_Event (K_MOUSE1 + i, false); | |
} | |
} | |
mouse_oldbuttonstate = mstate; | |
} | |
} | |
/* | |
=========== | |
IN_MouseMove | |
=========== | |
*/ | |
void IN_MouseMove (usercmd_t *cmd) | |
{ | |
int mx, my; | |
HDC hdc; | |
int i; | |
DIDEVICEOBJECTDATA od; | |
DWORD dwElements; | |
HRESULT hr; | |
if (!mouseactive) | |
return; | |
if (dinput) | |
{ | |
mx = 0; | |
my = 0; | |
for (;;) | |
{ | |
dwElements = 1; | |
hr = IDirectInputDevice_GetDeviceData(g_pMouse, | |
sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0); | |
if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) | |
{ | |
dinput_acquired = true; | |
IDirectInputDevice_Acquire(g_pMouse); | |
break; | |
} | |
/* Unable to read data or no data available */ | |
if (FAILED(hr) || dwElements == 0) | |
{ | |
break; | |
} | |
/* Look at the element to see what happened */ | |
switch (od.dwOfs) | |
{ | |
case DIMOFS_X: | |
mx += od.dwData; | |
break; | |
case DIMOFS_Y: | |
my += od.dwData; | |
break; | |
case DIMOFS_BUTTON0: | |
if (od.dwData & 0x80) | |
mstate_di |= 1; | |
else | |
mstate_di &= ~1; | |
break; | |
case DIMOFS_BUTTON1: | |
if (od.dwData & 0x80) | |
mstate_di |= (1<<1); | |
else | |
mstate_di &= ~(1<<1); | |
break; | |
case DIMOFS_BUTTON2: | |
if (od.dwData & 0x80) | |
mstate_di |= (1<<2); | |
else | |
mstate_di &= ~(1<<2); | |
break; | |
} | |
} | |
// perform button actions | |
for (i=0 ; i<mouse_buttons ; i++) | |
{ | |
if ( (mstate_di & (1<<i)) && | |
!(mouse_oldbuttonstate & (1<<i)) ) | |
{ | |
Key_Event (K_MOUSE1 + i, true); | |
} | |
if ( !(mstate_di & (1<<i)) && | |
(mouse_oldbuttonstate & (1<<i)) ) | |
{ | |
Key_Event (K_MOUSE1 + i, false); | |
} | |
} | |
mouse_oldbuttonstate = mstate_di; | |
} | |
else | |
{ | |
GetCursorPos (¤t_pos); | |
mx = current_pos.x - window_center_x + mx_accum; | |
my = current_pos.y - window_center_y + my_accum; | |
mx_accum = 0; | |
my_accum = 0; | |
} | |
//if (mx || my) | |
// Con_DPrintf("mx=%d, my=%d\n", mx, my); | |
if (m_filter.value) | |
{ | |
mouse_x = (mx + old_mouse_x) * 0.5; | |
mouse_y = (my + old_mouse_y) * 0.5; | |
} | |
else | |
{ | |
mouse_x = mx; | |
mouse_y = my; | |
} | |
old_mouse_x = mx; | |
old_mouse_y = my; | |
mouse_x *= sensitivity.value; | |
mouse_y *= sensitivity.value; | |
// add mouse X/Y movement to cmd | |
if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) | |
cmd->sidemove += m_side.value * mouse_x; | |
else | |
cl.viewangles[YAW] -= m_yaw.value * mouse_x; | |
if (in_mlook.state & 1) | |
V_StopPitchDrift (); | |
if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) | |
{ | |
cl.viewangles[PITCH] += m_pitch.value * mouse_y; | |
if (cl.viewangles[PITCH] > 80) | |
cl.viewangles[PITCH] = 80; | |
if (cl.viewangles[PITCH] < -70) | |
cl.viewangles[PITCH] = -70; | |
} | |
else | |
{ | |
if ((in_strafe.state & 1) && noclip_anglehack) | |
cmd->upmove -= m_forward.value * mouse_y; | |
else | |
cmd->forwardmove -= m_forward.value * mouse_y; | |
} | |
// if the mouse has moved, force it to the center, so there's room to move | |
if (mx || my) | |
{ | |
SetCursorPos (window_center_x, window_center_y); | |
} | |
} | |
/* | |
=========== | |
IN_Move | |
=========== | |
*/ | |
void IN_Move (usercmd_t *cmd) | |
{ | |
if (ActiveApp && !Minimized) | |
{ | |
IN_MouseMove (cmd); | |
IN_JoyMove (cmd); | |
} | |
} | |
/* | |
=========== | |
IN_Accumulate | |
=========== | |
*/ | |
void IN_Accumulate (void) | |
{ | |
int mx, my; | |
HDC hdc; | |
if (mouseactive) | |
{ | |
if (!dinput) | |
{ | |
GetCursorPos (¤t_pos); | |
mx_accum += current_pos.x - window_center_x; | |
my_accum += current_pos.y - window_center_y; | |
// force the mouse to the center, so there's room to move | |
SetCursorPos (window_center_x, window_center_y); | |
} | |
} | |
} | |
/* | |
=================== | |
IN_ClearStates | |
=================== | |
*/ | |
void IN_ClearStates (void) | |
{ | |
if (mouseactive) | |
{ | |
mx_accum = 0; | |
my_accum = 0; | |
mouse_oldbuttonstate = 0; | |
} | |
} | |
/* | |
=============== | |
IN_StartupJoystick | |
=============== | |
*/ | |
void IN_StartupJoystick (void) | |
{ | |
int i, numdevs; | |
JOYCAPS jc; | |
MMRESULT mmr; | |
// assume no joystick | |
joy_avail = false; | |
// abort startup if user requests no joystick | |
if ( COM_CheckParm ("-nojoy") ) | |
return; | |
// verify joystick driver is present | |
if ((numdevs = joyGetNumDevs ()) == 0) | |
{ | |
Con_Printf ("\njoystick not found -- driver not present\n\n"); | |
return; | |
} | |
// cycle through the joystick ids for the first valid one | |
for (joy_id=0 ; joy_id<numdevs ; joy_id++) | |
{ | |
memset (&ji, 0, sizeof(ji)); | |
ji.dwSize = sizeof(ji); | |
ji.dwFlags = JOY_RETURNCENTERED; | |
if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR) | |
break; | |
} | |
// abort startup if we didn't find a valid joystick | |
if (mmr != JOYERR_NOERROR) | |
{ | |
Con_Printf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr); | |
return; | |
} | |
// get the capabilities of the selected joystick | |
// abort startup if command fails | |
memset (&jc, 0, sizeof(jc)); | |
if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR) | |
{ | |
Con_Printf ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr); | |
return; | |
} | |
// save the joystick's number of buttons and POV status | |
joy_numbuttons = jc.wNumButtons; | |
joy_haspov = jc.wCaps & JOYCAPS_HASPOV; | |
// old button and POV states default to no buttons pressed | |
joy_oldbuttonstate = joy_oldpovstate = 0; | |
// mark the joystick as available and advanced initialization not completed | |
// this is needed as cvars are not available during initialization | |
joy_avail = true; | |
joy_advancedinit = false; | |
Con_Printf ("\njoystick detected\n\n"); | |
} | |
/* | |
=========== | |
RawValuePointer | |
=========== | |
*/ | |
PDWORD RawValuePointer (int axis) | |
{ | |
switch (axis) | |
{ | |
case JOY_AXIS_X: | |
return &ji.dwXpos; | |
case JOY_AXIS_Y: | |
return &ji.dwYpos; | |
case JOY_AXIS_Z: | |
return &ji.dwZpos; | |
case JOY_AXIS_R: | |
return &ji.dwRpos; | |
case JOY_AXIS_U: | |
return &ji.dwUpos; | |
case JOY_AXIS_V: | |
return &ji.dwVpos; | |
} | |
} | |
/* | |
=========== | |
Joy_AdvancedUpdate_f | |
=========== | |
*/ | |
void Joy_AdvancedUpdate_f (void) | |
{ | |
// called once by IN_ReadJoystick and by user whenever an update is needed | |
// cvars are now available | |
int i; | |
DWORD dwTemp; | |
// initialize all the maps | |
for (i = 0; i < JOY_MAX_AXES; i++) | |
{ | |
dwAxisMap[i] = AxisNada; | |
dwControlMap[i] = JOY_ABSOLUTE_AXIS; | |
pdwRawValue[i] = RawValuePointer(i); | |
} | |
if( joy_advanced.value == 0.0) | |
{ | |
// default joystick initialization | |
// 2 axes only with joystick control | |
dwAxisMap[JOY_AXIS_X] = AxisTurn; | |
// dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS; | |
dwAxisMap[JOY_AXIS_Y] = AxisForward; | |
// dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; | |
} | |
else | |
{ | |
if (Q_strcmp (joy_name.string, "joystick") != 0) | |
{ | |
// notify user of advanced controller | |
Con_Printf ("\n%s configured\n\n", joy_name.string); | |
} | |
// advanced initialization here | |
// data supplied by user via joy_axisn cvars | |
dwTemp = (DWORD) joy_advaxisx.value; | |
dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; | |
dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; | |
dwTemp = (DWORD) joy_advaxisy.value; | |
dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; | |
dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; | |
dwTemp = (DWORD) joy_advaxisz.value; | |
dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; | |
dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; | |
dwTemp = (DWORD) joy_advaxisr.value; | |
dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; | |
dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; | |
dwTemp = (DWORD) joy_advaxisu.value; | |
dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; | |
dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; | |
dwTemp = (DWORD) joy_advaxisv.value; | |
dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; | |
dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; | |
} | |
// compute the axes to collect from DirectInput | |
joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; | |
for (i = 0; i < JOY_MAX_AXES; i++) | |
{ | |
if (dwAxisMap[i] != AxisNada) | |
{ | |
joy_flags |= dwAxisFlags[i]; | |
} | |
} | |
} | |
/* | |
=========== | |
IN_Commands | |
=========== | |
*/ | |
void IN_Commands (void) | |
{ | |
int i, key_index; | |
DWORD buttonstate, povstate; | |
if (!joy_avail) | |
{ | |
return; | |
} | |
// loop through the joystick buttons | |
// key a joystick event or auxillary event for higher number buttons for each state change | |
buttonstate = ji.dwButtons; | |
for (i=0 ; i < joy_numbuttons ; i++) | |
{ | |
if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) ) | |
{ | |
key_index = (i < 4) ? K_JOY1 : K_AUX1; | |
Key_Event (key_index + i, true); | |
} | |
if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) ) | |
{ | |
key_index = (i < 4) ? K_JOY1 : K_AUX1; | |
Key_Event (key_index + i, false); | |
} | |
} | |
joy_oldbuttonstate = buttonstate; | |
if (joy_haspov) | |
{ | |
// convert POV information into 4 bits of state information | |
// this avoids any potential problems related to moving from one | |
// direction to another without going through the center position | |
povstate = 0; | |
if(ji.dwPOV != JOY_POVCENTERED) | |
{ | |
if (ji.dwPOV == JOY_POVFORWARD) | |
povstate |= 0x01; | |
if (ji.dwPOV == JOY_POVRIGHT) | |
povstate |= 0x02; | |
if (ji.dwPOV == JOY_POVBACKWARD) | |
povstate |= 0x04; | |
if (ji.dwPOV == JOY_POVLEFT) | |
povstate |= 0x08; | |
} | |
// determine which bits have changed and key an auxillary event for each change | |
for (i=0 ; i < 4 ; i++) | |
{ | |
if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) ) | |
{ | |
Key_Event (K_AUX29 + i, true); | |
} | |
if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) ) | |
{ | |
Key_Event (K_AUX29 + i, false); | |
} | |
} | |
joy_oldpovstate = povstate; | |
} | |
} | |
/* | |
=============== | |
IN_ReadJoystick | |
=============== | |
*/ | |
qboolean IN_ReadJoystick (void) | |
{ | |
memset (&ji, 0, sizeof(ji)); | |
ji.dwSize = sizeof(ji); | |
ji.dwFlags = joy_flags; | |
if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR) | |
{ | |
// this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver | |
// rather than having 32768 be the zero point, they have the zero point at 32668 | |
// go figure -- anyway, now we get the full resolution out of the device | |
if (joy_wwhack1.value != 0.0) | |
{ | |
ji.dwUpos += 100; | |
} | |
return true; | |
} | |
else | |
{ | |
// read error occurred | |
// turning off the joystick seems too harsh for 1 read error,\ | |
// but what should be done? | |
// Con_Printf ("IN_ReadJoystick: no response\n"); | |
// joy_avail = false; | |
return false; | |
} | |
} | |
/* | |
=========== | |
IN_JoyMove | |
=========== | |
*/ | |
void IN_JoyMove (usercmd_t *cmd) | |
{ | |
float speed, aspeed; | |
float fAxisValue, fTemp; | |
int i; | |
// complete initialization if first time in | |
// this is needed as cvars are not available at initialization time | |
if( joy_advancedinit != true ) | |
{ | |
Joy_AdvancedUpdate_f(); | |
joy_advancedinit = true; | |
} | |
// verify joystick is available and that the user wants to use it | |
if (!joy_avail || !in_joystick.value) | |
{ | |
return; | |
} | |
// collect the joystick data, if possible | |
if (IN_ReadJoystick () != true) | |
{ | |
return; | |
} | |
if (in_speed.state & 1) | |
speed = cl_movespeedkey.value; | |
else | |
speed = 1; | |
aspeed = speed * host_frametime; | |
// loop through the axes | |
for (i = 0; i < JOY_MAX_AXES; i++) | |
{ | |
// get the floating point zero-centered, potentially-inverted data for the current axis | |
fAxisValue = (float) *pdwRawValue[i]; | |
// move centerpoint to zero | |
fAxisValue -= 32768.0; | |
if (joy_wwhack2.value != 0.0) | |
{ | |
if (dwAxisMap[i] == AxisTurn) | |
{ | |
// this is a special formula for the Logitech WingMan Warrior | |
// y=ax^b; where a = 300 and b = 1.3 | |
// also x values are in increments of 800 (so this is factored out) | |
// then bounds check result to level out excessively high spin rates | |
fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3); | |
if (fTemp > 14000.0) | |
fTemp = 14000.0; | |
// restore direction information | |
fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; | |
} | |
} | |
// convert range from -32768..32767 to -1..1 | |
fAxisValue /= 32768.0; | |
switch (dwAxisMap[i]) | |
{ | |
case AxisForward: | |
if ((joy_advanced.value == 0.0) && (in_mlook.state & 1)) | |
{ | |
// user wants forward control to become look control | |
if (fabs(fAxisValue) > joy_pitchthreshold.value) | |
{ | |
// if mouse invert is on, invert the joystick pitch value | |
// only absolute control support here (joy_advanced is false) | |
if (m_pitch.value < 0.0) | |
{ | |
cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; | |
} | |
else | |
{ | |
cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; | |
} | |
V_StopPitchDrift(); | |
} | |
else | |
{ | |
// no pitch movement | |
// disable pitch return-to-center unless requested by user | |
// *** this code can be removed when the lookspring bug is fixed | |
// *** the bug always has the lookspring feature on | |
if(lookspring.value == 0.0) | |
V_StopPitchDrift(); | |
} | |
} | |
else | |
{ | |
// user wants forward control to be forward control | |
if (fabs(fAxisValue) > joy_forwardthreshold.value) | |
{ | |
cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value; | |
} | |
} | |
break; | |
case AxisSide: | |
if (fabs(fAxisValue) > joy_sidethreshold.value) | |
{ | |
cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; | |
} | |
break; | |
case AxisTurn: | |
if ((in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1))) | |
{ | |
// user wants turn control to become side control | |
if (fabs(fAxisValue) > joy_sidethreshold.value) | |
{ | |
cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; | |
} | |
} | |
else | |
{ | |
// user wants turn control to be turn control | |
if (fabs(fAxisValue) > joy_yawthreshold.value) | |
{ | |
if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) | |
{ | |
cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value; | |
} | |
else | |
{ | |
cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0; | |
} | |
} | |
} | |
break; | |
case AxisLook: | |
if (in_mlook.state & 1) | |
{ | |
if (fabs(fAxisValue) > joy_pitchthreshold.value) | |
{ | |
// pitch movement detected and pitch movement desired by user | |
if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) | |
{ | |
cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; | |
} | |
else | |
{ | |
cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0; | |
} | |
V_StopPitchDrift(); | |
} | |
else | |
{ | |
// no pitch movement | |
// disable pitch return-to-center unless requested by user | |
// *** this code can be removed when the lookspring bug is fixed | |
// *** the bug always has the lookspring feature on | |
if(lookspring.value == 0.0) | |
V_StopPitchDrift(); | |
} | |
} | |
break; | |
default: | |
break; | |
} | |
} | |
// bounds check pitch | |
if (cl.viewangles[PITCH] > 80.0) | |
cl.viewangles[PITCH] = 80.0; | |
if (cl.viewangles[PITCH] < -70.0) | |
cl.viewangles[PITCH] = -70.0; | |
} |