mirror of
https://github.com/Q3Rally-Team/rallyunlimited-engine.git
synced 2024-11-26 06:01:34 +00:00
1654 lines
37 KiB
C
1654 lines
37 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code 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.
|
|
|
|
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
// win_input.c -- win32 mouse and joystick code
|
|
// 02/21/97 JCB Added extended DirectInput code to support external controllers.
|
|
|
|
#include "../client/client.h"
|
|
#include "win_local.h"
|
|
#include "glw_win.h"
|
|
|
|
|
|
typedef struct {
|
|
int oldButtonState;
|
|
|
|
qboolean mouseActive;
|
|
qboolean mouseInitialized;
|
|
} WinMouseVars_t;
|
|
|
|
static WinMouseVars_t s_wmv;
|
|
|
|
static POINT window_center;
|
|
static POINT client_center;
|
|
|
|
#ifdef USE_MIDI
|
|
//
|
|
// MIDI definitions
|
|
//
|
|
static void IN_StartupMIDI( void );
|
|
static void IN_ShutdownMIDI( void );
|
|
|
|
#define MAX_MIDIIN_DEVICES 8
|
|
|
|
typedef struct {
|
|
int numDevices;
|
|
MIDIINCAPS caps[MAX_MIDIIN_DEVICES];
|
|
|
|
HMIDIIN hMidiIn;
|
|
} MidiInfo_t;
|
|
|
|
static MidiInfo_t s_midiInfo;
|
|
#endif
|
|
|
|
#ifdef USE_JOYSTICK
|
|
//
|
|
// Joystick definitions
|
|
//
|
|
#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V
|
|
|
|
typedef struct {
|
|
qboolean avail;
|
|
int id; // joystick number
|
|
JOYCAPS jc;
|
|
|
|
int oldbuttonstate;
|
|
int oldpovstate;
|
|
|
|
JOYINFOEX ji;
|
|
} joystickInfo_t;
|
|
|
|
static joystickInfo_t joy;
|
|
#endif
|
|
|
|
|
|
#ifdef USE_MIDI
|
|
cvar_t *in_midi;
|
|
cvar_t *in_midiport;
|
|
cvar_t *in_midichannel;
|
|
cvar_t *in_mididevice;
|
|
#endif
|
|
|
|
cvar_t *in_minimize;
|
|
cvar_t *in_nograb;
|
|
cvar_t *in_lagged;
|
|
|
|
cvar_t *in_mouse;
|
|
cvar_t *in_logitechbug;
|
|
|
|
#ifdef USE_JOYSTICK
|
|
cvar_t *in_joystick;
|
|
cvar_t *in_joyBallScale;
|
|
cvar_t *in_debugJoystick;
|
|
cvar_t *joy_threshold;
|
|
#endif
|
|
|
|
// forward-referenced functions
|
|
#ifdef USE_JOYSTICK
|
|
void IN_StartupJoystick (void);
|
|
void IN_JoyMove(void);
|
|
#endif
|
|
|
|
#ifdef USE_MIDI
|
|
static void MidiInfo_f( void );
|
|
#endif
|
|
|
|
/*
|
|
============================================================
|
|
|
|
WIN32 MOUSE CONTROL
|
|
|
|
============================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
================
|
|
IN_MouseActive
|
|
================
|
|
*/
|
|
qboolean IN_MouseActive( void )
|
|
{
|
|
return ( s_wmv.mouseActive && in_nograb->integer == 0 );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
IN_UpdateWindow
|
|
|
|
Called when window gets resized/moved
|
|
Updates window center and clip region
|
|
================
|
|
*/
|
|
void IN_UpdateWindow( RECT *window_rect, qboolean updateClipRegion )
|
|
{
|
|
RECT rect;
|
|
|
|
if ( !window_rect )
|
|
window_rect = ▭
|
|
|
|
if ( GetClientRect( g_wv.hWnd, window_rect ) ) {
|
|
POINT pos;
|
|
int sx = 0, sy = 0;
|
|
|
|
pos.x = window_rect->left;
|
|
pos.y = window_rect->top;
|
|
ClientToScreen( g_wv.hWnd, &pos );
|
|
window_rect->left = pos.x;
|
|
window_rect->top = pos.y;
|
|
window_rect->right += pos.x;
|
|
window_rect->bottom += pos.y;
|
|
|
|
// do not overlap with taskbar
|
|
if ( window_rect->bottom > glw_state.workArea.bottom )
|
|
window_rect->bottom = glw_state.workArea.bottom;
|
|
// ... and with 90 degrees clockwise rotation
|
|
if ( window_rect->right > glw_state.workArea.right )
|
|
window_rect->right = glw_state.workArea.right;
|
|
|
|
if ( window_rect->top < glw_state.workArea.top )
|
|
sy = glw_state.workArea.top - window_rect->top;
|
|
|
|
if ( window_rect->left < glw_state.workArea.left )
|
|
sx = glw_state.workArea.left - window_rect->left;
|
|
|
|
client_center.x = (window_rect->right - window_rect->left + sx) / 2;
|
|
client_center.y = (window_rect->bottom - window_rect->top + sy) / 2;
|
|
window_center = client_center;
|
|
ClientToScreen( g_wv.hWnd, &window_center );
|
|
|
|
} else {
|
|
if ( !GetWindowRect( g_wv.hWnd, window_rect ) )
|
|
return;
|
|
window_center.x = ( window_rect->right + window_rect->left )/2;
|
|
window_center.y = ( window_rect->top + window_rect->bottom )/2;
|
|
client_center = window_center;
|
|
ScreenToClient( g_wv.hWnd, &client_center );
|
|
}
|
|
|
|
if ( updateClipRegion && s_wmv.mouseActive && gw_active ) {
|
|
ClipCursor( window_rect );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
IN_CaptureMouse
|
|
================
|
|
*/
|
|
static void IN_CaptureMouse( const RECT *clipRect )
|
|
{
|
|
CURSORINFO ci;
|
|
|
|
ClipCursor( clipRect );
|
|
SetCursorPos( window_center.x, window_center.y );
|
|
SetCapture( g_wv.hWnd );
|
|
|
|
memset( &ci, 0, sizeof( ci ) );
|
|
ci.cbSize = sizeof( CURSORINFO );
|
|
if ( GetCursorInfo( &ci ) ) {
|
|
if ( ci.flags == CURSOR_SHOWING ) {
|
|
while ( ShowCursor( FALSE ) >= 0 )
|
|
;
|
|
}
|
|
} else {
|
|
while ( ShowCursor( FALSE ) >= 0 )
|
|
;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
IN_ActivateWin32Mouse
|
|
================
|
|
*/
|
|
static void IN_ActivateWin32Mouse( void )
|
|
{
|
|
RECT window_rect;
|
|
IN_UpdateWindow( &window_rect, qfalse );
|
|
IN_CaptureMouse( &window_rect );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
IN_DeactivateWin32Mouse
|
|
================
|
|
*/
|
|
static void IN_DeactivateWin32Mouse( void )
|
|
{
|
|
CURSORINFO ci;
|
|
|
|
if ( !gw_minimized ) {
|
|
IN_UpdateWindow( NULL, qfalse );
|
|
SetCursorPos( window_center.x, window_center.y );
|
|
}
|
|
|
|
ReleaseCapture();
|
|
ClipCursor( NULL );
|
|
|
|
memset( &ci, 0, sizeof( ci ) );
|
|
ci.cbSize = sizeof( CURSORINFO );
|
|
if ( GetCursorInfo( &ci ) ) {
|
|
if ( ci.flags == 0 ) {
|
|
while ( ShowCursor( TRUE ) < 0 )
|
|
;
|
|
}
|
|
} else {
|
|
while ( ShowCursor( TRUE ) < 0 )
|
|
;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
IN_Win32Mouse
|
|
================
|
|
*/
|
|
static void IN_Win32Mouse( int *mx, int *my )
|
|
{
|
|
POINT current_pos;
|
|
|
|
// find mouse movement
|
|
GetCursorPos( ¤t_pos );
|
|
|
|
*mx = current_pos.x - window_center.x;
|
|
*my = current_pos.y - window_center.y;
|
|
}
|
|
|
|
|
|
/*
|
|
============================================================
|
|
|
|
RAW INPUT MOUSE CONTROL
|
|
|
|
============================================================
|
|
*/
|
|
#define ISWINXP(sys) (sys.dwPlatformId==VER_PLATFORM_WIN32_NT && \
|
|
((sys.dwMajorVersion==5 && sys.dwMinorVersion>=1)||(sys.dwMajorVersion>5)))
|
|
|
|
typedef UINT (WINAPI *PGRRID)(PRAWINPUTDEVICE pRawInputDevices, PUINT puiNumDevices, UINT cbSize);
|
|
typedef BOOL (WINAPI *PRRID)(PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize);
|
|
typedef UINT (WINAPI *PGRID)(HRAWINPUT hRawInput, UINT uiCommand, LPVOID pData, PUINT pcbSize, UINT cbSizeHeader);
|
|
|
|
static PGRRID GRRID;
|
|
static PRRID RRID;
|
|
static PGRID GRID;
|
|
|
|
static BOOL raw_inited = FALSE;
|
|
static BOOL raw_activated = FALSE;
|
|
|
|
|
|
/*
|
|
================
|
|
IN_InitRawMouse
|
|
================
|
|
*/
|
|
static BOOL IN_InitRawMouse( void ) {
|
|
|
|
HMODULE dll;
|
|
|
|
#ifndef idx64
|
|
if ( !ISWINXP( g_wv.osversion ) ) {
|
|
return FALSE; // operating system is not supported
|
|
}
|
|
#endif
|
|
|
|
if ( raw_inited ) {
|
|
return TRUE; // already inited
|
|
}
|
|
|
|
GRRID = NULL;
|
|
RRID = NULL;
|
|
GRID = NULL;
|
|
|
|
dll = GetModuleHandle( T("user32") ); // should always success
|
|
if ( !dll ) {
|
|
return FALSE;
|
|
}
|
|
|
|
GRRID = (PGRRID) GetProcAddress( dll, "GetRegisteredRawInputDevices" );
|
|
RRID = (PRRID) GetProcAddress( dll, "RegisterRawInputDevices" );
|
|
GRID = (PGRID) GetProcAddress( dll, "GetRawInputData" );
|
|
|
|
//CloseHandle( dll );
|
|
|
|
if ( !GRRID || !RRID || !GRID ) {
|
|
return FALSE;
|
|
}
|
|
|
|
raw_inited = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
IN_ActivateRawMouse
|
|
================
|
|
*/
|
|
static void IN_ActivateRawMouse( void )
|
|
{
|
|
RECT window_rect;
|
|
RAWINPUTDEVICE Rid;
|
|
UINT num;
|
|
int cnt;
|
|
|
|
if ( raw_activated )
|
|
{
|
|
return; // already activated
|
|
}
|
|
|
|
num = 1;
|
|
cnt = GRRID( &Rid, &num, sizeof( Rid ) );
|
|
if ( cnt < 0 || !g_wv.hWnd )
|
|
{
|
|
Com_Printf( S_COLOR_YELLOW "Error getting registered raw input devices\n" );
|
|
return; // error getting registered raw input devices
|
|
}
|
|
|
|
IN_UpdateWindow( &window_rect, qfalse );
|
|
|
|
if ( cnt >= 1 && Rid.hwndTarget == g_wv.hWnd )
|
|
{
|
|
// device already exists?
|
|
}
|
|
else
|
|
{
|
|
Rid.usUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
Rid.usUsage = HID_USAGE_GENERIC_MOUSE;
|
|
Rid.dwFlags = RIDEV_NOLEGACY /*| RIDEV_CAPTUREMOUSE*/; // skip all WM_*BUTTON* and WM_MOUSEMOVE stuff
|
|
Rid.hwndTarget = g_wv.hWnd;
|
|
|
|
if( !RRID( &Rid, 1, sizeof( Rid ) ) )
|
|
{
|
|
Com_Printf( S_COLOR_YELLOW "Error registering raw input device\n" );
|
|
return;
|
|
}
|
|
}
|
|
|
|
IN_CaptureMouse( &window_rect );
|
|
|
|
raw_activated = TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
IN_RawMouse
|
|
================
|
|
*/
|
|
static void IN_RawMouse( int *mx, int *my ) {
|
|
|
|
*mx = g_wv.raw_mx;
|
|
*my = g_wv.raw_my;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
IN_DeactivateRawMouse
|
|
================
|
|
*/
|
|
static void IN_DeactivateRawMouse( void )
|
|
{
|
|
if ( raw_activated )
|
|
{
|
|
RAWINPUTDEVICE Rid;
|
|
|
|
Rid.usUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
Rid.usUsage = HID_USAGE_GENERIC_MOUSE;
|
|
Rid.dwFlags = RIDEV_REMOVE;
|
|
Rid.hwndTarget = NULL;
|
|
if ( !RRID( &Rid, 1, sizeof( Rid ) ) )
|
|
{
|
|
Com_Printf( S_COLOR_YELLOW "Error removing raw input device\n" );
|
|
return;
|
|
}
|
|
}
|
|
|
|
raw_activated = FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
============================================================
|
|
|
|
DIRECT INPUT MOUSE CONTROL
|
|
|
|
============================================================
|
|
*/
|
|
|
|
#undef DEFINE_GUID
|
|
|
|
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
const GUID name \
|
|
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
|
|
|
DEFINE_GUID(GUID_SysMouse, 0x6F1D2B60,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
DEFINE_GUID(GUID_XAxis, 0xA36D02E0,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
DEFINE_GUID(GUID_YAxis, 0xA36D02E1,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
DEFINE_GUID(GUID_ZAxis, 0xA36D02E2,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
|
|
|
#define DINPUT_BUFFERSIZE 64
|
|
#define iDirectInputCreate(a,b,c,d) pDirectInputCreate(a,b,c,d)
|
|
|
|
HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion,
|
|
LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);
|
|
|
|
static HINSTANCE hInstDI;
|
|
|
|
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]))
|
|
|
|
// NOTE TTimo: would be easier using c_dfDIMouse or c_dfDIMouse2
|
|
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
|
|
};
|
|
|
|
static LPDIRECTINPUT g_pdi;
|
|
static LPDIRECTINPUTDEVICE g_pMouse;
|
|
|
|
static void IN_DIMouse( int *mx, int *my );
|
|
|
|
/*
|
|
========================
|
|
IN_InitDIMouse
|
|
========================
|
|
*/
|
|
static qboolean IN_InitDIMouse( void ) {
|
|
HRESULT hr;
|
|
int x, y;
|
|
DIPROPDWORD dipdw = {
|
|
{
|
|
sizeof(DIPROPDWORD), // diph.dwSize
|
|
sizeof(DIPROPHEADER), // diph.dwHeaderSize
|
|
0, // diph.dwObj
|
|
DIPH_DEVICE, // diph.dwHow
|
|
},
|
|
DINPUT_BUFFERSIZE, // dwData
|
|
};
|
|
|
|
Com_DPrintf( "Initializing DirectInput...\n");
|
|
|
|
if (!hInstDI) {
|
|
hInstDI = LoadLibrary( TEXT( "dinput.dll" ) );
|
|
|
|
if (hInstDI == NULL) {
|
|
Com_DPrintf ("Couldn't load dinput.dll\n");
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
if (!pDirectInputCreate) {
|
|
pDirectInputCreate = (HRESULT (WINAPI *)(HINSTANCE, DWORD, LPDIRECTINPUT *, LPUNKNOWN))
|
|
GetProcAddress(hInstDI,"DirectInputCreateA");
|
|
|
|
if (!pDirectInputCreate) {
|
|
Com_DPrintf ("Couldn't get DI proc addr\n");
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
// register with DirectInput and get an IDirectInput to play with.
|
|
hr = iDirectInputCreate( g_wv.hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
|
|
|
|
if (FAILED(hr)) {
|
|
Com_DPrintf ("iDirectInputCreate failed\n");
|
|
return qfalse;
|
|
}
|
|
|
|
// obtain an interface to the system mouse device.
|
|
hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
|
|
|
|
if (FAILED(hr)) {
|
|
Com_DPrintf ("Couldn't open DI mouse device\n");
|
|
return qfalse;
|
|
}
|
|
|
|
// set the data format to "mouse format".
|
|
hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df);
|
|
|
|
if (FAILED(hr)) {
|
|
Com_DPrintf ("Couldn't set DI mouse format\n");
|
|
return qfalse;
|
|
}
|
|
|
|
// set the cooperativity level.
|
|
hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, g_wv.hWnd,
|
|
DISCL_EXCLUSIVE | DISCL_FOREGROUND);
|
|
|
|
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=50
|
|
if (FAILED(hr)) {
|
|
Com_DPrintf ("Couldn't set DI coop level\n");
|
|
return qfalse;
|
|
}
|
|
|
|
|
|
// set the buffer size to DINPUT_BUFFERSIZE elements.
|
|
// the buffer size is a DWORD property associated with the device
|
|
{
|
|
const GUID *guid = (const GUID *)(size_t)(1);
|
|
hr = IDirectInputDevice_SetProperty( g_pMouse, guid, &dipdw.diph );
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
Com_DPrintf ("Couldn't set DI buffersize\n");
|
|
return qfalse;
|
|
}
|
|
|
|
// clear any pending samples
|
|
IN_DIMouse( &x, &y );
|
|
IN_DIMouse( &x, &y );
|
|
|
|
Com_DPrintf( "DirectInput initialized.\n");
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
IN_ShutdownDIMouse
|
|
==========================
|
|
*/
|
|
static void IN_ShutdownDIMouse( void ) {
|
|
if (g_pMouse) {
|
|
IDirectInputDevice_Release(g_pMouse);
|
|
g_pMouse = NULL;
|
|
}
|
|
|
|
if (g_pdi) {
|
|
IDirectInput_Release(g_pdi);
|
|
g_pdi = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
IN_ActivateDIMouse
|
|
==========================
|
|
*/
|
|
static void IN_ActivateDIMouse( void ) {
|
|
HRESULT hr;
|
|
|
|
if (!g_pMouse) {
|
|
return;
|
|
}
|
|
|
|
// we may fail to reacquire if the window has been recreated
|
|
hr = IDirectInputDevice_Acquire( g_pMouse );
|
|
if (FAILED(hr)) {
|
|
if ( !IN_InitDIMouse() ) {
|
|
Com_Printf ("Falling back to Win32 mouse support...\n");
|
|
Cvar_Set( "in_mouse", "-1" );
|
|
}
|
|
}
|
|
while (ShowCursor (FALSE) >= 0)
|
|
;
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
IN_DeactivateDIMouse
|
|
==========================
|
|
*/
|
|
static void IN_DeactivateDIMouse( void ) {
|
|
if (!g_pMouse) {
|
|
return;
|
|
}
|
|
IDirectInputDevice_Unacquire( g_pMouse );
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
IN_DIMouse
|
|
===================
|
|
*/
|
|
static void IN_DIMouse( int *mx, int *my ) {
|
|
DIDEVICEOBJECTDATA od;
|
|
DIMOUSESTATE state;
|
|
DWORD dwElements;
|
|
HRESULT hr;
|
|
int value;
|
|
|
|
if ( !g_pMouse ) {
|
|
return;
|
|
}
|
|
|
|
// fetch new events
|
|
for (;;)
|
|
{
|
|
dwElements = 1;
|
|
|
|
hr = IDirectInputDevice_GetDeviceData(g_pMouse,
|
|
sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
|
|
if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
|
|
IDirectInputDevice_Acquire(g_pMouse);
|
|
return;
|
|
}
|
|
|
|
/* Unable to read data or no data available */
|
|
if ( FAILED(hr) ) {
|
|
break;
|
|
}
|
|
|
|
if ( dwElements == 0 ) {
|
|
break;
|
|
}
|
|
|
|
switch (od.dwOfs) {
|
|
case DIMOFS_BUTTON0:
|
|
if (od.dwData & 0x80)
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE1, qtrue, 0, NULL );
|
|
else
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE1, qfalse, 0, NULL );
|
|
break;
|
|
|
|
case DIMOFS_BUTTON1:
|
|
if (od.dwData & 0x80)
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE2, qtrue, 0, NULL );
|
|
else
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE2, qfalse, 0, NULL );
|
|
break;
|
|
|
|
case DIMOFS_BUTTON2:
|
|
if (od.dwData & 0x80)
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE3, qtrue, 0, NULL );
|
|
else
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE3, qfalse, 0, NULL );
|
|
break;
|
|
|
|
case DIMOFS_BUTTON3:
|
|
if (od.dwData & 0x80)
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE4, qtrue, 0, NULL );
|
|
else
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE4, qfalse, 0, NULL );
|
|
break;
|
|
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=50
|
|
case DIMOFS_Z:
|
|
value = od.dwData;
|
|
if (value == 0) {
|
|
|
|
} else if (value < 0) {
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL );
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL );
|
|
} else {
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MWHEELUP, qtrue, 0, NULL );
|
|
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MWHEELUP, qfalse, 0, NULL );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// read the raw delta counter and ignore
|
|
// the individual sample time / values
|
|
hr = IDirectInputDevice_GetDeviceState(g_pMouse,
|
|
sizeof(DIDEVICEOBJECTDATA), &state);
|
|
if ( FAILED(hr) ) {
|
|
*mx = *my = 0;
|
|
return;
|
|
}
|
|
*mx = state.lX;
|
|
*my = state.lY;
|
|
}
|
|
|
|
/*
|
|
============================================================
|
|
|
|
MOUSE CONTROL
|
|
|
|
============================================================
|
|
*/
|
|
|
|
/*
|
|
===========
|
|
IN_ActivateMouse
|
|
|
|
Called when the window gains focus or changes in some way
|
|
===========
|
|
*/
|
|
static void IN_ActivateMouse( void )
|
|
{
|
|
if ( !s_wmv.mouseInitialized )
|
|
return;
|
|
|
|
if ( !in_mouse->integer ) {
|
|
s_wmv.mouseActive = qfalse;
|
|
return;
|
|
}
|
|
|
|
if ( s_wmv.mouseActive )
|
|
return;
|
|
|
|
s_wmv.mouseActive = qtrue;
|
|
|
|
if ( in_mouse->integer == -1 ) {
|
|
IN_ActivateWin32Mouse();
|
|
} else {
|
|
if ( raw_inited )
|
|
IN_ActivateRawMouse();
|
|
else
|
|
IN_ActivateDIMouse();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_DeactivateMouse
|
|
|
|
Called when the window loses focus
|
|
===========
|
|
*/
|
|
static void IN_DeactivateMouse( void )
|
|
{
|
|
if ( !s_wmv.mouseActive )
|
|
return;
|
|
|
|
if ( !s_wmv.mouseInitialized )
|
|
return;
|
|
|
|
s_wmv.oldButtonState = 0;
|
|
s_wmv.mouseActive = qfalse;
|
|
|
|
IN_DeactivateDIMouse();
|
|
IN_DeactivateRawMouse();
|
|
IN_DeactivateWin32Mouse();
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_StartupMouse
|
|
===========
|
|
*/
|
|
static void IN_StartupMouse( void )
|
|
{
|
|
s_wmv.mouseInitialized = qfalse;
|
|
|
|
if ( in_mouse->integer == 0 ) {
|
|
Com_DPrintf( "Mouse control not active.\n" );
|
|
return;
|
|
}
|
|
|
|
if ( in_mouse->integer == -1 ) {
|
|
Com_DPrintf( "Skipping check for Raw/DirectInput\n" );
|
|
} else {
|
|
|
|
if ( !g_wv.hWnd ) {
|
|
Com_Error( ERR_FATAL, "No window for mouse init" );
|
|
}
|
|
|
|
if ( IN_InitRawMouse() ) {
|
|
s_wmv.mouseInitialized = qtrue;
|
|
Com_DPrintf( "Raw mouse input initialized.\n" );
|
|
return;
|
|
}
|
|
|
|
if ( IN_InitDIMouse() ) {
|
|
s_wmv.mouseInitialized = qtrue;
|
|
return;
|
|
}
|
|
Com_DPrintf( "Falling back to Win32 mouse support...\n" );
|
|
}
|
|
|
|
s_wmv.mouseInitialized = qtrue;
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_Win32MouseEvent
|
|
===========
|
|
*/
|
|
void IN_Win32MouseEvent( int x, int y, int mstate )
|
|
{
|
|
int dx, dy;
|
|
|
|
if ( in_lagged->integer ) {
|
|
|
|
} else {
|
|
dx = x - g_wv.mouse.x;
|
|
dy = y - g_wv.mouse.y;
|
|
g_wv.mouse.x = x;
|
|
g_wv.mouse.y = y;
|
|
if ( dx || dy ) {
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_MOUSE, dx, dy, 0, NULL );
|
|
}
|
|
}
|
|
|
|
#define CHECK_BUTTON(button) \
|
|
if ( mstate & (1<<(button-1)) ) { \
|
|
if ( !(s_wmv.oldButtonState & (1<<(button-1))) ) \
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MOUSE##button, qtrue, 0, NULL ); \
|
|
} else { \
|
|
if ( s_wmv.oldButtonState & (1<<(button-1)) ) \
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MOUSE##button, qfalse, 0, NULL ); \
|
|
}
|
|
|
|
// perform button actions
|
|
CHECK_BUTTON(1);
|
|
CHECK_BUTTON(2);
|
|
CHECK_BUTTON(3);
|
|
CHECK_BUTTON(4);
|
|
CHECK_BUTTON(5);
|
|
|
|
#undef CHECK_BUTTON
|
|
|
|
s_wmv.oldButtonState = mstate;
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_RawMouseEvent
|
|
===========
|
|
*/
|
|
void IN_RawMouseEvent( LPARAM lParam )
|
|
{
|
|
UINT err, dwSize;
|
|
union {
|
|
BYTE lpb[40];
|
|
RAWINPUT raw;
|
|
} u;
|
|
|
|
dwSize = sizeof( u.raw );
|
|
|
|
err = GRID( (HRAWINPUT) lParam, RID_INPUT, &u.raw, &dwSize, sizeof( RAWINPUTHEADER ) );
|
|
if ( err == -1 )
|
|
return;
|
|
|
|
if ( u.raw.header.dwType != RIM_TYPEMOUSE || u.raw.data.mouse.usFlags != MOUSE_MOVE_RELATIVE )
|
|
return;
|
|
|
|
if ( u.raw.data.mouse.lLastX || u.raw.data.mouse.lLastY ) {
|
|
if ( in_lagged->integer ) {
|
|
g_wv.raw_mx += u.raw.data.mouse.lLastX;
|
|
g_wv.raw_my += u.raw.data.mouse.lLastY;
|
|
} else {
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_MOUSE, u.raw.data.mouse.lLastX,
|
|
u.raw.data.mouse.lLastY, 0, NULL );
|
|
}
|
|
}
|
|
|
|
if ( !u.raw.data.mouse.usButtonFlags )
|
|
return;
|
|
|
|
#define CHECK_RAW_BUTTON(button) \
|
|
if ( u.raw.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_##button##_DOWN ) \
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MOUSE##button, qtrue, 0, NULL ); \
|
|
if ( u.raw.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_##button##_UP ) \
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MOUSE##button, qfalse, 0, NULL )
|
|
|
|
CHECK_RAW_BUTTON(1);
|
|
CHECK_RAW_BUTTON(2);
|
|
CHECK_RAW_BUTTON(3);
|
|
CHECK_RAW_BUTTON(4);
|
|
CHECK_RAW_BUTTON(5);
|
|
|
|
#undef CHECK_RAW_BUTTON
|
|
|
|
if ( u.raw.data.mouse.usButtonFlags & RI_MOUSE_WHEEL )
|
|
{
|
|
short data = u.raw.data.mouse.usButtonData;
|
|
if ( data > 0 )
|
|
{
|
|
while( data > 0 )
|
|
{
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qtrue, 0, NULL );
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qfalse, 0, NULL );
|
|
data -= 120;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while( data < 0 )
|
|
{
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL );
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL );
|
|
data += 120;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_MouseMove
|
|
===========
|
|
*/
|
|
static void IN_MouseMove( void ) {
|
|
int mx = 0, my = 0;
|
|
|
|
if ( g_pMouse ) {
|
|
IN_DIMouse( &mx, &my );
|
|
} else {
|
|
if ( in_lagged->integer ) {
|
|
if ( raw_activated ) {
|
|
IN_RawMouse( &mx, &my );
|
|
} else {
|
|
IN_Win32Mouse( &mx, &my );
|
|
}
|
|
}
|
|
g_wv.raw_mx = 0;
|
|
g_wv.raw_my = 0;
|
|
|
|
// force the mouse to the center, so there's room to move
|
|
if ( in_mouse->integer == -1 )
|
|
SetCursorPos( window_center.x, window_center.y );
|
|
|
|
// reset delta base
|
|
g_wv.mouse = client_center;
|
|
}
|
|
|
|
if ( !mx && !my ) {
|
|
return;
|
|
}
|
|
|
|
Sys_QueEvent( 0, SE_MOUSE, mx, my, 0, NULL );
|
|
}
|
|
|
|
|
|
/*
|
|
in_minimize processing
|
|
*/
|
|
|
|
extern int HotKey;
|
|
extern void Win_RemoveHotkey( void );
|
|
extern void Win_AddHotkey( void );
|
|
|
|
extern int Win32_GetKey( const char **s, char *buf, int buflen );
|
|
|
|
/*
|
|
=========================================================================
|
|
|
|
=========================================================================
|
|
*/
|
|
static void IN_GetHotkey( cvar_t *var, int *pHotKey ) {
|
|
|
|
char kset[256], buf[64];
|
|
const char *s;
|
|
int i, code;
|
|
|
|
if ( !pHotKey )
|
|
return;
|
|
|
|
*pHotKey = 0;
|
|
|
|
if ( !var )
|
|
return;
|
|
|
|
s = var->string;
|
|
|
|
if ( !s ) {
|
|
Win_RemoveHotkey();
|
|
return;
|
|
}
|
|
|
|
memset( kset, 0, sizeof( kset ) );
|
|
|
|
for ( i = 0; i < 4; i++ )
|
|
{
|
|
code = Win32_GetKey( &s, buf, sizeof( buf ) );
|
|
if ( code == 0 ) // no more tokens
|
|
break;
|
|
if ( code < 0 || kset[code & 0xFF ] ||
|
|
( code != VK_CONTROL && code != VK_LCONTROL && code != VK_RCONTROL
|
|
&& code != VK_MENU && code != VK_LMENU && code != VK_RMENU
|
|
&& code != VK_SHIFT && code != VK_LSHIFT && code != VK_RSHIFT
|
|
&& code != (VK_LWIN|HK_MOD_LWIN) && code != (VK_RWIN|HK_MOD_RWIN)
|
|
&& *pHotKey & 0xFF )) {
|
|
Com_Printf( "%s:"S_COLOR_YELLOW" invalid token %s\n", var->name, buf );
|
|
*pHotKey = 0;
|
|
break;
|
|
}
|
|
kset[code & 0xFF] = 1;
|
|
switch ( code ) {
|
|
case VK_MENU: *pHotKey |= HK_MOD_ALT; break;
|
|
case VK_LMENU: *pHotKey |= (HK_MOD_ALT|HK_MOD_LALT); break;
|
|
case VK_RMENU: *pHotKey |= (HK_MOD_ALT|HK_MOD_RALT); break;
|
|
case VK_CONTROL: *pHotKey |= HK_MOD_CONTROL; break;
|
|
case VK_LCONTROL:*pHotKey |= (HK_MOD_CONTROL|HK_MOD_LCONTROL); break;
|
|
case VK_RCONTROL:*pHotKey |= (HK_MOD_CONTROL|HK_MOD_RCONTROL); break;
|
|
case VK_SHIFT: *pHotKey |= HK_MOD_SHIFT; break;
|
|
case VK_LSHIFT: *pHotKey |= HK_MOD_SHIFT|HK_MOD_LSHIFT; break;
|
|
case VK_RSHIFT: *pHotKey |= HK_MOD_SHIFT|HK_MOD_RSHIFT; break;
|
|
case VK_LWIN: *pHotKey |= HK_MOD_WIN; break;
|
|
case (VK_LWIN|HK_MOD_LWIN): *pHotKey |= (HK_MOD_WIN|HK_MOD_LWIN); break;
|
|
case (VK_RWIN|HK_MOD_RWIN): *pHotKey |= (HK_MOD_WIN|HK_MOD_RWIN); break;
|
|
default: *pHotKey |= (code & 0xFF); break;
|
|
};
|
|
}
|
|
|
|
if ( i == 0 )
|
|
{
|
|
Win_RemoveHotkey();
|
|
return;
|
|
}
|
|
|
|
if ( *pHotKey == VK_OEM_3 // '~'
|
|
|| *pHotKey == VK_RETURN
|
|
|| *pHotKey == HK_MOD_WIN
|
|
|| *pHotKey == (HK_MOD_WIN|HK_MOD_LWIN)
|
|
|| *pHotKey == (HK_MOD_WIN|HK_MOD_RWIN)
|
|
|| *pHotKey == (VK_RETURN|HK_MOD_ALT)
|
|
|| *pHotKey == (HK_MOD_CONTROL|VK_PAUSE)) {
|
|
Com_Printf( "%s:"S_COLOR_YELLOW" invalid hotkey %s\n", var->name, var->string );
|
|
*pHotKey = 0;
|
|
}
|
|
|
|
//Com_Printf("GetHotkey: %06X\n",*HotKey);
|
|
Win_RemoveHotkey();
|
|
Win_AddHotkey();
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_Minimize
|
|
===========
|
|
*/
|
|
static void IN_Minimize( void )
|
|
{
|
|
if ( !CL_VideoRecording() || ( re.CanMinimize && re.CanMinimize() ) )
|
|
WIN_Minimize();
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_Startup
|
|
===========
|
|
*/
|
|
void IN_Startup( void ) {
|
|
Com_DPrintf( "\n------- Input Initialization -------\n" );
|
|
IN_StartupMouse();
|
|
#ifdef USE_JOYSTICK
|
|
IN_StartupJoystick ();
|
|
#endif
|
|
#ifdef USE_MIDI
|
|
IN_StartupMIDI();
|
|
#endif
|
|
Com_DPrintf( "------------------------------------\n" );
|
|
|
|
in_mouse->modified = qfalse;
|
|
#ifdef USE_JOYSTICK
|
|
in_joystick->modified = qfalse;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_Shutdown
|
|
===========
|
|
*/
|
|
void IN_Shutdown( void ) {
|
|
IN_DeactivateMouse();
|
|
IN_ShutdownDIMouse();
|
|
#ifdef USE_MIDI
|
|
IN_ShutdownMIDI();
|
|
Cmd_RemoveCommand( "midiinfo" );
|
|
#endif
|
|
Cmd_RemoveCommand( "minimize" );
|
|
Cmd_RemoveCommand( "in_restart" );
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_Init
|
|
===========
|
|
*/
|
|
void IN_Restart_f( void );
|
|
|
|
void IN_Init( void ) {
|
|
|
|
#ifdef USE_MIDI
|
|
// MIDI input controler variables
|
|
in_midi = Cvar_Get( "in_midi", "0", CVAR_ARCHIVE );
|
|
in_midiport = Cvar_Get( "in_midiport", "1", CVAR_ARCHIVE );
|
|
Cvar_SetDescription( in_midiport, "Toggle the use of a midi port as an input device." );
|
|
in_midichannel = Cvar_Get( "in_midichannel", "1", CVAR_ARCHIVE );
|
|
Cvar_SetDescription( in_midichannel, "Toggle the use of a midi channel as an input device." );
|
|
in_mididevice = Cvar_Get( "in_mididevice", "0", CVAR_ARCHIVE );
|
|
Cvar_SetDescription( in_mididevice, "Toggle the use of a midi device as an input device." );
|
|
Cmd_AddCommand( "midiinfo", MidiInfo_f );
|
|
#endif
|
|
|
|
#ifdef USE_JOYSTICK
|
|
// joystick variables
|
|
in_joystick = Cvar_Get( "in_joystick", "0", CVAR_ARCHIVE | CVAR_LATCH );
|
|
Cvar_SetDescription( in_joystick, "Whether or not joystick support is on." );
|
|
in_joyBallScale = Cvar_Get( "in_joyBallScale", "0.02", CVAR_ARCHIVE );
|
|
Cvar_SetDescription( in_joyBallScale, "Sets the scale of a joyball rotation to player model rotation." );
|
|
in_debugJoystick = Cvar_Get( "in_debugjoystick", "0", CVAR_TEMP );
|
|
joy_threshold = Cvar_Get( "joy_threshold", "0.15", CVAR_ARCHIVE );
|
|
Cvar_SetDescription( joy_threshold, "Threshold of joystick moving distance." );
|
|
#endif
|
|
|
|
// mouse variables
|
|
in_mouse = Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE |CVAR_LATCH );
|
|
Cvar_CheckRange( in_mouse, "-1", "1", CV_INTEGER );
|
|
Cvar_SetDescription( in_mouse,
|
|
"Mouse data input source:\n" \
|
|
" 0 - disable mouse input\n" \
|
|
" 1 - di/raw mouse\n" \
|
|
" -1 - win32 mouse" );
|
|
|
|
in_nograb = Cvar_Get( "in_nograb", "0", 0 );
|
|
Cvar_SetDescription( in_nograb, "Do not capture mouse in game, may be useful during online streaming." );
|
|
in_lagged = Cvar_Get( "in_lagged", "0", 0 );
|
|
Cvar_SetDescription( in_lagged,
|
|
"Mouse movement processing order:\n" \
|
|
" 0 - before rendering\n" \
|
|
" 1 - before framerate limiter" );
|
|
|
|
in_logitechbug = Cvar_Get( "in_logitechbug", "0", CVAR_ARCHIVE_ND );
|
|
Cvar_SetDescription( in_logitechbug, "Toggle the use of special code in the game that addresses a bug in the logitech mouse driver software." );
|
|
|
|
in_minimize = Cvar_Get( "in_minimize", "", CVAR_ARCHIVE | CVAR_LATCH );
|
|
Cvar_SetDescription( in_minimize, "Hotkey for minimize/restore main window." );
|
|
|
|
Cmd_AddCommand( "minimize", IN_Minimize );
|
|
Cmd_AddCommand( "in_restart", IN_Restart_f );
|
|
|
|
IN_GetHotkey( in_minimize, &HotKey );
|
|
|
|
IN_Startup();
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
IN_Activate
|
|
|
|
Called when the main window gains or loses focus.
|
|
The window may have been destroyed and recreated
|
|
between a deactivate and an activate.
|
|
===========
|
|
*/
|
|
void IN_Activate( qboolean active ) {
|
|
|
|
if ( !active ) {
|
|
IN_DeactivateMouse();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
IN_Frame
|
|
|
|
Called every frame, even if not generating commands
|
|
==================
|
|
*/
|
|
void IN_Frame( void ) {
|
|
// post joystick events
|
|
#ifdef USE_JOYSTICK
|
|
IN_JoyMove();
|
|
#endif
|
|
|
|
if ( !s_wmv.mouseInitialized ) {
|
|
return;
|
|
}
|
|
|
|
if ( Key_GetCatcher() & KEYCATCH_CONSOLE ) {
|
|
// temporarily deactivate if not in the game and
|
|
// running on the desktop with multimonitor configuration
|
|
if ( !glw_state.cdsFullscreen || glw_state.monitorCount > 1 ) {
|
|
IN_DeactivateMouse();
|
|
//WIN_EnableAltTab();
|
|
//WIN_DisableHook();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( !gw_active || gw_minimized || in_nograb->integer ) {
|
|
IN_DeactivateMouse();
|
|
return;
|
|
}
|
|
|
|
IN_ActivateMouse();
|
|
|
|
//WIN_DisableAltTab();
|
|
WIN_EnableHook();
|
|
|
|
// post events to the system que
|
|
IN_MouseMove();
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
In_Restart_f
|
|
|
|
Restart the input subsystem
|
|
=================
|
|
*/
|
|
void IN_Restart_f( void ) {
|
|
IN_Shutdown();
|
|
IN_Init();
|
|
}
|
|
|
|
|
|
/*
|
|
=========================================================================
|
|
|
|
JOYSTICK
|
|
|
|
=========================================================================
|
|
*/
|
|
|
|
#ifdef USE_JOYSTICK
|
|
/*
|
|
===============
|
|
IN_StartupJoystick
|
|
===============
|
|
*/
|
|
void IN_StartupJoystick (void) {
|
|
int numdevs;
|
|
MMRESULT mmr;
|
|
|
|
// assume no joystick
|
|
joy.avail = qfalse;
|
|
|
|
if (! in_joystick->integer ) {
|
|
Com_DPrintf ("Joystick is not active.\n");
|
|
return;
|
|
}
|
|
|
|
// verify joystick driver is present
|
|
if ((numdevs = joyGetNumDevs ()) == 0)
|
|
{
|
|
Com_DPrintf ("joystick not found -- driver not present\n");
|
|
return;
|
|
}
|
|
|
|
// cycle through the joystick ids for the first valid one
|
|
mmr = 0;
|
|
for (joy.id=0 ; joy.id<numdevs ; joy.id++)
|
|
{
|
|
Com_Memset (&joy.ji, 0, sizeof(joy.ji));
|
|
joy.ji.dwSize = sizeof(joy.ji);
|
|
joy.ji.dwFlags = JOY_RETURNCENTERED;
|
|
|
|
if ((mmr = joyGetPosEx (joy.id, &joy.ji)) == JOYERR_NOERROR)
|
|
break;
|
|
}
|
|
|
|
// abort startup if we didn't find a valid joystick
|
|
if (mmr != JOYERR_NOERROR)
|
|
{
|
|
Com_DPrintf ("joystick not found -- no valid joysticks (%x)\n", mmr);
|
|
return;
|
|
}
|
|
|
|
// get the capabilities of the selected joystick
|
|
// abort startup if command fails
|
|
Com_Memset (&joy.jc, 0, sizeof(joy.jc));
|
|
if ((mmr = joyGetDevCaps (joy.id, &joy.jc, sizeof(joy.jc))) != JOYERR_NOERROR)
|
|
{
|
|
Com_DPrintf ("joystick not found -- invalid joystick capabilities (%x)\n", mmr);
|
|
return;
|
|
}
|
|
|
|
Com_DPrintf( "Joystick found.\n" );
|
|
Com_DPrintf( "Pname: %s\n", joy.jc.szPname );
|
|
Com_DPrintf( "OemVxD: %s\n", joy.jc.szOEMVxD );
|
|
Com_DPrintf( "RegKey: %s\n", joy.jc.szRegKey );
|
|
|
|
Com_DPrintf( "Numbuttons: %i / %i\n", joy.jc.wNumButtons, joy.jc.wMaxButtons );
|
|
Com_DPrintf( "Axis: %i / %i\n", joy.jc.wNumAxes, joy.jc.wMaxAxes );
|
|
Com_DPrintf( "Caps: 0x%x\n", joy.jc.wCaps );
|
|
if ( joy.jc.wCaps & JOYCAPS_HASPOV ) {
|
|
Com_DPrintf( "HASPOV\n" );
|
|
} else {
|
|
Com_DPrintf( "no POV\n" );
|
|
}
|
|
|
|
// old button and POV states default to no buttons pressed
|
|
joy.oldbuttonstate = 0;
|
|
joy.oldpovstate = 0;
|
|
|
|
// mark the joystick as available
|
|
joy.avail = qtrue;
|
|
}
|
|
|
|
/*
|
|
===========
|
|
JoyToF
|
|
===========
|
|
*/
|
|
float JoyToF( int value ) {
|
|
float fValue;
|
|
|
|
// move centerpoint to zero
|
|
value -= 32768;
|
|
|
|
// convert range from -32768..32767 to -1..1
|
|
fValue = (float)value / 32768.0;
|
|
|
|
if ( fValue < -1 ) {
|
|
fValue = -1;
|
|
}
|
|
if ( fValue > 1 ) {
|
|
fValue = 1;
|
|
}
|
|
return fValue;
|
|
}
|
|
|
|
int JoyToI( int value ) {
|
|
// move centerpoint to zero
|
|
value -= 32768;
|
|
|
|
return value;
|
|
}
|
|
|
|
int joyDirectionKeys[16] = {
|
|
K_LEFTARROW, K_RIGHTARROW,
|
|
K_UPARROW, K_DOWNARROW,
|
|
K_JOY16, K_JOY17,
|
|
K_JOY18, K_JOY19,
|
|
K_JOY20, K_JOY21,
|
|
K_JOY22, K_JOY23,
|
|
|
|
K_JOY24, K_JOY25,
|
|
K_JOY26, K_JOY27
|
|
};
|
|
|
|
/*
|
|
===========
|
|
IN_JoyMove
|
|
===========
|
|
*/
|
|
void IN_JoyMove( void ) {
|
|
float fAxisValue;
|
|
int i;
|
|
DWORD buttonstate, povstate;
|
|
int x, y;
|
|
|
|
// verify joystick is available and that the user wants to use it
|
|
if ( !joy.avail ) {
|
|
return;
|
|
}
|
|
|
|
// collect the joystick data, if possible
|
|
Com_Memset (&joy.ji, 0, sizeof(joy.ji));
|
|
joy.ji.dwSize = sizeof(joy.ji);
|
|
joy.ji.dwFlags = JOY_RETURNALL;
|
|
|
|
if ( joyGetPosEx (joy.id, &joy.ji) != JOYERR_NOERROR ) {
|
|
// read error occurred
|
|
// turning off the joystick seems too harsh for 1 read error,
|
|
// but what should be done?
|
|
// Com_Printf ("IN_ReadJoystick: no response\n");
|
|
// joy.avail = false;
|
|
return;
|
|
}
|
|
|
|
if ( in_debugJoystick->integer ) {
|
|
Com_Printf( "%8x %5i %5.2f %5.2f %5.2f %5.2f %6i %6i\n",
|
|
JoyToI( joy.ji.dwButtons ),
|
|
JoyToI( joy.ji.dwPOV ),
|
|
JoyToF( joy.ji.dwXpos ), JoyToF( joy.ji.dwYpos ),
|
|
JoyToF( joy.ji.dwZpos ), JoyToF( joy.ji.dwRpos ),
|
|
JoyToI( joy.ji.dwUpos ), JoyToI( joy.ji.dwVpos ) );
|
|
}
|
|
|
|
// loop through the joystick buttons
|
|
// key a joystick event or auxiliary event for higher number buttons for each state change
|
|
buttonstate = joy.ji.dwButtons;
|
|
for ( i=0 ; i < joy.jc.wNumButtons ; i++ ) {
|
|
if ( (buttonstate & (1<<i)) && !(joy.oldbuttonstate & (1<<i)) ) {
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_JOY1 + i, qtrue, 0, NULL );
|
|
}
|
|
if ( !(buttonstate & (1<<i)) && (joy.oldbuttonstate & (1<<i)) ) {
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_JOY1 + i, qfalse, 0, NULL );
|
|
}
|
|
}
|
|
joy.oldbuttonstate = buttonstate;
|
|
|
|
povstate = 0;
|
|
|
|
// convert main joystick motion into 6 direction button bits
|
|
for (i = 0; i < joy.jc.wNumAxes && i < 4 ; i++) {
|
|
// get the floating point zero-centered, potentially-inverted data for the current axis
|
|
fAxisValue = JoyToF( (&joy.ji.dwXpos)[i] );
|
|
|
|
if ( fAxisValue < -joy_threshold->value ) {
|
|
povstate |= (1<<(i*2));
|
|
} else if ( fAxisValue > joy_threshold->value ) {
|
|
povstate |= (1<<(i*2+1));
|
|
}
|
|
}
|
|
|
|
// convert POV information from a direction into 4 button bits
|
|
if ( joy.jc.wCaps & JOYCAPS_HASPOV ) {
|
|
if ( joy.ji.dwPOV != JOY_POVCENTERED ) {
|
|
if (joy.ji.dwPOV == JOY_POVFORWARD)
|
|
povstate |= 1<<12;
|
|
if (joy.ji.dwPOV == JOY_POVBACKWARD)
|
|
povstate |= 1<<13;
|
|
if (joy.ji.dwPOV == JOY_POVRIGHT)
|
|
povstate |= 1<<14;
|
|
if (joy.ji.dwPOV == JOY_POVLEFT)
|
|
povstate |= 1<<15;
|
|
}
|
|
}
|
|
|
|
// determine which bits have changed and key an auxiliary event for each change
|
|
for (i=0 ; i < 16 ; i++) {
|
|
if ( (povstate & (1<<i)) && !(joy.oldpovstate & (1<<i)) ) {
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, joyDirectionKeys[i], qtrue, 0, NULL );
|
|
}
|
|
|
|
if ( !(povstate & (1<<i)) && (joy.oldpovstate & (1<<i)) ) {
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, joyDirectionKeys[i], qfalse, 0, NULL );
|
|
}
|
|
}
|
|
joy.oldpovstate = povstate;
|
|
|
|
// if there is a trackball like interface, simulate mouse moves
|
|
if ( joy.jc.wNumAxes >= 6 ) {
|
|
x = JoyToI( joy.ji.dwUpos ) * in_joyBallScale->value;
|
|
y = JoyToI( joy.ji.dwVpos ) * in_joyBallScale->value;
|
|
if ( x || y ) {
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_MOUSE, x, y, 0, NULL );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=========================================================================
|
|
|
|
MIDI
|
|
|
|
=========================================================================
|
|
*/
|
|
|
|
#ifdef USE_MIDI
|
|
static void MIDI_NoteOff( int note )
|
|
{
|
|
int qkey;
|
|
|
|
qkey = note - 60 + K_AUX1;
|
|
|
|
if ( qkey > 255 || qkey < K_AUX1 )
|
|
return;
|
|
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, qkey, qfalse, 0, NULL );
|
|
}
|
|
|
|
static void MIDI_NoteOn( int note, int velocity )
|
|
{
|
|
int qkey;
|
|
|
|
if ( velocity == 0 )
|
|
MIDI_NoteOff( note );
|
|
|
|
qkey = note - 60 + K_AUX1;
|
|
|
|
if ( qkey > 255 || qkey < K_AUX1 )
|
|
return;
|
|
|
|
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, qkey, qtrue, 0, NULL );
|
|
}
|
|
|
|
static void CALLBACK MidiInProc( HMIDIIN hMidiIn, UINT uMsg, DWORD dwInstance,
|
|
DWORD dwParam1, DWORD dwParam2 )
|
|
{
|
|
int message;
|
|
|
|
switch ( uMsg )
|
|
{
|
|
case MIM_OPEN:
|
|
break;
|
|
case MIM_CLOSE:
|
|
break;
|
|
case MIM_DATA:
|
|
message = dwParam1 & 0xff;
|
|
|
|
// note on
|
|
if ( ( message & 0xf0 ) == 0x90 )
|
|
{
|
|
if ( ( ( message & 0x0f ) + 1 ) == in_midichannel->integer )
|
|
MIDI_NoteOn( ( dwParam1 & 0xff00 ) >> 8, ( dwParam1 & 0xff0000 ) >> 16 );
|
|
}
|
|
else if ( ( message & 0xf0 ) == 0x80 )
|
|
{
|
|
if ( ( ( message & 0x0f ) + 1 ) == in_midichannel->integer )
|
|
MIDI_NoteOff( ( dwParam1 & 0xff00 ) >> 8 );
|
|
}
|
|
break;
|
|
case MIM_LONGDATA:
|
|
break;
|
|
case MIM_ERROR:
|
|
break;
|
|
case MIM_LONGERROR:
|
|
break;
|
|
}
|
|
|
|
// Sys_QueEvent( sys_msg_time, SE_KEY, wMsg, qtrue, 0, NULL );
|
|
}
|
|
|
|
static void MidiInfo_f( void )
|
|
{
|
|
int i;
|
|
|
|
const char *enableStrings[] = { "disabled", "enabled" };
|
|
|
|
Com_Printf( "\nMIDI control: %s\n", enableStrings[in_midi->integer != 0] );
|
|
Com_Printf( "port: %d\n", in_midiport->integer );
|
|
Com_Printf( "channel: %d\n", in_midichannel->integer );
|
|
Com_Printf( "current device: %d\n", in_mididevice->integer );
|
|
Com_Printf( "number of devices: %d\n", s_midiInfo.numDevices );
|
|
for ( i = 0; i < s_midiInfo.numDevices; i++ )
|
|
{
|
|
if ( i == Cvar_VariableIntegerValue( "in_mididevice" ) )
|
|
Com_Printf( "***" );
|
|
else
|
|
Com_Printf( "..." );
|
|
Com_Printf( "device %2d: %s\n", i, s_midiInfo.caps[i].szPname );
|
|
Com_Printf( "...manufacturer ID: 0x%hx\n", s_midiInfo.caps[i].wMid );
|
|
Com_Printf( "...product ID: 0x%hx\n", s_midiInfo.caps[i].wPid );
|
|
|
|
Com_Printf( "\n" );
|
|
}
|
|
}
|
|
|
|
static void IN_StartupMIDI( void )
|
|
{
|
|
int i;
|
|
|
|
if ( !Cvar_VariableIntegerValue( "in_midi" ) )
|
|
return;
|
|
|
|
//
|
|
// enumerate MIDI IN devices
|
|
//
|
|
s_midiInfo.numDevices = midiInGetNumDevs();
|
|
|
|
for ( i = 0; i < s_midiInfo.numDevices; i++ )
|
|
{
|
|
midiInGetDevCaps( i, &s_midiInfo.caps[i], sizeof( s_midiInfo.caps[i] ) );
|
|
}
|
|
|
|
//
|
|
// open the MIDI IN port
|
|
//
|
|
if ( midiInOpen( &s_midiInfo.hMidiIn,
|
|
in_mididevice->integer,
|
|
( unsigned long ) MidiInProc,
|
|
( unsigned long ) NULL,
|
|
CALLBACK_FUNCTION ) != MMSYSERR_NOERROR )
|
|
{
|
|
Com_DPrintf( "WARNING: could not open MIDI device %d: '%s'\n",
|
|
in_mididevice->integer , s_midiInfo.caps[( int ) in_mididevice->value].szPname );
|
|
return;
|
|
}
|
|
|
|
midiInStart( s_midiInfo.hMidiIn );
|
|
}
|
|
|
|
static void IN_ShutdownMIDI( void )
|
|
{
|
|
if ( s_midiInfo.hMidiIn )
|
|
{
|
|
midiInClose( s_midiInfo.hMidiIn );
|
|
}
|
|
Com_Memset( &s_midiInfo, 0, sizeof( s_midiInfo ) );
|
|
}
|
|
#endif
|