#include "stdafx.h" /* ----------------------------------------------------------------------------- File: RageInput.h Desc: Wrapper for DirectInput. Generates InputEvents. Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. Chris Danford ----------------------------------------------------------------------------- */ //----------------------------------------------------------------------------- // In-line Links //----------------------------------------------------------------------------- #pragma comment(lib, "ddk/setupapi.lib") #pragma comment(lib, "ddk/hid.lib") //----------------------------------------------------------------------------- // Includes //----------------------------------------------------------------------------- #include "RageInput.h" #include "SDL.h" #include "SDL_keyboard.h" #include "RageUtil.h" #include "RageLog.h" #include "RageException.h" RageInput* INPUTMAN = NULL; // globally accessable input device static const char *PumpButtonNames[] = { "UL", "UR", "MID", "DL", "DR", "Esc", "P2 UL", "P2 UR", "P2 MID", "P2 DL", "P2 DR" }; int DeviceInput::NumButtons(InputDevice device) { switch( device ) { case DEVICE_KEYBOARD: return NUM_KEYBOARD_BUTTONS; case DEVICE_JOY1: case DEVICE_JOY2: case DEVICE_JOY3: case DEVICE_JOY4: return NUM_JOYSTICK_BUTTONS; case DEVICE_PUMP1: case DEVICE_PUMP2: return NUM_PUMP_PAD_BUTTONS; default: ASSERT(0); // invalid device } return -1; /* quiet compiler */ } CString DeviceInput::GetDescription() { CString sReturn; switch( device ) { case DEVICE_NONE: // blank ASSERT( false ); // called GetDescription on a blank DeviceInput! break; case DEVICE_JOY1: // joystick case DEVICE_JOY2: case DEVICE_JOY3: case DEVICE_JOY4: sReturn = ssprintf("Joy%d ", device - DEVICE_JOY1 + 1 ); switch( button ) { case JOY_LEFT: sReturn += "Left"; break; case JOY_RIGHT: sReturn += "Right"; break; case JOY_UP: sReturn += "Up"; break; case JOY_DOWN: sReturn += "Down"; break; case JOY_Z_UP: sReturn += "Z-Up"; break; case JOY_Z_DOWN: sReturn += "Z-Down"; break; case JOY_Z_ROT_UP: sReturn += "ZR-Up"; break; case JOY_Z_ROT_DOWN:sReturn += "ZR-Down"; break; case JOY_HAT_LEFT: sReturn += "H-Left"; break; case JOY_HAT_RIGHT: sReturn += "H-Right"; break; case JOY_HAT_UP: sReturn += "H-Up"; break; case JOY_HAT_DOWN: sReturn += "H-Down"; break; case JOY_1: sReturn += "1"; break; case JOY_2: sReturn += "2"; break; case JOY_3: sReturn += "3"; break; case JOY_4: sReturn += "4"; break; case JOY_5: sReturn += "5"; break; case JOY_6: sReturn += "6"; break; case JOY_7: sReturn += "7"; break; case JOY_8: sReturn += "8"; break; case JOY_9: sReturn += "9"; break; case JOY_10: sReturn += "10"; break; case JOY_11: sReturn += "11"; break; case JOY_12: sReturn += "12"; break; case JOY_13: sReturn += "13"; break; case JOY_14: sReturn += "14"; break; case JOY_15: sReturn += "15"; break; case JOY_16: sReturn += "16"; break; case JOY_17: sReturn += "17"; break; case JOY_18: sReturn += "18"; break; case JOY_19: sReturn += "19"; break; case JOY_20: sReturn += "20"; break; case JOY_21: sReturn += "21"; break; case JOY_22: sReturn += "22"; break; case JOY_23: sReturn += "23"; break; case JOY_24: sReturn += "24"; break; } break; case DEVICE_PUMP1: case DEVICE_PUMP2: /* There is almost always only one Pump device, even if there are * two pads, so only enumerate the second and higher. */ if(device == DEVICE_PUMP1) sReturn = ssprintf("PIU "); else sReturn = ssprintf("PIU#%d ", device - DEVICE_PUMP1 + 1 ); if(button < NUM_PUMP_PAD_BUTTONS) sReturn += PumpButtonNames[button]; else /* This can happen if the INI is corrupt and has an invalid * button number. Crashing with an assertion failure because * we got something unexpected is no good. */ sReturn += "???"; break; case DEVICE_KEYBOARD: // keyboard sReturn = ssprintf("Key %s", SDL_GetKeyName((SDLKey)button) ); /* switch( button ) { case SDLK_ESCAPE: sReturn += "Escape"; break; case SDLK_1: sReturn += "1"; break; case SDLK_2: sReturn += "2"; break; case SDLK_3: sReturn += "3"; break; case SDLK_4: sReturn += "4"; break; case SDLK_5: sReturn += "5"; break; case SDLK_6: sReturn += "6"; break; case SDLK_7: sReturn += "7"; break; case SDLK_8: sReturn += "8"; break; case SDLK_9: sReturn += "9"; break; case SDLK_0: sReturn += "0"; break; case SDLK_MINUS: sReturn += "Minus"; break; case SDLK_EQUALS: sReturn += "Equals"; break; case SDLK_BACKSPACE: sReturn += "Backsp"; break; case SDLK_TAB: sReturn += "Tab"; break; case SDLK_Q: sReturn += "Q"; break; case SDLK_W: sReturn += "W"; break; case SDLK_E: sReturn += "E"; break; case SDLK_R: sReturn += "R"; break; case SDLK_T: sReturn += "T"; break; case SDLK_Y: sReturn += "Y"; break; case SDLK_U: sReturn += "U"; break; case SDLK_I: sReturn += "I"; break; case SDLK_O: sReturn += "O"; break; case SDLK_P: sReturn += "P"; break; case SDLK_LBRACKET: sReturn += "LBracket"; break; case SDLK_RBRACKET: sReturn += "RBracket"; break; case SDLK_RETURN: sReturn += "Return"; break; case SDLK_LCONTROL: sReturn += "LControl"; break; case SDLK_A: sReturn += "A"; break; case SDLK_S: sReturn += "S"; break; case SDLK_D: sReturn += "D"; break; case SDLK_F: sReturn += "F"; break; case SDLK_G: sReturn += "G"; break; case SDLK_H: sReturn += "H"; break; case SDLK_J: sReturn += "J"; break; case SDLK_K: sReturn += "K"; break; case SDLK_L: sReturn += "L"; break; case SDLK_SEMICOLON: sReturn += "Semicln"; break; case SDLK_APOSTROPHE:sReturn += "Apostro"; break; case SDLK_GRAVE: sReturn += "Grave"; break; case SDLK_LSHIFT: sReturn += "LShift"; break; case SDLK_BACKSLASH: sReturn += "Backslsh"; break; case SDLK_Z: sReturn += "Z"; break; case SDLK_X: sReturn += "X"; break; case SDLK_C: sReturn += "C"; break; case SDLK_V: sReturn += "V"; break; case SDLK_B: sReturn += "B"; break; case SDLK_N: sReturn += "N"; break; case SDLK_M: sReturn += "M"; break; case SDLK_COMMA: sReturn += "Comma"; break; case SDLK_PERIOD: sReturn += "Period"; break; case SDLK_SLASH: sReturn += "Slash"; break; case SDLK_RSHIFT: sReturn += "RShift"; break; case SDLK_MULTIPLY: sReturn += "Mult"; break; case SDLK_LMENU: sReturn += "LMenu"; break; case SDLK_SPACE: sReturn += "Space"; break; case SDLK_CAPITAL: sReturn += "CapsLk"; break; case SDLK_F1: sReturn += "F1"; break; case SDLK_F2: sReturn += "F2"; break; case SDLK_F3: sReturn += "F3"; break; case SDLK_F4: sReturn += "F4"; break; case SDLK_F5: sReturn += "F5"; break; case SDLK_F6: sReturn += "F6"; break; case SDLK_F7: sReturn += "F7"; break; case SDLK_F8: sReturn += "F8"; break; case SDLK_F9: sReturn += "F9"; break; case SDLK_F10: sReturn += "F10"; break; case SDLK_NUMLOCK: sReturn += "Numlock"; break; case SDLK_SCROLL: sReturn += "Scroll"; break; case SDLK_NUMPAD7: sReturn += "NumPad7"; break; case SDLK_NUMPAD8: sReturn += "NumPad8"; break; case SDLK_NUMPAD9: sReturn += "NumPad9"; break; case SDLK_SUBTRACT: sReturn += "Subtract"; break; case SDLK_NUMPAD4: sReturn += "NumPad4"; break; case SDLK_NUMPAD5: sReturn += "NumPad5"; break; case SDLK_NUMPAD6: sReturn += "NumPad6"; break; case SDLK_ADD: sReturn += "Add"; break; case SDLK_NUMPAD1: sReturn += "NumPad1"; break; case SDLK_NUMPAD2: sReturn += "NumPad2"; break; case SDLK_NUMPAD3: sReturn += "NumPad3"; break; case SDLK_NUMPAD0: sReturn += "NumPad0"; break; case SDLK_DECIMAL: sReturn += "Decimal"; break; case SDLK_RMENU: sReturn += "RightAlt"; break; case SDLK_PAUSE: sReturn += "Pause"; break; case SDLK_HOME: sReturn += "Home"; break; case SDLK_UP: sReturn += "Up"; break; case SDLK_PRIOR: sReturn += "PageUp"; break; case SDLK_LEFT: sReturn += "Left"; break; case SDLK_RIGHT: sReturn += "Right"; break; case SDLK_END: sReturn += "End"; break; case SDLK_DOWN: sReturn += "Down"; break; case SDLK_NEXT: sReturn += "PageDn"; break; case SDLK_INSERT: sReturn += "Insert"; break; case SDLK_DELETE: sReturn += "Delete"; break; case SDLK_LWIN: sReturn += "LeftWin"; break; case SDLK_RWIN: sReturn += "RightWin"; break; case SDLK_APPS: sReturn += "AppMenu"; break; case SDLK_NUMPADENTER: sReturn += "PadEnter"; break; case SDLK_DIVIDE: sReturn += "PadSlash"; break; default: sReturn += "Unknown Key"; break; } */ break; default: ASSERT(0); // what device is this? } return sReturn; } CString DeviceInput::toString() { if( device == DEVICE_NONE ) return ""; else return ssprintf("%d-%d", device, button ); } bool DeviceInput::fromString( const CString &s ) { CStringArray a; split( s, "-", a); if( a.size() != 2 ) { device = DEVICE_NONE; return false; } device = (InputDevice)atoi( a[0] ); button = atoi( a[1] ); return true; } #if 0 /* FIXME: The main font doesn't have '`' (0x29), so that's disabled. */ static const char SDLK_charmap[] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ /* 0x0x */ 0, 0,'1','2','3','4','5','6','7', '8','9', '0','-','=', 0, 0, /* 0x1x */ 'q','w','e','r','t','y','u','i','o', 'p','[', ']', 0, 0,'a','s', /* 0x2x */ 'd','f','g','h','j','k','l',';','\'', 0, 0,'\\','z','x','c','v', /* 0x3x */ 'b','n','m',',','.','/', 0,'*', 0, ' ', 0, 0, 0, 0, 0, 0, /* 0x4x */ 0, 0, 0, 0, 0, 0, 0,'7','8', '9','-', '4','5','6','+','1', /* 0x5x */ '2','3','0','.', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x6x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x7x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'.', 0, /* 0x8x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'=', 0, 0, /* 0x9x */ 0,'@',':','_', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xAx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xBx */ 0, 0, 0,',', 0,'/', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #endif char DeviceInput::ToChar() const { switch( device ) { case DEVICE_KEYBOARD: if( button < 128 ) return (char) button; /* XXX: SDLK_WORLD_* are for international keyboards; we can handle those, * now, by mapping it to Unicode. However, I can't find any documentation * on those keysyms. */ return '\0'; default: return '\0'; } } RageInput::RageInput() { LOG->Trace( "RageInput::RageInput()" ); SDL_InitSubSystem( SDL_INIT_JOYSTICK ); // init state info memset( m_keys, 0, sizeof(m_keys) ); memset( m_oldKeys, 0, sizeof(m_oldKeys) ); memset( m_joyState, 0, sizeof(m_joyState) ); memset( m_oldJoyState, 0, sizeof(m_oldJoyState) ); memset( m_pumpState, 0, sizeof(m_pumpState) ); memset( m_oldPumpState, 0, sizeof(m_oldPumpState) ); // // Init keyboard // SDL_EnableKeyRepeat( 0, 0 ); /* If we use key events, we can do this to get Unicode values * in the key struct, which (with a little more work) will make * us work on international keyboards: */ // SDL_EnableUNICODE( 1 ); // // Init joysticks // memset( m_pJoystick, 0, sizeof(m_pJoystick) ); int iNumJoySticks = min( SDL_NumJoysticks(), NUM_JOYSTICKS ); for( int i=0; iTrace( "Found joystick %d: %s", i, SDL_JoystickName(i) ); } SDL_JoystickEventState( SDL_IGNORE ); // // Init pumps // m_Pumps = new pump_t[NUM_PUMPS]; for(int pumpNo = 0; pumpNo < NUM_PUMPS; ++pumpNo) { if(m_Pumps[pumpNo].init(pumpNo)) LOG->Trace("Found Pump pad %i\n", pumpNo); } } RageInput::~RageInput() { // // De-init keyboard // // // De-init joysticks // for( int i=0; iTrace( "axis %d = %d", axis, val ); if( val < -16000 ) { JoystickButton b = (JoystickButton)(JOY_LEFT+2*axis); m_joyState[joy].button[b] = true; } else if( val > +16000 ) { JoystickButton b = (JoystickButton)(JOY_RIGHT+2*axis); m_joyState[joy].button[b] = true; } } int iNumJoyHats = min(NUM_JOYSTICK_HATS,SDL_JoystickNumHats(pJoy)); for( int hat=0; axiscbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); if (SetupDiGetDeviceInterfaceDetail (DeviceInfo, &DeviceInterface, DeviceDetail, size, &size, NULL)) { ret = strdup(DeviceDetail->DevicePath); } err: SetupDiDestroyDeviceInfoList (DeviceInfo); free (DeviceDetail); return ret; } HANDLE USB::OpenUSB (int VID, int PID, int num) { DWORD index = 0; char *path; HANDLE h = INVALID_HANDLE_VALUE; while ((path = GetUSBDevicePath (index++)) != NULL) { if(h != INVALID_HANDLE_VALUE) CloseHandle (h); h = CreateFile (path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); free(path); if(h == INVALID_HANDLE_VALUE) continue; HIDD_ATTRIBUTES attr; if (!HidD_GetAttributes (h, &attr)) continue; if ((VID != -1 && attr.VendorID != VID) && (PID != -1 && attr.ProductID != PID)) continue; /* This isn't it. */ /* The VID and PID match. */ if(num-- == 0) return h; } if(h != INVALID_HANDLE_VALUE) CloseHandle (h); return INVALID_HANDLE_VALUE; } RageInput::pump_t::pump_t() { ZeroMemory( &ov, sizeof(ov) ); pending=false; h = INVALID_HANDLE_VALUE; } RageInput::pump_t::~pump_t() { if(h != INVALID_HANDLE_VALUE) CloseHandle(h); } bool RageInput::pump_t::init(int devno) { const int pump_usb_vid = 0x0d2f, pump_usb_pid = 0x0001; h = USB::OpenUSB (pump_usb_vid, pump_usb_pid, devno); return h != INVALID_HANDLE_VALUE; } int RageInput::pump_t::GetPadEvent() { int ret; if(!pending) { /* Request feedback from the device. */ unsigned long r; ret = ReadFile(h, &buf, sizeof(buf), &r, &ov); pending=true; } /* See if we have a response for our request (which we may * have made on a previous cal): */ if(WaitForSingleObjectEx(h, 0, TRUE) == WAIT_TIMEOUT) return -1; /* We do; get the result. It'll go into the original &buf * we supplied on the original call; that's why buf is a * member instead of a local. */ unsigned long cnt; ret = GetOverlappedResult(h, &ov, &cnt, FALSE); pending=false; if(ret == 0 && (GetLastError() == ERROR_IO_PENDING || GetLastError() == ERROR_IO_INCOMPLETE)) return -1; if(ret == 0) { LOG->Trace(werr_ssprintf(GetLastError(), "Error reading Pump pad")); return -1; } return buf; }