Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

win_input.c (26325B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 // win_input.c -- win32 mouse and joystick code
     23 // 02/21/97 JCB Added extended DirectInput code to support external controllers.
     24 
     25 #include "../client/client.h"
     26 #include "win_local.h"
     27 
     28 
     29 typedef struct {
     30 	int			oldButtonState;
     31 
     32 	qboolean	mouseActive;
     33 	qboolean	mouseInitialized;
     34   qboolean  mouseStartupDelayed; // delay mouse init to try DI again when we have a window
     35 } WinMouseVars_t;
     36 
     37 static WinMouseVars_t s_wmv;
     38 
     39 static int	window_center_x, window_center_y;
     40 
     41 //
     42 // MIDI definitions
     43 //
     44 static void IN_StartupMIDI( void );
     45 static void IN_ShutdownMIDI( void );
     46 
     47 #define MAX_MIDIIN_DEVICES	8
     48 
     49 typedef struct {
     50 	int			numDevices;
     51 	MIDIINCAPS	caps[MAX_MIDIIN_DEVICES];
     52 
     53 	HMIDIIN		hMidiIn;
     54 } MidiInfo_t;
     55 
     56 static MidiInfo_t s_midiInfo;
     57 
     58 //
     59 // Joystick definitions
     60 //
     61 #define	JOY_MAX_AXES		6				// X, Y, Z, R, U, V
     62 
     63 typedef struct {
     64 	qboolean	avail;
     65 	int			id;			// joystick number
     66 	JOYCAPS		jc;
     67 
     68 	int			oldbuttonstate;
     69 	int			oldpovstate;
     70 
     71 	JOYINFOEX	ji;
     72 } joystickInfo_t;
     73 
     74 static	joystickInfo_t	joy;
     75 
     76 
     77 
     78 cvar_t	*in_midi;
     79 cvar_t	*in_midiport;
     80 cvar_t	*in_midichannel;
     81 cvar_t	*in_mididevice;
     82 
     83 cvar_t	*in_mouse;
     84 cvar_t  *in_logitechbug;
     85 cvar_t	*in_joystick;
     86 cvar_t	*in_joyBallScale;
     87 cvar_t	*in_debugJoystick;
     88 cvar_t	*joy_threshold;
     89 
     90 qboolean	in_appactive;
     91 
     92 // forward-referenced functions
     93 void IN_StartupJoystick (void);
     94 void IN_JoyMove(void);
     95 
     96 static void MidiInfo_f( void );
     97 
     98 /*
     99 ============================================================
    100 
    101 WIN32 MOUSE CONTROL
    102 
    103 ============================================================
    104 */
    105 
    106 /*
    107 ================
    108 IN_InitWin32Mouse
    109 ================
    110 */
    111 void IN_InitWin32Mouse( void ) 
    112 {
    113 }
    114 
    115 /*
    116 ================
    117 IN_ShutdownWin32Mouse
    118 ================
    119 */
    120 void IN_ShutdownWin32Mouse( void ) {
    121 }
    122 
    123 /*
    124 ================
    125 IN_ActivateWin32Mouse
    126 ================
    127 */
    128 void IN_ActivateWin32Mouse( void ) {
    129 	int			width, height;
    130 	RECT		window_rect;
    131 
    132 	width = GetSystemMetrics (SM_CXSCREEN);
    133 	height = GetSystemMetrics (SM_CYSCREEN);
    134 
    135 	GetWindowRect ( g_wv.hWnd, &window_rect);
    136 	if (window_rect.left < 0)
    137 		window_rect.left = 0;
    138 	if (window_rect.top < 0)
    139 		window_rect.top = 0;
    140 	if (window_rect.right >= width)
    141 		window_rect.right = width-1;
    142 	if (window_rect.bottom >= height-1)
    143 		window_rect.bottom = height-1;
    144 	window_center_x = (window_rect.right + window_rect.left)/2;
    145 	window_center_y = (window_rect.top + window_rect.bottom)/2;
    146 
    147 	SetCursorPos (window_center_x, window_center_y);
    148 
    149 	SetCapture ( g_wv.hWnd );
    150 	ClipCursor (&window_rect);
    151 	while (ShowCursor (FALSE) >= 0)
    152 		;
    153 }
    154 
    155 /*
    156 ================
    157 IN_DeactivateWin32Mouse
    158 ================
    159 */
    160 void IN_DeactivateWin32Mouse( void ) 
    161 {
    162 	ClipCursor (NULL);
    163 	ReleaseCapture ();
    164 	while (ShowCursor (TRUE) < 0)
    165 		;
    166 }
    167 
    168 /*
    169 ================
    170 IN_Win32Mouse
    171 ================
    172 */
    173 void IN_Win32Mouse( int *mx, int *my ) {
    174 	POINT		current_pos;
    175 
    176 	// find mouse movement
    177 	GetCursorPos (&current_pos);
    178 
    179 	// force the mouse to the center, so there's room to move
    180 	SetCursorPos (window_center_x, window_center_y);
    181 
    182 	*mx = current_pos.x - window_center_x;
    183 	*my = current_pos.y - window_center_y;
    184 }
    185 
    186 
    187 /*
    188 ============================================================
    189 
    190 DIRECT INPUT MOUSE CONTROL
    191 
    192 ============================================================
    193 */
    194 
    195 #undef DEFINE_GUID
    196 
    197 #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
    198         EXTERN_C const GUID name \
    199                 = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
    200 
    201 DEFINE_GUID(GUID_SysMouse,   0x6F1D2B60,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00);
    202 DEFINE_GUID(GUID_XAxis,   0xA36D02E0,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00);
    203 DEFINE_GUID(GUID_YAxis,   0xA36D02E1,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00);
    204 DEFINE_GUID(GUID_ZAxis,   0xA36D02E2,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00);
    205 
    206 
    207 #define DINPUT_BUFFERSIZE           16
    208 #define iDirectInputCreate(a,b,c,d)	pDirectInputCreate(a,b,c,d)
    209 
    210 HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion,
    211 	LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);
    212 
    213 static HINSTANCE hInstDI;
    214 
    215 typedef struct MYDATA {
    216 	LONG  lX;                   // X axis goes here
    217 	LONG  lY;                   // Y axis goes here
    218 	LONG  lZ;                   // Z axis goes here
    219 	BYTE  bButtonA;             // One button goes here
    220 	BYTE  bButtonB;             // Another button goes here
    221 	BYTE  bButtonC;             // Another button goes here
    222 	BYTE  bButtonD;             // Another button goes here
    223 } MYDATA;
    224 
    225 static DIOBJECTDATAFORMAT rgodf[] = {
    226   { &GUID_XAxis,    FIELD_OFFSET(MYDATA, lX),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
    227   { &GUID_YAxis,    FIELD_OFFSET(MYDATA, lY),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
    228   { &GUID_ZAxis,    FIELD_OFFSET(MYDATA, lZ),       0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
    229   { 0,              FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
    230   { 0,              FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
    231   { 0,              FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
    232   { 0,              FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
    233 };
    234 
    235 #define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
    236 
    237 // NOTE TTimo: would be easier using c_dfDIMouse or c_dfDIMouse2 
    238 static DIDATAFORMAT	df = {
    239 	sizeof(DIDATAFORMAT),       // this structure
    240 	sizeof(DIOBJECTDATAFORMAT), // size of object data format
    241 	DIDF_RELAXIS,               // absolute axis coordinates
    242 	sizeof(MYDATA),             // device data size
    243 	NUM_OBJECTS,                // number of objects
    244 	rgodf,                      // and here they are
    245 };
    246 
    247 static LPDIRECTINPUT		g_pdi;
    248 static LPDIRECTINPUTDEVICE	g_pMouse;
    249 
    250 void IN_DIMouse( int *mx, int *my );
    251 
    252 /*
    253 ========================
    254 IN_InitDIMouse
    255 ========================
    256 */
    257 qboolean IN_InitDIMouse( void ) {
    258     HRESULT		hr;
    259 	int			x, y;
    260 	DIPROPDWORD	dipdw = {
    261 		{
    262 			sizeof(DIPROPDWORD),        // diph.dwSize
    263 			sizeof(DIPROPHEADER),       // diph.dwHeaderSize
    264 			0,                          // diph.dwObj
    265 			DIPH_DEVICE,                // diph.dwHow
    266 		},
    267 		DINPUT_BUFFERSIZE,              // dwData
    268 	};
    269 
    270 	Com_Printf( "Initializing DirectInput...\n");
    271 
    272 	if (!hInstDI) {
    273 		hInstDI = LoadLibrary("dinput.dll");
    274 		
    275 		if (hInstDI == NULL) {
    276 			Com_Printf ("Couldn't load dinput.dll\n");
    277 			return qfalse;
    278 		}
    279 	}
    280 
    281 	if (!pDirectInputCreate) {
    282 		pDirectInputCreate = (long (__stdcall *)(void *,unsigned long ,struct IDirectInputA ** ,struct IUnknown *))
    283 			GetProcAddress(hInstDI,"DirectInputCreateA");
    284 
    285 		if (!pDirectInputCreate) {
    286 			Com_Printf ("Couldn't get DI proc addr\n");
    287 			return qfalse;
    288 		}
    289 	}
    290 
    291 	// register with DirectInput and get an IDirectInput to play with.
    292 	hr = iDirectInputCreate( g_wv.hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
    293 
    294 	if (FAILED(hr)) {
    295 		Com_Printf ("iDirectInputCreate failed\n");
    296 		return qfalse;
    297 	}
    298 
    299 	// obtain an interface to the system mouse device.
    300 	hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
    301 
    302 	if (FAILED(hr)) {
    303 		Com_Printf ("Couldn't open DI mouse device\n");
    304 		return qfalse;
    305 	}
    306 
    307 	// set the data format to "mouse format".
    308 	hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df);
    309 
    310 	if (FAILED(hr)) 	{
    311 		Com_Printf ("Couldn't set DI mouse format\n");
    312 		return qfalse;
    313 	}
    314 
    315 	// set the cooperativity level.
    316 	hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, g_wv.hWnd,
    317 			DISCL_EXCLUSIVE | DISCL_FOREGROUND);
    318 
    319 	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=50
    320 	if (FAILED(hr)) {
    321 		Com_Printf ("Couldn't set DI coop level\n");
    322 		return qfalse;
    323 	}
    324 
    325 
    326 	// set the buffer size to DINPUT_BUFFERSIZE elements.
    327 	// the buffer size is a DWORD property associated with the device
    328 	hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);
    329 
    330 	if (FAILED(hr)) {
    331 		Com_Printf ("Couldn't set DI buffersize\n");
    332 		return qfalse;
    333 	}
    334 
    335 	// clear any pending samples
    336 	IN_DIMouse( &x, &y );
    337 	IN_DIMouse( &x, &y );
    338 
    339 	Com_Printf( "DirectInput initialized.\n");
    340 	return qtrue;
    341 }
    342 
    343 /*
    344 ==========================
    345 IN_ShutdownDIMouse
    346 ==========================
    347 */
    348 void IN_ShutdownDIMouse( void ) {
    349     if (g_pMouse) {
    350 		IDirectInputDevice_Release(g_pMouse);
    351 		g_pMouse = NULL;
    352 	}
    353 
    354     if (g_pdi) {
    355 		IDirectInput_Release(g_pdi);
    356 		g_pdi = NULL;
    357 	}
    358 }
    359 
    360 /*
    361 ==========================
    362 IN_ActivateDIMouse
    363 ==========================
    364 */
    365 void IN_ActivateDIMouse( void ) {
    366 	HRESULT		hr;
    367 
    368 	if (!g_pMouse) {
    369 		return;
    370 	}
    371 
    372 	// we may fail to reacquire if the window has been recreated
    373 	hr = IDirectInputDevice_Acquire( g_pMouse );
    374 	if (FAILED(hr)) {
    375 		if ( !IN_InitDIMouse() ) {
    376 			Com_Printf ("Falling back to Win32 mouse support...\n");
    377 			Cvar_Set( "in_mouse", "-1" );
    378 		}
    379 	}
    380 }
    381 
    382 /*
    383 ==========================
    384 IN_DeactivateDIMouse
    385 ==========================
    386 */
    387 void IN_DeactivateDIMouse( void ) {
    388 	if (!g_pMouse) {
    389 		return;
    390 	}
    391 	IDirectInputDevice_Unacquire( g_pMouse );
    392 }
    393 
    394 
    395 /*
    396 ===================
    397 IN_DIMouse
    398 ===================
    399 */
    400 void IN_DIMouse( int *mx, int *my ) {
    401 	DIDEVICEOBJECTDATA	od;
    402 	DIMOUSESTATE		state;
    403 	DWORD				dwElements;
    404 	HRESULT				hr;
    405   int value;
    406 	static float		oldSysTime;
    407 
    408 	if ( !g_pMouse ) {
    409 		return;
    410 	}
    411 
    412 	// fetch new events
    413 	for (;;)
    414 	{
    415 		dwElements = 1;
    416 
    417 		hr = IDirectInputDevice_GetDeviceData(g_pMouse,
    418 				sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
    419 		if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
    420 			IDirectInputDevice_Acquire(g_pMouse);
    421 			return;
    422 		}
    423 
    424 		/* Unable to read data or no data available */
    425 		if ( FAILED(hr) ) {
    426 			break;
    427 		}
    428 
    429 		if ( dwElements == 0 ) {
    430 			break;
    431 		}
    432 
    433 		switch (od.dwOfs) {
    434 		case DIMOFS_BUTTON0:
    435 			if (od.dwData & 0x80)
    436 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE1, qtrue, 0, NULL );
    437 			else
    438 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE1, qfalse, 0, NULL );
    439 			break;
    440 
    441 		case DIMOFS_BUTTON1:
    442 			if (od.dwData & 0x80)
    443 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE2, qtrue, 0, NULL );
    444 			else
    445 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE2, qfalse, 0, NULL );
    446 			break;
    447 			
    448 		case DIMOFS_BUTTON2:
    449 			if (od.dwData & 0x80)
    450 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE3, qtrue, 0, NULL );
    451 			else
    452 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE3, qfalse, 0, NULL );
    453 			break;
    454 
    455 		case DIMOFS_BUTTON3:
    456 			if (od.dwData & 0x80)
    457 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE4, qtrue, 0, NULL );
    458 			else
    459 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE4, qfalse, 0, NULL );
    460 			break;      
    461     // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=50
    462 		case DIMOFS_Z:
    463 			value = od.dwData;
    464 			if (value == 0) {
    465 
    466 			} else if (value < 0) {
    467 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL );
    468 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL );
    469 			} else {
    470 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MWHEELUP, qtrue, 0, NULL );
    471 				Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MWHEELUP, qfalse, 0, NULL );
    472 			}
    473 			break;
    474 		}
    475 	}
    476 
    477 	// read the raw delta counter and ignore
    478 	// the individual sample time / values
    479 	hr = IDirectInputDevice_GetDeviceState(g_pMouse,
    480 			sizeof(DIDEVICEOBJECTDATA), &state);
    481 	if ( FAILED(hr) ) {
    482 		*mx = *my = 0;
    483 		return;
    484 	}
    485 	*mx = state.lX;
    486 	*my = state.lY;
    487 }
    488 
    489 /*
    490 ============================================================
    491 
    492   MOUSE CONTROL
    493 
    494 ============================================================
    495 */
    496 
    497 /*
    498 ===========
    499 IN_ActivateMouse
    500 
    501 Called when the window gains focus or changes in some way
    502 ===========
    503 */
    504 void IN_ActivateMouse( void ) 
    505 {
    506 	if (!s_wmv.mouseInitialized ) {
    507 		return;
    508 	}
    509 	if ( !in_mouse->integer ) 
    510 	{
    511 		s_wmv.mouseActive = qfalse;
    512 		return;
    513 	}
    514 	if ( s_wmv.mouseActive ) 
    515 	{
    516 		return;
    517 	}
    518 
    519 	s_wmv.mouseActive = qtrue;
    520 
    521 	if ( in_mouse->integer != -1 ) {
    522 		IN_ActivateDIMouse();
    523 	}
    524 	IN_ActivateWin32Mouse();
    525 }
    526 
    527 
    528 /*
    529 ===========
    530 IN_DeactivateMouse
    531 
    532 Called when the window loses focus
    533 ===========
    534 */
    535 void IN_DeactivateMouse( void ) {
    536 	if (!s_wmv.mouseInitialized ) {
    537 		return;
    538 	}
    539 	if (!s_wmv.mouseActive ) {
    540 		return;
    541 	}
    542 	s_wmv.mouseActive = qfalse;
    543 
    544 	IN_DeactivateDIMouse();
    545 	IN_DeactivateWin32Mouse();
    546 }
    547 
    548 
    549 
    550 /*
    551 ===========
    552 IN_StartupMouse
    553 ===========
    554 */
    555 void IN_StartupMouse( void ) 
    556 {
    557 	s_wmv.mouseInitialized = qfalse;
    558   s_wmv.mouseStartupDelayed = qfalse;
    559 
    560 	if ( in_mouse->integer == 0 ) {
    561 		Com_Printf ("Mouse control not active.\n");
    562 		return;
    563 	}
    564 
    565 	// nt4.0 direct input is screwed up
    566 	if ( ( g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ) &&
    567 		 ( g_wv.osversion.dwMajorVersion == 4 ) )
    568 	{
    569 		Com_Printf ("Disallowing DirectInput on NT 4.0\n");
    570 		Cvar_Set( "in_mouse", "-1" );
    571 	}
    572 
    573 	if ( in_mouse->integer == -1 ) {
    574 		Com_Printf ("Skipping check for DirectInput\n");
    575 	} else {
    576     if (!g_wv.hWnd)
    577     {
    578       Com_Printf ("No window for DirectInput mouse init, delaying\n");
    579       s_wmv.mouseStartupDelayed = qtrue;
    580       return;
    581     }
    582 		if ( IN_InitDIMouse() ) {
    583 	    s_wmv.mouseInitialized = qtrue;
    584 			return;
    585 		}
    586 		Com_Printf ("Falling back to Win32 mouse support...\n");
    587 	}
    588 	s_wmv.mouseInitialized = qtrue;
    589 	IN_InitWin32Mouse();
    590 }
    591 
    592 /*
    593 ===========
    594 IN_MouseEvent
    595 ===========
    596 */
    597 void IN_MouseEvent (int mstate)
    598 {
    599 	int		i;
    600 
    601 	if ( !s_wmv.mouseInitialized )
    602 		return;
    603 
    604 // perform button actions
    605 	for  (i = 0 ; i < 3 ; i++ )
    606 	{
    607 		if ( (mstate & (1<<i)) &&
    608 			!(s_wmv.oldButtonState & (1<<i)) )
    609 		{
    610 			Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MOUSE1 + i, qtrue, 0, NULL );
    611 		}
    612 
    613 		if ( !(mstate & (1<<i)) &&
    614 			(s_wmv.oldButtonState & (1<<i)) )
    615 		{
    616 			Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MOUSE1 + i, qfalse, 0, NULL );
    617 		}
    618 	}	
    619 
    620 	s_wmv.oldButtonState = mstate;
    621 }
    622 
    623 
    624 /*
    625 ===========
    626 IN_MouseMove
    627 ===========
    628 */
    629 void IN_MouseMove ( void ) {
    630 	int		mx, my;
    631 
    632 	if ( g_pMouse ) {
    633 		IN_DIMouse( &mx, &my );
    634 	} else {
    635 		IN_Win32Mouse( &mx, &my );
    636 	}
    637 
    638 	if ( !mx && !my ) {
    639 		return;
    640 	}
    641 
    642 	Sys_QueEvent( 0, SE_MOUSE, mx, my, 0, NULL );
    643 }
    644 
    645 
    646 /*
    647 =========================================================================
    648 
    649 =========================================================================
    650 */
    651 
    652 /*
    653 ===========
    654 IN_Startup
    655 ===========
    656 */
    657 void IN_Startup( void ) {
    658 	Com_Printf ("\n------- Input Initialization -------\n");
    659 	IN_StartupMouse ();
    660 	IN_StartupJoystick ();
    661 	IN_StartupMIDI();
    662 	Com_Printf ("------------------------------------\n");
    663 
    664 	in_mouse->modified = qfalse;
    665 	in_joystick->modified = qfalse;
    666 }
    667 
    668 /*
    669 ===========
    670 IN_Shutdown
    671 ===========
    672 */
    673 void IN_Shutdown( void ) {
    674 	IN_DeactivateMouse();
    675 	IN_ShutdownDIMouse();
    676 	IN_ShutdownMIDI();
    677 	Cmd_RemoveCommand("midiinfo" );
    678 }
    679 
    680 
    681 /*
    682 ===========
    683 IN_Init
    684 ===========
    685 */
    686 void IN_Init( void ) {
    687 	// MIDI input controler variables
    688 	in_midi					= Cvar_Get ("in_midi",					"0",		CVAR_ARCHIVE);
    689 	in_midiport				= Cvar_Get ("in_midiport",				"1",		CVAR_ARCHIVE);
    690 	in_midichannel			= Cvar_Get ("in_midichannel",			"1",		CVAR_ARCHIVE);
    691 	in_mididevice			= Cvar_Get ("in_mididevice",			"0",		CVAR_ARCHIVE);
    692 
    693 	Cmd_AddCommand( "midiinfo", MidiInfo_f );
    694 
    695 	// mouse variables
    696   in_mouse				= Cvar_Get ("in_mouse",					"1",		CVAR_ARCHIVE|CVAR_LATCH);
    697 	in_logitechbug  = Cvar_Get ("in_logitechbug", "0", CVAR_ARCHIVE);
    698 
    699 	// joystick variables
    700 	in_joystick				= Cvar_Get ("in_joystick",				"0",		CVAR_ARCHIVE|CVAR_LATCH);
    701 	in_joyBallScale			= Cvar_Get ("in_joyBallScale",			"0.02",		CVAR_ARCHIVE);
    702 	in_debugJoystick		= Cvar_Get ("in_debugjoystick",			"0",		CVAR_TEMP);
    703 
    704 	joy_threshold			= Cvar_Get ("joy_threshold",			"0.15",		CVAR_ARCHIVE);
    705 
    706 	IN_Startup();
    707 }
    708 
    709 
    710 /*
    711 ===========
    712 IN_Activate
    713 
    714 Called when the main window gains or loses focus.
    715 The window may have been destroyed and recreated
    716 between a deactivate and an activate.
    717 ===========
    718 */
    719 void IN_Activate (qboolean active) {
    720 	in_appactive = active;
    721 
    722 	if ( !active )
    723 	{
    724 		IN_DeactivateMouse();
    725 	}
    726 }
    727 
    728 
    729 /*
    730 ==================
    731 IN_Frame
    732 
    733 Called every frame, even if not generating commands
    734 ==================
    735 */
    736 void IN_Frame (void) {
    737 	// post joystick events
    738 	IN_JoyMove();
    739 
    740 	if ( !s_wmv.mouseInitialized ) {
    741     if (s_wmv.mouseStartupDelayed && g_wv.hWnd)
    742 		{
    743 			Com_Printf("Proceeding with delayed mouse init\n");
    744       IN_StartupMouse();
    745 			s_wmv.mouseStartupDelayed = qfalse;
    746 		}
    747 		return;
    748 	}
    749 
    750 	if ( cls.keyCatchers & KEYCATCH_CONSOLE ) {
    751 		// temporarily deactivate if not in the game and
    752 		// running on the desktop
    753 		// voodoo always counts as full screen
    754 		if (Cvar_VariableValue ("r_fullscreen") == 0
    755 			&& strcmp( Cvar_VariableString("r_glDriver"), _3DFX_DRIVER_NAME) )	{
    756 			IN_DeactivateMouse ();
    757 			return;
    758 		}
    759 	}
    760 
    761 	if ( !in_appactive ) {
    762 		IN_DeactivateMouse ();
    763 		return;
    764 	}
    765 
    766 	IN_ActivateMouse();
    767 
    768 	// post events to the system que
    769 	IN_MouseMove();
    770 
    771 }
    772 
    773 
    774 /*
    775 ===================
    776 IN_ClearStates
    777 ===================
    778 */
    779 void IN_ClearStates (void) 
    780 {
    781 	s_wmv.oldButtonState = 0;
    782 }
    783 
    784 
    785 /*
    786 =========================================================================
    787 
    788 JOYSTICK
    789 
    790 =========================================================================
    791 */
    792 
    793 /* 
    794 =============== 
    795 IN_StartupJoystick 
    796 =============== 
    797 */  
    798 void IN_StartupJoystick (void) { 
    799 	int			numdevs;
    800 	MMRESULT	mmr;
    801 
    802 	// assume no joystick
    803 	joy.avail = qfalse; 
    804 
    805 	if (! in_joystick->integer ) {
    806 		Com_Printf ("Joystick is not active.\n");
    807 		return;
    808 	}
    809 
    810 	// verify joystick driver is present
    811 	if ((numdevs = joyGetNumDevs ()) == 0)
    812 	{
    813 		Com_Printf ("joystick not found -- driver not present\n");
    814 		return;
    815 	}
    816 
    817 	// cycle through the joystick ids for the first valid one
    818 	mmr = 0;
    819 	for (joy.id=0 ; joy.id<numdevs ; joy.id++)
    820 	{
    821 		Com_Memset (&joy.ji, 0, sizeof(joy.ji));
    822 		joy.ji.dwSize = sizeof(joy.ji);
    823 		joy.ji.dwFlags = JOY_RETURNCENTERED;
    824 
    825 		if ((mmr = joyGetPosEx (joy.id, &joy.ji)) == JOYERR_NOERROR)
    826 			break;
    827 	} 
    828 
    829 	// abort startup if we didn't find a valid joystick
    830 	if (mmr != JOYERR_NOERROR)
    831 	{
    832 		Com_Printf ("joystick not found -- no valid joysticks (%x)\n", mmr);
    833 		return;
    834 	}
    835 
    836 	// get the capabilities of the selected joystick
    837 	// abort startup if command fails
    838 	Com_Memset (&joy.jc, 0, sizeof(joy.jc));
    839 	if ((mmr = joyGetDevCaps (joy.id, &joy.jc, sizeof(joy.jc))) != JOYERR_NOERROR)
    840 	{
    841 		Com_Printf ("joystick not found -- invalid joystick capabilities (%x)\n", mmr); 
    842 		return;
    843 	}
    844 
    845 	Com_Printf( "Joystick found.\n" );
    846 	Com_Printf( "Pname: %s\n", joy.jc.szPname );
    847 	Com_Printf( "OemVxD: %s\n", joy.jc.szOEMVxD );
    848 	Com_Printf( "RegKey: %s\n", joy.jc.szRegKey );
    849 
    850 	Com_Printf( "Numbuttons: %i / %i\n", joy.jc.wNumButtons, joy.jc.wMaxButtons );
    851 	Com_Printf( "Axis: %i / %i\n", joy.jc.wNumAxes, joy.jc.wMaxAxes );
    852 	Com_Printf( "Caps: 0x%x\n", joy.jc.wCaps );
    853 	if ( joy.jc.wCaps & JOYCAPS_HASPOV ) {
    854 		Com_Printf( "HASPOV\n" );
    855 	} else {
    856 		Com_Printf( "no POV\n" );
    857 	}
    858 
    859 	// old button and POV states default to no buttons pressed
    860 	joy.oldbuttonstate = 0;
    861 	joy.oldpovstate = 0;
    862 
    863 	// mark the joystick as available
    864 	joy.avail = qtrue; 
    865 }
    866 
    867 /*
    868 ===========
    869 JoyToF
    870 ===========
    871 */
    872 float JoyToF( int value ) {
    873 	float	fValue;
    874 
    875 	// move centerpoint to zero
    876 	value -= 32768;
    877 
    878 	// convert range from -32768..32767 to -1..1 
    879 	fValue = (float)value / 32768.0;
    880 
    881 	if ( fValue < -1 ) {
    882 		fValue = -1;
    883 	}
    884 	if ( fValue > 1 ) {
    885 		fValue = 1;
    886 	}
    887 	return fValue;
    888 }
    889 
    890 int JoyToI( int value ) {
    891 	// move centerpoint to zero
    892 	value -= 32768;
    893 
    894 	return value;
    895 }
    896 
    897 int	joyDirectionKeys[16] = {
    898 	K_LEFTARROW, K_RIGHTARROW,
    899 	K_UPARROW, K_DOWNARROW,
    900 	K_JOY16, K_JOY17,
    901 	K_JOY18, K_JOY19,
    902 	K_JOY20, K_JOY21,
    903 	K_JOY22, K_JOY23,
    904 
    905 	K_JOY24, K_JOY25,
    906 	K_JOY26, K_JOY27
    907 };
    908 
    909 /*
    910 ===========
    911 IN_JoyMove
    912 ===========
    913 */
    914 void IN_JoyMove( void ) {
    915 	float	fAxisValue;
    916 	int		i;
    917 	DWORD	buttonstate, povstate;
    918 	int		x, y;
    919 
    920 	// verify joystick is available and that the user wants to use it
    921 	if ( !joy.avail ) {
    922 		return; 
    923 	}
    924 
    925 	// collect the joystick data, if possible
    926 	Com_Memset (&joy.ji, 0, sizeof(joy.ji));
    927 	joy.ji.dwSize = sizeof(joy.ji);
    928 	joy.ji.dwFlags = JOY_RETURNALL;
    929 
    930 	if ( joyGetPosEx (joy.id, &joy.ji) != JOYERR_NOERROR ) {
    931 		// read error occurred
    932 		// turning off the joystick seems too harsh for 1 read error,\
    933 		// but what should be done?
    934 		// Com_Printf ("IN_ReadJoystick: no response\n");
    935 		// joy.avail = false;
    936 		return;
    937 	}
    938 
    939 	if ( in_debugJoystick->integer ) {
    940 		Com_Printf( "%8x %5i %5.2f %5.2f %5.2f %5.2f %6i %6i\n", 
    941 			joy.ji.dwButtons,
    942 			joy.ji.dwPOV,
    943 			JoyToF( joy.ji.dwXpos ), JoyToF( joy.ji.dwYpos ),
    944 			JoyToF( joy.ji.dwZpos ), JoyToF( joy.ji.dwRpos ),
    945 			JoyToI( joy.ji.dwUpos ), JoyToI( joy.ji.dwVpos ) );
    946 	}
    947 
    948 	// loop through the joystick buttons
    949 	// key a joystick event or auxillary event for higher number buttons for each state change
    950 	buttonstate = joy.ji.dwButtons;
    951 	for ( i=0 ; i < joy.jc.wNumButtons ; i++ ) {
    952 		if ( (buttonstate & (1<<i)) && !(joy.oldbuttonstate & (1<<i)) ) {
    953 			Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_JOY1 + i, qtrue, 0, NULL );
    954 		}
    955 		if ( !(buttonstate & (1<<i)) && (joy.oldbuttonstate & (1<<i)) ) {
    956 			Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_JOY1 + i, qfalse, 0, NULL );
    957 		}
    958 	}
    959 	joy.oldbuttonstate = buttonstate;
    960 
    961 	povstate = 0;
    962 
    963 	// convert main joystick motion into 6 direction button bits
    964 	for (i = 0; i < joy.jc.wNumAxes && i < 4 ; i++) {
    965 		// get the floating point zero-centered, potentially-inverted data for the current axis
    966 		fAxisValue = JoyToF( (&joy.ji.dwXpos)[i] );
    967 
    968 		if ( fAxisValue < -joy_threshold->value ) {
    969 			povstate |= (1<<(i*2));
    970 		} else if ( fAxisValue > joy_threshold->value ) {
    971 			povstate |= (1<<(i*2+1));
    972 		}
    973 	}
    974 
    975 	// convert POV information from a direction into 4 button bits
    976 	if ( joy.jc.wCaps & JOYCAPS_HASPOV ) {
    977 		if ( joy.ji.dwPOV != JOY_POVCENTERED ) {
    978 			if (joy.ji.dwPOV == JOY_POVFORWARD)
    979 				povstate |= 1<<12;
    980 			if (joy.ji.dwPOV == JOY_POVBACKWARD)
    981 				povstate |= 1<<13;
    982 			if (joy.ji.dwPOV == JOY_POVRIGHT)
    983 				povstate |= 1<<14;
    984 			if (joy.ji.dwPOV == JOY_POVLEFT)
    985 				povstate |= 1<<15;
    986 		}
    987 	}
    988 
    989 	// determine which bits have changed and key an auxillary event for each change
    990 	for (i=0 ; i < 16 ; i++) {
    991 		if ( (povstate & (1<<i)) && !(joy.oldpovstate & (1<<i)) ) {
    992 			Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, joyDirectionKeys[i], qtrue, 0, NULL );
    993 		}
    994 
    995 		if ( !(povstate & (1<<i)) && (joy.oldpovstate & (1<<i)) ) {
    996 			Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, joyDirectionKeys[i], qfalse, 0, NULL );
    997 		}
    998 	}
    999 	joy.oldpovstate = povstate;
   1000 
   1001 	// if there is a trackball like interface, simulate mouse moves
   1002 	if ( joy.jc.wNumAxes >= 6 ) {
   1003 		x = JoyToI( joy.ji.dwUpos ) * in_joyBallScale->value;
   1004 		y = JoyToI( joy.ji.dwVpos ) * in_joyBallScale->value;
   1005 		if ( x || y ) {
   1006 			Sys_QueEvent( g_wv.sysMsgTime, SE_MOUSE, x, y, 0, NULL );
   1007 		}
   1008 	}
   1009 }
   1010 
   1011 /*
   1012 =========================================================================
   1013 
   1014 MIDI
   1015 
   1016 =========================================================================
   1017 */
   1018 
   1019 static void MIDI_NoteOff( int note )
   1020 {
   1021 	int qkey;
   1022 
   1023 	qkey = note - 60 + K_AUX1;
   1024 
   1025 	if ( qkey > 255 || qkey < K_AUX1 )
   1026 		return;
   1027 
   1028 	Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, qkey, qfalse, 0, NULL );
   1029 }
   1030 
   1031 static void MIDI_NoteOn( int note, int velocity )
   1032 {
   1033 	int qkey;
   1034 
   1035 	if ( velocity == 0 )
   1036 		MIDI_NoteOff( note );
   1037 
   1038 	qkey = note - 60 + K_AUX1;
   1039 
   1040 	if ( qkey > 255 || qkey < K_AUX1 )
   1041 		return;
   1042 
   1043 	Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, qkey, qtrue, 0, NULL );
   1044 }
   1045 
   1046 static void CALLBACK MidiInProc( HMIDIIN hMidiIn, UINT uMsg, DWORD dwInstance, 
   1047 								 DWORD dwParam1, DWORD dwParam2 )
   1048 {
   1049 	int message;
   1050 
   1051 	switch ( uMsg )
   1052 	{
   1053 	case MIM_OPEN:
   1054 		break;
   1055 	case MIM_CLOSE:
   1056 		break;
   1057 	case MIM_DATA:
   1058 		message = dwParam1 & 0xff;
   1059 
   1060 		// note on
   1061 		if ( ( message & 0xf0 ) == 0x90 )
   1062 		{
   1063 			if ( ( ( message & 0x0f ) + 1 ) == in_midichannel->integer )
   1064 				MIDI_NoteOn( ( dwParam1 & 0xff00 ) >> 8, ( dwParam1 & 0xff0000 ) >> 16 );
   1065 		}
   1066 		else if ( ( message & 0xf0 ) == 0x80 )
   1067 		{
   1068 			if ( ( ( message & 0x0f ) + 1 ) == in_midichannel->integer )
   1069 				MIDI_NoteOff( ( dwParam1 & 0xff00 ) >> 8 );
   1070 		}
   1071 		break;
   1072 	case MIM_LONGDATA:
   1073 		break;
   1074 	case MIM_ERROR:
   1075 		break;
   1076 	case MIM_LONGERROR:
   1077 		break;
   1078 	}
   1079 
   1080 //	Sys_QueEvent( sys_msg_time, SE_KEY, wMsg, qtrue, 0, NULL );
   1081 }
   1082 
   1083 static void MidiInfo_f( void )
   1084 {
   1085 	int i;
   1086 
   1087 	const char *enableStrings[] = { "disabled", "enabled" };
   1088 
   1089 	Com_Printf( "\nMIDI control:       %s\n", enableStrings[in_midi->integer != 0] );
   1090 	Com_Printf( "port:               %d\n", in_midiport->integer );
   1091 	Com_Printf( "channel:            %d\n", in_midichannel->integer );
   1092 	Com_Printf( "current device:     %d\n", in_mididevice->integer );
   1093 	Com_Printf( "number of devices:  %d\n", s_midiInfo.numDevices );
   1094 	for ( i = 0; i < s_midiInfo.numDevices; i++ )
   1095 	{
   1096 		if ( i == Cvar_VariableValue( "in_mididevice" ) )
   1097 			Com_Printf( "***" );
   1098 		else
   1099 			Com_Printf( "..." );
   1100 		Com_Printf(    "device %2d:       %s\n", i, s_midiInfo.caps[i].szPname );
   1101 		Com_Printf( "...manufacturer ID: 0x%hx\n", s_midiInfo.caps[i].wMid );
   1102 		Com_Printf( "...product ID:      0x%hx\n", s_midiInfo.caps[i].wPid );
   1103 
   1104 		Com_Printf( "\n" );
   1105 	}
   1106 }
   1107 
   1108 static void IN_StartupMIDI( void )
   1109 {
   1110 	int i;
   1111 
   1112 	if ( !Cvar_VariableValue( "in_midi" ) )
   1113 		return;
   1114 
   1115 	//
   1116 	// enumerate MIDI IN devices
   1117 	//
   1118 	s_midiInfo.numDevices = midiInGetNumDevs();
   1119 
   1120 	for ( i = 0; i < s_midiInfo.numDevices; i++ )
   1121 	{
   1122 		midiInGetDevCaps( i, &s_midiInfo.caps[i], sizeof( s_midiInfo.caps[i] ) );
   1123 	}
   1124 
   1125 	//
   1126 	// open the MIDI IN port
   1127 	//
   1128 	if ( midiInOpen( &s_midiInfo.hMidiIn, 
   1129 		             in_mididevice->integer,
   1130 					 ( unsigned long ) MidiInProc,
   1131 					 ( unsigned long ) NULL,
   1132 					 CALLBACK_FUNCTION ) != MMSYSERR_NOERROR )
   1133 	{
   1134 		Com_Printf( "WARNING: could not open MIDI device %d: '%s'\n", in_mididevice->integer , s_midiInfo.caps[( int ) in_mididevice->value] );
   1135 		return;
   1136 	}
   1137 
   1138 	midiInStart( s_midiInfo.hMidiIn );
   1139 }
   1140 
   1141 static void IN_ShutdownMIDI( void )
   1142 {
   1143 	if ( s_midiInfo.hMidiIn )
   1144 	{
   1145 		midiInClose( s_midiInfo.hMidiIn );
   1146 	}
   1147 	Com_Memset( &s_midiInfo, 0, sizeof( s_midiInfo ) );
   1148 }
   1149