/* =========================================================================== 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 =========================================================================== */ #include "../client/client.h" #include "win_local.h" struct Mouse { virtual qbool Init() { return qtrue; } virtual qbool Activate( qbool active ); virtual void OnWindowMoved() {} virtual void Shutdown() {} virtual qbool ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ) { return qfalse; } // returns true if the event was handled Mouse() : active(qfalse), wheel(0) {} void UpdateWheel( int delta ); private: qbool active; int wheel; }; static Mouse* mouse; qbool Mouse::Activate( qbool _active ) { if (active == _active) return qfalse; active = _active; wheel = 0; return qtrue; } void Mouse::UpdateWheel( int delta ) { wheel += delta; while (wheel >= WHEEL_DELTA) { Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qtrue, 0, NULL ); Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qfalse, 0, NULL ); wheel -= WHEEL_DELTA; } while (wheel <= -WHEEL_DELTA) { Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL ); Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL ); wheel += WHEEL_DELTA; } } /////////////////////////////////////////////////////////////// struct rawmouse_t : public Mouse { virtual qbool Init(); virtual qbool Activate( qbool active ); virtual qbool ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ); }; static rawmouse_t rawmouse; qbool rawmouse_t::Init() { return Activate( qtrue ); } qbool rawmouse_t::Activate( qbool active ) { RAWINPUTDEVICE rid; rid.usUsagePage = 0x01; rid.usUsage = 0x02; // page 1 item 2 = mouse, gg constants you asswipes >:( if (active) rid.dwFlags = RIDEV_NOLEGACY; else rid.dwFlags = RIDEV_REMOVE; rid.hwndTarget = 0; return RegisterRawInputDevices( &rid, 1, sizeof(rid) ); } // MSDN says you have to always let DefWindowProc run for WM_INPUT // regardless of whether you process the message or not, so ALWAYS return false here qbool rawmouse_t::ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ) { if (msg != WM_INPUT) return qfalse; HRAWINPUT h = (HRAWINPUT)lParam; UINT size; if (GetRawInputData( h, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER) ) == -1) return qfalse; RAWINPUT ri; if (GetRawInputData( h, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER) ) != size) return qfalse; if ( (ri.header.dwType != RIM_TYPEMOUSE) || (ri.data.mouse.usFlags != MOUSE_MOVE_RELATIVE) ) return qfalse; const int dx = (int)ri.data.mouse.lLastX; const int dy = (int)ri.data.mouse.lLastY; if (dx != 0 || dy != 0) Sys_QueEvent( g_wv.sysMsgTime, SE_MOUSE, dx, dy, 0, NULL ); if (!ri.data.mouse.usButtonFlags) // no button or wheel transitions return qfalse; if (ri.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) UpdateWheel( (SHORT)ri.data.mouse.usButtonData ); // typical MS clusterfuck for button handling, sigh... and better yet, // ulRawButtons isn't actually populated at all with the ms mouse drivers #define QUEUE_RI_BUTTON( button ) \ if (ri.data.mouse.usButtonFlags & (RI_MOUSE_BUTTON_##button##_DOWN)) \ Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MOUSE##button##, qtrue, 0, NULL ); \ if (ri.data.mouse.usButtonFlags & (RI_MOUSE_BUTTON_##button##_UP)) \ Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MOUSE##button##, qfalse, 0, NULL ); QUEUE_RI_BUTTON( 1 ); QUEUE_RI_BUTTON( 2 ); QUEUE_RI_BUTTON( 3 ); QUEUE_RI_BUTTON( 4 ); QUEUE_RI_BUTTON( 5 ); #undef QUEUE_RI_BUTTON return qfalse; } /////////////////////////////////////////////////////////////// struct winmouse_t : public Mouse { virtual qbool Activate( qbool active ); virtual void OnWindowMoved(); virtual qbool ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ); void UpdateWindowCenter(); int window_center_x, window_center_y; qbool active; }; static winmouse_t winmouse; void winmouse_t::UpdateWindowCenter() { const int sw = GetSystemMetrics( SM_CXSCREEN ); const int sh = GetSystemMetrics( SM_CYSCREEN ); RECT rc; GetWindowRect( g_wv.hWnd, &rc ); window_center_x = ( max(rc.left, 0) + min(rc.right, sw) ) / 2; window_center_y = ( max(rc.top, 0) + min(rc.bottom, sh) ) / 2; } qbool winmouse_t::Activate( qbool _active ) { active = _active; if (!_active) return qtrue; UpdateWindowCenter(); SetCursorPos( window_center_x, window_center_y ); return qtrue; } void winmouse_t::OnWindowMoved() { UpdateWindowCenter(); } qbool winmouse_t::ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ) { if ( !active ) return qfalse; #define QUEUE_WM_BUTTON( qbutton, mask ) \ Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, qbutton, (wParam & mask), 0, NULL ); POINT p; GetCursorPos( &p ); const int dx = p.x - window_center_x; const int dy = p.y - window_center_y; if (dx != 0 || dy != 0) { Sys_QueEvent( g_wv.sysMsgTime, SE_MOUSE, dx, dy, 0, NULL ); SetCursorPos( window_center_x, window_center_y ); } switch (msg) { case WM_LBUTTONDOWN: case WM_LBUTTONUP: QUEUE_WM_BUTTON( K_MOUSE1, MK_LBUTTON ); break; case WM_RBUTTONDOWN: case WM_RBUTTONUP: QUEUE_WM_BUTTON( K_MOUSE2, MK_RBUTTON ); break; case WM_MBUTTONDOWN: case WM_MBUTTONUP: QUEUE_WM_BUTTON( K_MOUSE3, MK_MBUTTON ); break; case WM_XBUTTONDOWN: case WM_XBUTTONUP: if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) QUEUE_WM_BUTTON( K_MOUSE4, MK_XBUTTON1 ); if (GET_XBUTTON_WPARAM(wParam) == XBUTTON2) QUEUE_WM_BUTTON( K_MOUSE5, MK_XBUTTON2 ); break; #undef QUEUE_WM_BUTTON case WM_MOUSEWHEEL: UpdateWheel( GET_WHEEL_DELTA_WPARAM(wParam) ); break; default: return qfalse; } return qtrue; } /////////////////////////////////////////////////////////////// static void IN_StartupMouse() { assert( !mouse ); mouse = 0; cvar_t* in_mouse = Cvar_Get( "in_mouse", "1", CVAR_ARCHIVE|CVAR_LATCH ); in_mouse->modified = qfalse; if (!in_mouse->integer) { Com_Printf( "Mouse not active.\n" ); return; } if (in_mouse->integer == 1) { if (rawmouse.Init()) { mouse = &rawmouse; Com_Printf( "Using raw mouse input\n" ); return; } Com_Printf( "Raw mouse initialization failed\n" ); } mouse = &winmouse; mouse->Init(); Com_Printf( "Using Win32 mouse input\n" ); } qbool IN_ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ) { if (!mouse) return qfalse; return mouse->ProcessMessage( msg, wParam, lParam ); } /////////////////////////////////////////////////////////////// static void IN_StartupMIDI(); static void IN_ShutdownMIDI(); static void IN_StartupJoystick(); static void IN_JoyMove(); cvar_t* in_joystick; cvar_t* in_midi; static void IN_Startup() { QSUBSYSTEM_INIT_START( "Input" ); IN_StartupMouse(); IN_StartupJoystick(); IN_StartupMIDI(); QSUBSYSTEM_INIT_DONE( "Input" ); in_joystick->modified = qfalse; } void IN_Init() { in_midi = Cvar_Get( "in_midi", "0", CVAR_ARCHIVE ); in_joystick = Cvar_Get( "in_joystick", "0", CVAR_ARCHIVE|CVAR_LATCH ); IN_Startup(); } void IN_Shutdown() { IN_Activate( qfalse ); if (mouse) { mouse->Shutdown(); mouse = 0; } IN_ShutdownMIDI(); } // called when the window gains or loses focus or changes in some way // the window may have been destroyed and recreated between a deactivate and an activate void IN_Activate( qbool active ) { if ( !mouse || !mouse->Mouse::Activate( active ) ) return; if (active) { while (ShowCursor(FALSE) >= 0) ; RECT rc; GetWindowRect( g_wv.hWnd, &rc ); SetCapture( g_wv.hWnd ); ClipCursor( &rc ); } else { while (ShowCursor(TRUE) < 0) ; ClipCursor( NULL ); ReleaseCapture(); } mouse->Activate( active ); } void IN_WindowMoved() { if (!mouse) return; mouse->OnWindowMoved(); } qbool IN_ShouldBeActive() { return g_wv.activeApp && (!(cls.keyCatchers & KEYCATCH_CONSOLE) || Cvar_VariableIntegerValue("r_fullscreen")); } // called every frame, even if not generating commands void IN_Frame() { IN_JoyMove(); if (!mouse) return; if (!IN_ShouldBeActive()) { IN_Activate( qfalse ); return; } IN_Activate( qtrue ); } /* ========================================================================= JOYSTICK ========================================================================= */ typedef struct { qbool avail; int id; // joystick number JOYCAPS jc; int oldbuttonstate; int oldpovstate; JOYINFOEX ji; } joystickInfo_t; static joystickInfo_t joy; static const cvar_t* in_joyBallScale; static const cvar_t* in_debugJoystick; static const cvar_t* joy_threshold; static void IN_StartupJoystick() { // assume no joystick joy.avail = qfalse; if ( !in_joystick->integer ) return; // verify joystick driver is present int numdevs; if ((numdevs = joyGetNumDevs()) == 0) { Com_Printf( "joystick not found -- driver not present\n" ); return; } // cycle through the joystick ids for the first valid one MMRESULT mmr = !JOYERR_NOERROR; for (joy.id=0 ; joy.idinteger ) { Com_Printf( "%8x %5i %5.2f %5.2f %5.2f %5.2f %6i %6i\n", joy.ji.dwButtons, 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 auxillary event for higher number buttons for each state change buttonstate = joy.ji.dwButtons; for ( i=0 ; i < joy.jc.wNumButtons ; i++ ) { if ( (buttonstate & (1<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 auxillary event for each change for (i=0 ; i < 16 ; i++) { if ( (povstate & (1<= 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 ); } } } /* ========================================================================= MIDI ========================================================================= */ #define MAX_MIDIIN_DEVICES 8 typedef struct { int numDevices; MIDIINCAPS caps[MAX_MIDIIN_DEVICES]; HMIDIIN hMidiIn; } MidiInfo_t; static MidiInfo_t s_midiInfo; static const cvar_t* in_midiport; static const cvar_t* in_midichannel; static const cvar_t* in_mididevice; 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; 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_VariableValue( "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() { int i; if ( !Cvar_VariableValue( "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] ) ); } // activate and archive the cvars since they actually want midi support in_midiport = Cvar_Get ("in_midiport", "1", CVAR_ARCHIVE); in_midichannel = Cvar_Get ("in_midichannel", "1", CVAR_ARCHIVE); in_mididevice = Cvar_Get ("in_mididevice", "0", CVAR_ARCHIVE); Cmd_AddCommand( "midiinfo", MidiInfo_f ); // // open the MIDI IN port // if ( midiInOpen( &s_midiInfo.hMidiIn, in_mididevice->integer, ( unsigned long ) MidiInProc, ( unsigned long ) NULL, CALLBACK_FUNCTION ) != MMSYSERR_NOERROR ) { Com_Printf( "WARNING: could not open MIDI device %d: '%s'\n", in_mididevice->integer , s_midiInfo.caps[( int ) in_mididevice->value] ); return; } midiInStart( s_midiInfo.hMidiIn ); } static void IN_ShutdownMIDI() { if ( s_midiInfo.hMidiIn ) midiInClose( s_midiInfo.hMidiIn ); Com_Memset( &s_midiInfo, 0, sizeof( s_midiInfo ) ); Cmd_RemoveCommand( "midiinfo" ); }