/**************************************************************************** * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR * PURPOSE. * * Copyright (c) 1993 - 1995 Microsoft Corporation. All Rights Reserved. * * File: sidewndr.c * Content: Sidewinder joystick device driver - entry point, * initialization and message module *@@BEGIN_MSINTERNAL * History: * Date By Reason * ==== == ====== * 05-oct-94 craige re-write * 26-oct-94 craige move polling to vjoyd * 05-nov-94 craige generalized 4 axis joysticks * 28-nov-94 craige 6 axis support * 05-dec-94 craige allow either joystick to be 6 axis * 18-dec-94 craige increased maximum number of joysticks * 04-mar-95 craige Bug 9998: if jsGetHWCaps is false, fail jsGetDevCaps * 04-apr-95 craige Bug 8416: broadcast message when config changed * 29-mar-96 richj ported to Windows NT (rewrote but kept the comments) *@@END_MSINTERNAL * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "sidewndr.h" #include "resource.h" /* * CONFIGURATION ______________________________________________________________ * */ /* * Enable the definition below if SIDEWNDR.SYS is ever updated * to actually work with two joysticks. * */ // #define ALLOW_TWO_JOYSTICKS /* * Enable the definition below if SIDEWNDR.SYS is ever updated * to actually work with analog mode in general. * */ // #define ALLOW_NON_SIDEWINDER static TCHAR cszOURKERNELDRIVER[] = TEXT("SIDEWNDR"); static TCHAR cszOUROEMNAME[] = TEXT("Microsoft Sidewinder 3D Pro"); static TCHAR cszREGKEYNAME[] = TEXT("SIDEWNDR.DLL<0000>"); /* * DEFINITIONS ________________________________________________________________ * */ #define msecTimerCLOSE 5000 // close unused devices after N msecs typedef enum // JoyAxis { jaX = 0, jaY, jaZ, jaR, jaU, jaV } JoyAxis; #define jaFIRST jaX #define jaLAST jaV typedef struct // JoystickDevice { DWORD nReqForOpen; // count of Opens without Closes HANDLE hFile; // handle to kernel-mode device driver JOYREGHWCONFIG config; // hardware's ranges and capabilities JOYCALIBRATE joycal; // mapping between config and user DWORD tickLast; // tick when joystick last accessed BOOL fFakedOpen; // TRUE if opened for timer to close } JoystickDevice; #define GetString(_psz,_id) LoadString (g.hInstDLL, _id, _psz, cchLENGTH(_psz)) #ifdef min #undef min #endif #define min(_a,_b) ( ((_a)<(_b)) ? (_a) : (_b) ) #ifdef max #undef max #endif #define max(_a,_b) ( ((_a)<(_b)) ? (_b) : (_a) ) #ifndef DivRoundUp #define DivRoundUp(_a,_b) ( (LONG)(((_a) + (_b) -1) / (_b)) ) #endif #ifndef RoundUp #define RoundUp(_a,_b) ( (LONG)( ((_a)+(_b)-1) - ( ((_a)+(_b)-1) % (_b) ))) #endif #ifndef RoundDown #define RoundDown(_a,_b) ( (LONG)(_a) - ((LONG)_a % (LONG)_b) ) #endif #ifndef limit #define limit(_a,_x,_b) (min( max( (_a),(_x) ), (_b) )) #endif #ifndef inlimit #define inlimit(_a,_x,_b) ( (_x >= _a) && (_x <= _b) ) #endif static TCHAR cszPARAMETERS[] = TEXT("Parameters"); /* * PRIVATE DATA _______________________________________________________________ * * Per-process information: * */ struct g // (per-process data) { JoystickDevice *ajd; // Allocated array of devices size_t cjd; // Number of entries in {ajd} HANDLE hInstDLL; // This .DLL's HINSTANCE UINT msgConfigChanged; // JOY_CONFIGCHANGED_MSGSTRING message JOYREGUSERVALUES user; // What ranges callers want to see int timerClose; // Timer to close unused devices } g; /* * PROTOTYPES _________________________________________________________________ * */ #ifdef DEBUG void cdecl dprintf (LPWSTR szFormat, ...); void Joy_DumpUserValues (void); void Joy_DumpHardwareValues (DWORD dwIDZ); #define AXISNAME(_ja) (((_ja)==jaX)?TEXT('X'): ((_ja)==jaY)?TEXT('Y'): \ ((_ja)==jaZ)?TEXT('Z'): ((_ja)==jaR)?TEXT('R'): \ ((_ja)==jaU)?TEXT('U'): ((_ja)==jaV)?TEXT('V'):TEXT('?')) #endif extern MMRESULT WINAPI joyConfigChanged (DWORD dwFlags); // from WinMM HANDLE Joy_OpenDevice (DWORD dwIDZ); HANDLE Joy_GetDeviceHandle (DWORD dwIDZ); BOOL Joy_CloseDeviceHandle (DWORD dwIDZ); BOOL Joy_CloseDevice (DWORD dwIDZ); void Joy_CloseAllHandles (void); void Joy_CloseAll (void); void CALLBACK Joy_TimerProc (HWND hWnd, UINT msg, UINT id, DWORD dwTime); void Joy_RequeryAll (BOOL fRewriteRegistry); void Joy_Requery (BOOL fRewriteRegistry, DWORD dwIDZ, HANDLE hFile); BOOL Joy_ReadUserValues (void); void Joy_WriteUserValues (void); void Joy_DefaultUserValues (void); void Joy_DeleteHardwareValues (void); BOOL Joy_ReadHardwareValues (DWORD dwIDZ); void Joy_WriteHardwareValues (DWORD dwIDZ); void Joy_DefaultHardwareValues (DWORD dwIDZ, HANDLE hFile); MMRESULT Joy_RecalcCalibration (DWORD dwIDZ); DWORD Joy_GetMinAxisValueHardware (DWORD dwIDZ, JoyAxis ja); DWORD Joy_GetMaxAxisValueHardware (DWORD dwIDZ, JoyAxis ja); DWORD Joy_GetMinAxisValueUser (JoyAxis ja); DWORD Joy_GetMaxAxisValueUser (JoyAxis ja); DWORD Joy_GetDeadZonePercentage (JoyAxis ja); MMRESULT Joy_GetDevCaps (DWORD dwIDZ, JOYCAPS *pjc); MMRESULT Joy_GetPos (DWORD dwIDZ, JOYINFO *pji); MMRESULT Joy_GetPosEx (DWORD dwIDZ, JOYINFOEX *pjiex); MMRESULT Joy_SetCalibration (DWORD dwIDZ, JOYCALIBRATE *pjcNew, JOYCALIBRATE *pjcOld); BOOL CALLBACK Joy_ConfigDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp); void Joy_RemoveService (LPTSTR pszKernel); /* * ROUTINES ___________________________________________________________________ * */ /*** DLLEntryPoint - General library initialization/termination code * */ BOOL WINAPI DLLEntryPoint (HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lp) { switch (fdwReason) { case DLL_PROCESS_ATTACH: memset (&g, 0x00, sizeof(g)); g.hInstDLL = hInstDLL; g.msgConfigChanged = RegisterWindowMessage (TEXT(JOY_CONFIGCHANGED_MSGSTRING)); g.ajd = NULL; g.cjd = 0; break; case DLL_PROCESS_DETACH: KillTimer (NULL, g.timerClose); Joy_CloseAll (); break; } return TRUE; } /*************************************************************************** * @doc INTERNAL * * @api LONG | DriverProc | The entry point for an installable driver. * * @parm DWORD | dwDriverId | For most messages,

is the DWORD * value that the driver returns in response to a message. * Each time that the driver is opened, through the API, * the driver receives a message and can return an * arbitrary, non-zero value. The installable driver interface * saves this value and returns a unique driver handle to the * application. Whenever the application sends a message to the * driver using the driver handle, the interface routes the message * to this entry point and passes the corresponding

. * This mechanism allows the driver to use the same or different * identifiers for multiple opens but ensures that driver handles * are unique at the application interface layer. * * The following messages are not related to a particular open * instance of the driver. For these messages, the dwDriverId * will always be zero. * * DRV_LOAD, DRV_FREE, DRV_ENABLE, DRV_DISABLE, DRV_OPEN * * @parm HANDLE | hDriver | This is the handle returned to the * application by the driver interface. * * @parm WORD | wMessage | The requested action to be performed. Message * values below are used for globally defined messages. * Message values from to are used for * defined driver protocols. Messages above are used * for driver specific messages. * * @parm LONG | lParam1 | Data for this message. Defined separately for * each message * * @parm LONG | lParam2 | Data for this message. Defined separately for * each message * * @rdesc Defined separately for each message. ***************************************************************************/ LRESULT WINAPI DriverProc (DWORD dwID, HDRVR hDriver, UINT msg, LPARAM lp1, LPARAM lp2) { switch (msg) { // DRV_LOAD should return a nonzero value if the driver is // properly installed. Since our kernel-mode driver can be // started or stopped at any time, we'll just pretend // everything is hunky-dorey, even if we can't talk to it now. // case DRV_LOAD: DPF((L"DRV_LOAD")); Joy_RequeryAll (FALSE); // FALSE=read from registry if possible return 1L; break; // DRV_FREE is an appropriate place to free any per-process // data allocated during DRV_LOAD or since. Its return value // is ignored. // case DRV_FREE: DPF((L"DRV_FREE")); Joy_CloseAll (); return 0L; break; // DRV_OPEN is a request to start using a particular device; // lParam2 specifies a zero-based device number. The return // value should be nonzero if successful; we'll use a one-based // device number as the RC. // case DRV_OPEN: DPF((L"DRV_OPEN")); return (LRESULT)( lp2 + 1 ); break; // DRV_CLOSE is a notification that a device opened by a // call to DRV_OPEN will no longer be used. The return // value should be nonzero if successful. // case DRV_CLOSE: DPF((L"DRV_CLOSE")); return 1L; break; // DRV_ENABLE/DRV_DISABLE are intended to enable and disable // the given device; their return codes are ignored. We don't // do much with these. // case DRV_ENABLE: DPF((L"DRV_ENABLE")); return 1L; break; case DRV_DISABLE: DPF((L"DRV_DISABLE")); return 1L; break; // DRV_QUERYCONFIGURE/DRV_CONFIGURE are used to configure // the driver. Indeed, this driver does have a configuration // dialog--although the Joystick CPL applet is used to // calibrate the joystick, this driver lets you configure // the parameters given to SIDEWNDR.SYS. // case DRV_QUERYCONFIGURE: return 1L; break; case DRV_CONFIGURE: { int rc; static LPTSTR pszKernel = cszOURKERNELDRIVER; // TODO: Query this rc = DialogBoxParam (g.hInstDLL, MAKEINTRESOURCE( DLG_CONFIG ), (HWND)lp1, Joy_ConfigDlgProc, (LPARAM)pszKernel); return (LRESULT)rc; } break; // DRV_INSTALL is sent when the driver is being installed // on a machine. We require the DRV_CONFIGURE message before // being functional, as that configures and creates the // service which talks to the hardware. Also, USER's WinMM // doesn't know about the joystick until we reboot. // case DRV_INSTALL: DPF((L"DRV_INSTALL")); return DRVCNF_RESTART; break; // DRVREMOVE is sent when the driver is being removed // from the system. Here, we must delete the kernel service. // case DRV_REMOVE: { static LPTSTR pszKernel = cszOURKERNELDRIVER; // TODO: Query this DPF((L"DRV_REMOVE")); Joy_CloseAll (); Joy_RemoveService (pszKernel); return 1L; } break; // JDD_GETNUMDEVS is used to obtain the number of // devices supported by this driver. Note: This is // NOT required to be the number of devices currently // attached or the number we expect to get later. // It's the maximum number we'll ever use. // case JDD_GETNUMDEVS: DPF((L"JDD_GENUMDEVS")); return MAX_JOYSTICKS_SUPPORTED; break; // JDD_GETDEVCAPS is used to obtain information about the // capabilities of a particular device; it should return // a JOYERR code. // case JDD_GETDEVCAPS: { JOYCAPS jc; MMRESULT rc; DPF((L"JDD_GETDEVCAPS, dwID=0x%08lX, lp1=0x%08lX, lp2=0x%08lX",dwID,lp1,lp2)); if ( ((rc = Joy_GetDevCaps (dwID-1, &jc)) == JOYERR_NOERROR) && ((JOYCAPS *)lp1 != (JOYCAPS *)0) ) { memcpy( (char *)lp1, (char *)&jc, min(sizeof(jc),lp2) ); if ((size_t)lp2 > sizeof(jc)) memset( ((char *)lp1)+sizeof(jc), 0x00, (size_t)lp2-sizeof(jc) ); } return rc; } break; // JDD_SETCALIBRATION is used to provide new calibration // information to a joystick. Note that this information is // not stored anywhere, and will be lost during a rebot. // The proper way to fix this is to change the user ranges // or hardware ranges. // case JDD_SETCALIBRATION: { MMRESULT rc; DPF((L"JDD_SETCAL, dwID=0x%08lX, lp1=0x%08lX, lp2=0x%08lX",dwID,lp1,lp2)); if ( ((JOYCALIBRATE *)lp1) == (JOYCALIBRATE *)0 ) return JOYERR_PARMS; if ( (((JOYCALIBRATE *)lp2) == (JOYCALIBRATE *)0) ) { JOYCALIBRATE jcDummy; rc = Joy_SetCalibration (dwID-1, (JOYCALIBRATE *)lp1, &jcDummy); } else { rc = Joy_SetCalibration (dwID-1, (JOYCALIBRATE *)lp1, (JOYCALIBRATE *)lp2); } return rc; } break; // JDD_GETPOS is used to obtain the current position of // the given joystick; it should return a JOYERR code. // case JDD_GETPOS: { JOYINFO ji; MMRESULT rc; DPF((L"JDD_GETPOS, dwID=0x%08lX, lp1=0x%08lX, lp2=0x%08lX",dwID,lp1,lp2)); if ((rc = Joy_GetPos (dwID-1, &ji)) == JOYERR_NOERROR) { if ((JOYINFO *)lp1 != (JOYINFO *)0) { memcpy ((char *)lp1, (char *)&ji, sizeof(JOYINFO)); } } return rc; } break; // JDD_GETPOSEX is used to obtain the current position of // the given joystick; it should return a JOYERR code. // case JDD_GETPOSEX: { JOYINFOEX jiex; MMRESULT rc; DPF((L"JDD_GETPOSEX, dwID=0x%08lX, lp1=0x%08lX, lp2=0x%08lX",dwID,lp1,lp2)); jiex.dwSize = sizeof(jiex); jiex.dwFlags = JOY_RETURNALL; if ( ( ((JOYINFOEX *)lp1) != (JOYINFOEX *)0 ) && ( ((JOYINFOEX *)lp1)->dwSize == sizeof(JOYINFOEX) ) ) { jiex.dwFlags = ((JOYINFOEX *)lp1)->dwFlags; } if ((rc = Joy_GetPosEx (dwID-1, &jiex)) == JOYERR_NOERROR) { if ( ( ((JOYINFOEX *)lp1) != (JOYINFOEX *)0 ) && ( ((JOYINFOEX *)lp1)->dwSize == sizeof(JOYINFOEX) ) ) { memcpy ((char *)lp1, (char *)&jiex, sizeof(JOYINFOEX)); } } return rc; } break; // JDD_CONFIGCHANGED is broadcast whenever the joystick's // configuration has changed. // case JDD_CONFIGCHANGED: { DPF((L"JDD_CONFIGCHANGED")); Joy_RequeryAll (FALSE); PostMessage (HWND_BROADCAST, g.msgConfigChanged, 0, 0L); return JOYERR_NOERROR; } break; #ifdef JDD_CLOSEALL // TODO: Export this?? // JDD_CLOSEALL is broadcast whenever the kernel-mode driver // needs to be able to shut down; close any open handles to // it we may have. // case JDD_CLOSEALL: Joy_CloseAllHandles (); break; #endif } return DefDriverProc (dwID, hDriver, msg, lp1, lp2); } HANDLE Joy_OpenDevice (DWORD dwIDZ) { HANDLE hFile; DPF((L" Joy_OpenDevice (%lu)", dwIDZ)); if (!g.ajd || dwIDZ >= g.cjd) { size_t ii; size_t cjdNew = 1+ (size_t)dwIDZ; JoystickDevice *ajdNew; ajdNew = (JoystickDevice *)GlobalAlloc (GMEM_FIXED, sizeof(JoystickDevice) * cjdNew); if (!ajdNew) { return INVALID_HANDLE_VALUE; } for (ii = 0; ii < g.cjd; ++ii) { ajdNew[ ii ] = g.ajd[ ii ]; } for ( ; ii < cjdNew; ++ii) { ajdNew[ ii ].nReqForOpen = 0; ajdNew[ ii ].hFile = INVALID_HANDLE_VALUE; } if (g.ajd) { GlobalFree ((HGLOBAL)g.ajd); } g.ajd = ajdNew; g.cjd = cjdNew; } ++g.ajd[ dwIDZ ].nReqForOpen; if ((hFile = Joy_GetDeviceHandle (dwIDZ)) == INVALID_HANDLE_VALUE) { --g.ajd[ dwIDZ ].nReqForOpen; } else if (+g.ajd[ dwIDZ ].nReqForOpen == 1) { g.ajd[ dwIDZ ].fFakedOpen = FALSE; Joy_Requery (FALSE, dwIDZ, g.ajd[ dwIDZ ].hFile); } return hFile; } HANDLE Joy_GetDeviceHandle (DWORD dwIDZ) { DPF((L" Joy_GetDeviceHandle (%lu; %lu)", (LONG)dwIDZ, (LONG)g.cjd)); if (!g.ajd || dwIDZ >= g.cjd || !g.ajd[ dwIDZ ].nReqForOpen) { HANDLE hFile; DPF((L" opening device for timer")); if (g.timerClose == 0) g.timerClose = SetTimer (NULL, 0, msecTimerCLOSE, Joy_TimerProc); if (g.timerClose == 0) return INVALID_HANDLE_VALUE; hFile = Joy_OpenDevice (dwIDZ); // close will be done by timer if (g.ajd && dwIDZ < g.cjd) { g.ajd[ dwIDZ ].fFakedOpen = TRUE; } return hFile; } if (g.ajd[ dwIDZ ].hFile == INVALID_HANDLE_VALUE) { TCHAR szDeviceName[ 256 ]; DWORD dwFlags; #define DD_JOYSTICK_DEVICE_NAME TEXT("\\Device\\Joy") wsprintf (szDeviceName, TEXT("\\\\.%s%ld"), DD_JOYSTICK_DEVICE_NAME + lstrlen(TEXT("\\Device")), 1+ dwIDZ); DPF((L" opening Device '%s'...", szDeviceName)); g.ajd[ dwIDZ ].hFile = CreateFile (szDeviceName, // ("\\.\Joy1" etc) GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } if (g.ajd[ dwIDZ ].hFile != INVALID_HANDLE_VALUE) { DPF((L" ...returning handle 0x%08lX", (LONG)g.ajd[ dwIDZ ].hFile)); g.ajd[ dwIDZ ].tickLast = GetTickCount(); } return g.ajd[ dwIDZ ].hFile; } BOOL Joy_CloseDeviceHandle (DWORD dwIDZ) { DPF((L" Joy_CloseDeviceHandle (%lu)", dwIDZ)); if (!g.ajd || dwIDZ >= g.cjd) return FALSE; if (g.ajd[ dwIDZ ].nReqForOpen == 0) return FALSE; if (g.ajd[ dwIDZ ].hFile != INVALID_HANDLE_VALUE) { DPF((L" closing handle 0x%08lX", (LONG)g.ajd[ dwIDZ ].hFile)); CloseHandle (g.ajd[ dwIDZ ].hFile); g.ajd[ dwIDZ ].hFile = INVALID_HANDLE_VALUE; } return TRUE; } BOOL Joy_CloseDevice (DWORD dwIDZ) { DPF((L" Joy_CloseDevice (%lu)", dwIDZ)); if (!g.ajd || dwIDZ >= g.cjd) return FALSE; if (g.ajd[ dwIDZ ].nReqForOpen <= 1) Joy_CloseDeviceHandle (dwIDZ); if (g.ajd[ dwIDZ ].nReqForOpen > 0) --g.ajd[ dwIDZ ].nReqForOpen; return TRUE; } void Joy_CloseAllHandles (void) { DWORD dwIDZ; DPF((L"Joy_CloseAllHandles")); for (dwIDZ = 0; dwIDZ < g.cjd; ++dwIDZ) { if (g.ajd[ dwIDZ ].nReqForOpen) { if (g.ajd[ dwIDZ ].hFile != INVALID_HANDLE_VALUE) { Joy_CloseDeviceHandle (dwIDZ); } } } } void CALLBACK Joy_TimerProc (HWND hWnd, UINT msg, UINT id, DWORD dwTime) { DWORD dwIDZ; for (dwIDZ = 0; dwIDZ < g.cjd; ++dwIDZ) { if (g.ajd[ dwIDZ ].nReqForOpen) { if (g.ajd[ dwIDZ ].hFile != INVALID_HANDLE_VALUE) { if (GetTickCount() > g.ajd[ dwIDZ ].tickLast +msecTimerCLOSE) { DPF((L"Joy_TimerProc: closing device...")); if (g.ajd[ dwIDZ ].fFakedOpen && g.ajd[ dwIDZ ].nReqForOpen==1) Joy_CloseDevice (dwIDZ); else Joy_CloseDeviceHandle (dwIDZ); } } } } } void Joy_CloseAll (void) { DPF((L"Joy_CloseAll")); Joy_CloseAllHandles (); if (g.ajd != NULL) { GlobalFree ((HGLOBAL)g.ajd); g.ajd = NULL; g.cjd = 0; } } void Joy_RequeryAll (BOOL fRewriteRegistry) { DWORD dwIDZ; DPF((L"Joy_RequeryAll")); if (fRewriteRegistry || !Joy_ReadUserValues ()) { Joy_DefaultUserValues (); Joy_WriteUserValues (); } #ifdef DEBUG Joy_DumpUserValues(); #endif if (fRewriteRegistry) { Joy_DeleteHardwareValues (); } for (dwIDZ = 0; dwIDZ < MAX_JOYSTICKS_SUPPORTED; ++dwIDZ) { if ( (fRewriteRegistry) || (dwIDZ < g.cjd && g.ajd[ dwIDZ ].nReqForOpen) ) { HANDLE hFile; if ((hFile = Joy_GetDeviceHandle (dwIDZ)) != INVALID_HANDLE_VALUE) { // Refigger any settings for this device // Joy_Requery (fRewriteRegistry, dwIDZ, hFile); } } } } void Joy_Requery (BOOL fRewriteRegistry, DWORD dwIDZ, HANDLE hFile) { DPF((L"Joy_Requery (%lu, 0x%08lX)", dwIDZ, (LONG)hFile)); if (fRewriteRegistry || !Joy_ReadHardwareValues (dwIDZ)) { Joy_DefaultHardwareValues (dwIDZ, hFile); Joy_WriteHardwareValues (dwIDZ); } #ifdef DEBUG Joy_DumpHardwareValues(dwIDZ); #endif (void)Joy_RecalcCalibration (dwIDZ); } BOOL Joy_ReadUserValues (void) { HKEY hk; BOOL rc = FALSE; DPF((L"Joy_ReadUserValues")); DPF((L" ...opening key %s", REGSTR_PATH_JOYCONFIG)); if (RegOpenKey (HKEY_LOCAL_MACHINE, REGSTR_PATH_JOYCONFIG, &hk) == 0) { DWORD dwType; DWORD cb = sizeof (g.user); DPF((L" ...reading value %s", REGSTR_VAL_JOYUSERVALUES)); if (RegQueryValueEx (hk, REGSTR_VAL_JOYUSERVALUES, 0, &dwType, (LPBYTE)&g.user, &cb) == 0) { if ( (dwType == REG_BINARY) && (cb == sizeof (g.user)) ) rc = TRUE; } RegCloseKey (hk); } return rc; } void Joy_WriteUserValues (void) { HKEY hk; DPF((L"Joy_WriteUserValues")); DPF((L" ...creating key %s", REGSTR_PATH_JOYCONFIG)); if (RegCreateKey (HKEY_LOCAL_MACHINE, REGSTR_PATH_JOYCONFIG, &hk) == 0) { DPF((L" ...setting value %s", REGSTR_VAL_JOYUSERVALUES)); RegSetValueEx (hk, REGSTR_VAL_JOYUSERVALUES, 0, REG_BINARY, (LPBYTE)&g.user, sizeof(g.user)); RegCloseKey (hk); } } void Joy_DefaultUserValues (void) { DPF((L"Joy_DefaultUserValues")); g.user.dwTimeOut = DEFAULT_TIMEOUT; g.user.jrvRanges.jpMin.dwX = DEFAULT_RANGE_MIN; g.user.jrvRanges.jpMin.dwY = DEFAULT_RANGE_MIN; g.user.jrvRanges.jpMin.dwZ = DEFAULT_RANGE_MIN; g.user.jrvRanges.jpMin.dwR = DEFAULT_RANGE_MIN; g.user.jrvRanges.jpMin.dwU = DEFAULT_RANGE_MIN; g.user.jrvRanges.jpMin.dwV = DEFAULT_RANGE_MIN; g.user.jrvRanges.jpMax.dwX = DEFAULT_RANGE_MAX; g.user.jrvRanges.jpMax.dwY = DEFAULT_RANGE_MAX; g.user.jrvRanges.jpMax.dwZ = DEFAULT_RANGE_MAX; g.user.jrvRanges.jpMax.dwR = DEFAULT_RANGE_MAX; g.user.jrvRanges.jpMax.dwU = DEFAULT_RANGE_MAX; g.user.jrvRanges.jpMax.dwV = DEFAULT_RANGE_MAX; g.user.jpDeadZone.dwX = DEFAULT_DEADZONE; g.user.jpDeadZone.dwY = DEFAULT_DEADZONE; g.user.jpDeadZone.dwZ = DEFAULT_DEADZONE; g.user.jpDeadZone.dwR = DEFAULT_DEADZONE; g.user.jpDeadZone.dwU = DEFAULT_DEADZONE; g.user.jpDeadZone.dwV = DEFAULT_DEADZONE; } BOOL Joy_ReadHardwareValues (DWORD dwIDZ) { TCHAR szKey[ MAX_PATH ]; HKEY hk; BOOL rc = FALSE; DPF((L"Joy_ReadHardwareValues (%lu)", dwIDZ)); wsprintf (szKey, TEXT("%s\\%s\\%s"), REGSTR_PATH_JOYCONFIG, cszREGKEYNAME, REGSTR_KEY_JOYCURR); DPF((L" ...opening key %s", szKey)); if (RegOpenKey (HKEY_LOCAL_MACHINE, szKey, &hk) == 0) { TCHAR szValue[ MAX_PATH ]; DWORD dwType; DWORD cb = sizeof (g.ajd[ dwIDZ ].config); wsprintf (szValue, REGSTR_VAL_JOYNCONFIG, 1 + (int)dwIDZ); DPF((L" ...reading value %s", szValue)); if (RegQueryValueEx (hk, szValue, 0, &dwType, (LPBYTE)&g.ajd[ dwIDZ ].config, &cb) == 0) { if ( (dwType == REG_BINARY) && (cb == sizeof (g.ajd[ dwIDZ ].config)) ) rc = TRUE; } RegCloseKey (hk); } return rc; } void Joy_WriteHardwareValues (DWORD dwIDZ) { TCHAR szKey[ MAX_PATH ]; HKEY hk; DPF((L"Joy_WriteHardwareValues (%lu)", dwIDZ)); wsprintf (szKey, TEXT("%s\\%s\\%s"), REGSTR_PATH_JOYCONFIG, cszREGKEYNAME, REGSTR_KEY_JOYCURR); DPF((L" ...creating key %s", szKey)); if (RegCreateKey (HKEY_LOCAL_MACHINE, szKey, &hk) == 0) { TCHAR szValue[ MAX_PATH ]; wsprintf (szValue, REGSTR_VAL_JOYNCONFIG, 1 + (int)dwIDZ); DPF((L" ...setting value %s", szValue)); RegSetValueEx (hk, szValue, 0, REG_BINARY, (LPBYTE)&g.ajd[ dwIDZ ].config, sizeof (g.ajd[ dwIDZ ].config)); wsprintf (szValue, REGSTR_VAL_JOYNOEMNAME, 1 + (int)dwIDZ); RegSetValueEx (hk, szValue, 0, REG_SZ, (LPBYTE)cszOUROEMNAME, sizeof(TCHAR) * (1+lstrlen(cszOUROEMNAME))); RegCloseKey (hk); } } void Joy_DeleteHardwareValues (void) { TCHAR szKey[ MAX_PATH ]; wsprintf (szKey, TEXT("%s\\%s\\%s"), REGSTR_PATH_JOYCONFIG, cszREGKEYNAME, REGSTR_KEY_JOYCURR); RegDeleteKey (HKEY_LOCAL_MACHINE, szKey); } void Joy_DefaultHardwareValues (DWORD dwIDZ, HANDLE hFile) { JOYREGHWCONFIG *pcfg = &g.ajd[ dwIDZ ].config; DWORD dwBytesRead; DPF((L"Joy_DefaultHardwareValues (%lu, 0x%08lX)", dwIDZ, (LONG)hFile)); /* * First, try to get the kernel driver to fill out the hardware * settings--if it's an NT-compliant driver, it'll support this * (our SIDEWNDR.SYS does, anyway). * */ dwBytesRead = 0; DPF((L" sending IOCTL_JOY_GET_JOYREGHWCONFIG...")); if (DeviceIoControl (hFile, (DWORD)IOCTL_JOY_GET_JOYREGHWCONFIG, (LPVOID)0, (DWORD)0, (LPVOID)pcfg, (DWORD)sizeof(JOYREGHWCONFIG), &dwBytesRead, (LPOVERLAPPED)0)) { if (dwBytesRead == sizeof(JOYREGHWCONFIG)) { DPF((L" IOCTL_JOY_GET_JOYREGHWCONFIG succeeded")); return; // Success! } } DPF((L" IOCTL_JOY_GET_JOYREGHWCONFIG failed")); /* * Bummer--looks like it's some 3rd party driver, which wasn't * made properly. Or, it's also possible that the kernel portion of * this driver just isn't installed properly. We'll vote for the * latter, and assume we've got the default SideWinder characteristics. * */ pcfg->hws.dwFlags = JOY_HWS_HASPOV | JOY_HWS_POVISBUTTONCOMBOS | JOY_HWS_HASR | JOY_HWS_HASZ; pcfg->hws.dwNumButtons = 4; pcfg->dwUsageSettings = JOY_US_HASRUDDER | JOY_US_PRESENT | JOY_US_ISOEM; pcfg->hwv.jrvHardware.jpMin.dwX = 0; pcfg->hwv.jrvHardware.jpMin.dwY = 0; pcfg->hwv.jrvHardware.jpMin.dwZ = 0; pcfg->hwv.jrvHardware.jpMin.dwR = 0; pcfg->hwv.jrvHardware.jpMin.dwU = 0; pcfg->hwv.jrvHardware.jpMin.dwV = 0; pcfg->hwv.jrvHardware.jpMax.dwX = DEFAULT_HWRANGE_X; pcfg->hwv.jrvHardware.jpMax.dwY = DEFAULT_HWRANGE_Y; pcfg->hwv.jrvHardware.jpMax.dwZ = DEFAULT_HWRANGE_U; pcfg->hwv.jrvHardware.jpMax.dwR = DEFAULT_HWRANGE_R; pcfg->hwv.jrvHardware.jpMax.dwU = 0; pcfg->hwv.jrvHardware.jpMax.dwV = 0; pcfg->hwv.jrvHardware.jpCenter.dwX = DEFAULT_HWRANGE_X/2; pcfg->hwv.jrvHardware.jpCenter.dwY = DEFAULT_HWRANGE_Y/2; pcfg->hwv.jrvHardware.jpCenter.dwZ = DEFAULT_HWRANGE_U/2; pcfg->hwv.jrvHardware.jpCenter.dwR = DEFAULT_HWRANGE_R/2; pcfg->hwv.jrvHardware.jpCenter.dwU = 0; pcfg->hwv.jrvHardware.jpCenter.dwV = 0; pcfg->hwv.dwCalFlags = JOY_ISCAL_POV; pcfg->dwType = JOY_HW_CUSTOM; pcfg->dwReserved = 0; } DWORD Joy_GetDeadZonePercentage (JoyAxis ja) { switch (ja) { case jaX: return (g.user.jpDeadZone.dwX); case jaY: return (g.user.jpDeadZone.dwY); case jaZ: return (g.user.jpDeadZone.dwZ); case jaR: return (g.user.jpDeadZone.dwR); case jaU: return (g.user.jpDeadZone.dwU); case jaV: return (g.user.jpDeadZone.dwV); } return 0; } DWORD Joy_GetMinAxisValueUser (JoyAxis ja) { switch (ja) { case jaX: return (g.user.jrvRanges.jpMin.dwX); case jaY: return (g.user.jrvRanges.jpMin.dwY); case jaZ: return (g.user.jrvRanges.jpMin.dwZ); case jaR: return (g.user.jrvRanges.jpMin.dwR); case jaU: return (g.user.jrvRanges.jpMin.dwU); case jaV: return (g.user.jrvRanges.jpMin.dwV); } return 0; } DWORD Joy_GetMaxAxisValueUser (JoyAxis ja) { switch (ja) { case jaX: return (g.user.jrvRanges.jpMax.dwX); case jaY: return (g.user.jrvRanges.jpMax.dwY); case jaZ: return (g.user.jrvRanges.jpMax.dwZ); case jaR: return (g.user.jrvRanges.jpMax.dwR); case jaU: return (g.user.jrvRanges.jpMax.dwU); case jaV: return (g.user.jrvRanges.jpMax.dwV); } return 0; } DWORD Joy_GetMinAxisValueHardware (DWORD dwIDZ, JoyAxis ja) { switch (ja) { case jaX: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMin.dwX); case jaY: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMin.dwY); case jaZ: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMin.dwZ); case jaR: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMin.dwR); case jaU: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMin.dwU); case jaV: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMin.dwV); } return 0; } DWORD Joy_GetMaxAxisValueHardware (DWORD dwIDZ, JoyAxis ja) { switch (ja) { case jaX: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMax.dwX); case jaY: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMax.dwY); case jaZ: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMax.dwZ); case jaR: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMax.dwR); case jaU: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMax.dwU); case jaV: return (g.ajd[ dwIDZ ].config.hwv.jrvHardware.jpMax.dwV); } return 0; } MMRESULT Joy_RecalcCalibration (DWORD dwIDZ) { int ja; JOYCALIBRATE jcal; JOYCALIBRATE jcalOld; DPF((L"Joy_RecalcCalibration (%lu)", dwIDZ)); for (ja = (int)jaFIRST; ja <= (int)jaLAST; ++ja) { DWORD dwMinHardware = Joy_GetMinAxisValueHardware (dwIDZ, ja); DWORD dwMaxHardware = Joy_GetMaxAxisValueHardware (dwIDZ, ja); DWORD dwMinUser = Joy_GetMinAxisValueUser (ja); DWORD dwMaxUser = Joy_GetMaxAxisValueUser (ja); DWORD dwRangeHardware = dwMaxHardware - dwMinHardware; DWORD dwRangeUser = dwMaxUser - dwMinUser; WORD *pwBase = NULL; WORD *pwDelta = NULL; switch (ja) { case jaX: pwBase = &jcal.wXbase; pwDelta = &jcal.wXdelta; break; case jaY: pwBase = &jcal.wYbase; pwDelta = &jcal.wYdelta; break; case jaZ: pwBase = &jcal.wZbase; pwDelta = &jcal.wZdelta; break; case jaR: continue; case jaU: continue; case jaV: continue; } if ((dwRangeHardware == 0) || (dwRangeUser == 0)) { *pwBase = 0; *pwDelta = 0; continue; } DPF((L" %c Axis:", AXISNAME(ja))); DPF((L" hardware: %ld..%ld (range=%ld)", dwMinHardware, dwMaxHardware, dwRangeHardware)); DPF((L" user: %ld..%ld (range=%ld)", dwMinUser, dwMaxUser, dwRangeUser)); *pwDelta = (WORD)DivRoundUp( dwRangeUser, dwRangeHardware ); *pwBase = (WORD)( dwMinHardware - dwMinUser ); } return Joy_SetCalibration (dwIDZ, &jcal, &jcalOld); } MMRESULT Joy_GetDevCaps (DWORD dwIDZ, JOYCAPS *pjc) { HANDLE hFile; DPF((L"Joy_GetDevCaps (%lu)", dwIDZ)); if ((hFile = Joy_GetDeviceHandle (dwIDZ)) == INVALID_HANDLE_VALUE) return JOYERR_PARMS; pjc->wMid = MM_MICROSOFT; // manufacturer id pjc->wPid = MM_PC_JOYSTICK; // product id pjc->szPname[0] = TEXT('\0'); // (just in case LoadString fails) GetString (pjc->szPname, IDS_DESCRIPTION); // product description pjc->wXmin = Joy_GetMinAxisValueUser (jaX); pjc->wXmax = Joy_GetMaxAxisValueUser (jaX); pjc->wYmin = Joy_GetMinAxisValueUser (jaY); pjc->wYmax = Joy_GetMaxAxisValueUser (jaY); pjc->wZmin = 0; pjc->wZmax = 0; pjc->wRmin = 0; pjc->wRmax = 0; pjc->wUmin = 0; pjc->wUmax = 0; pjc->wVmin = 0; pjc->wVmax = 0; pjc->wNumButtons = (WORD)g.ajd[ dwIDZ ].config.hws.dwNumButtons; pjc->wPeriodMin = MIN_PERIOD; pjc->wPeriodMax = MAX_PERIOD; wsprintf (pjc->szRegKey, cszREGKEYNAME); lstrcpy (pjc->szOEMVxD, cszOURKERNELDRIVER); // TODO: Ask kernel driver pjc->wCaps = 0; pjc->wNumAxes = 2; // all joysticks have X && Y if ( Joy_GetMinAxisValueHardware (dwIDZ, jaZ) != Joy_GetMaxAxisValueHardware (dwIDZ, jaZ) ) { pjc->wCaps |= JOYCAPS_HASZ; ++pjc->wNumAxes; pjc->wZmin = Joy_GetMinAxisValueUser (jaZ); pjc->wZmax = Joy_GetMaxAxisValueUser (jaZ); } if ( Joy_GetMinAxisValueHardware (dwIDZ, jaR) != Joy_GetMaxAxisValueHardware (dwIDZ, jaR) ) { pjc->wCaps |= JOYCAPS_HASR; ++pjc->wNumAxes; pjc->wRmin = Joy_GetMinAxisValueUser (jaR); pjc->wRmax = Joy_GetMaxAxisValueUser (jaR); } if ( Joy_GetMinAxisValueHardware (dwIDZ, jaU) != Joy_GetMaxAxisValueHardware (dwIDZ, jaU) ) { pjc->wCaps |= JOYCAPS_HASU; ++pjc->wNumAxes; pjc->wUmin = Joy_GetMinAxisValueUser (jaU); pjc->wUmax = Joy_GetMaxAxisValueUser (jaU); } if ( Joy_GetMinAxisValueHardware (dwIDZ, jaV) != Joy_GetMaxAxisValueHardware (dwIDZ, jaV) ) { pjc->wCaps |= JOYCAPS_HASV; ++pjc->wNumAxes; pjc->wVmin = Joy_GetMinAxisValueUser (jaV); pjc->wVmax = Joy_GetMaxAxisValueUser (jaV); } if (g.ajd[ dwIDZ ].config.hws.dwFlags & JOY_HWS_HASPOV) { pjc->wCaps |= JOYCAPS_HASPOV; if (g.ajd[ dwIDZ ].config.hws.dwFlags & JOY_HWS_POVISPOLL) pjc->wCaps |= JOYCAPS_POVCTS; else // (g.ajd[ dwIDZ ].config.hws.dwFlags & JOY_HWS_POVISBUTTONCOMBOS) pjc->wCaps |= JOYCAPS_POV4DIR; if (g.ajd[ dwIDZ ].config.hws.dwFlags & JOY_HWS_POVISPOLL) ++pjc->wNumAxes; } pjc->wMaxButtons = MAX_BUTTONS_SUPPORTED; pjc->wMaxAxes = MAX_AXES_SUPPORTED; return JOYERR_NOERROR; } MMRESULT Joy_SetCalibration (DWORD dwIDZ, JOYCALIBRATE *pjcNew, JOYCALIBRATE *pjcOld) { HANDLE hFile; DPF((L"Joy_SetCalibration (%lu)", dwIDZ)); if ((hFile = Joy_GetDeviceHandle (dwIDZ)) == INVALID_HANDLE_VALUE) return JOYERR_PARMS; *pjcOld = g.ajd[ dwIDZ ].joycal; g.ajd[ dwIDZ ].joycal = *pjcNew; return JOYERR_NOERROR; } MMRESULT Joy_GetPos (DWORD dwIDZ, JOYINFO *pji) { JOYINFOEX jiex; MMRESULT rc; DPF((L"Joy_GetPos (%lu)", dwIDZ)); jiex.dwSize = sizeof(jiex); jiex.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS; if ((rc = Joy_GetPosEx (dwIDZ, &jiex)) != JOYERR_NOERROR) return rc; pji->wXpos = (UINT)jiex.dwXpos; pji->wYpos = (UINT)jiex.dwYpos; pji->wZpos = (UINT)jiex.dwZpos; pji->wButtons = (UINT)jiex.dwButtons; return JOYERR_NOERROR; } #define fCENTERED (dwFlags & JOY_RETURNCENTERED) DWORD Joy_CalculateAxis (DWORD dwIDZ, DWORD dwPos, DWORD dwFlags, JoyAxis axis) { /* * The digital mode doesn't really need calibration for its axes--they * always have the following limits: * X = [0,1024) (0=left) * Y = [0,1024) (0=forward) * R = [0,512) (0=counterclockwise) * U = [0,1024) (0=forward) * * The analog mode needs calibration from machine to machine, but you * can expect values close to: * XTime 20..1600 uS 20 = leftmost, 1600 = rightmost * YTime 20..1600 uS 20 = up, 1600 = down * ZTime 20..1600 uS 20 = left, 1600 = right * TTime 20..1600 uS 20 = forward 1600 = back * * Fortunately, those values are encoded as the calibration for the * digital axis anyway--so it's busines as usual. Remember that the * JOYCALIBRATE structure doesn't have data for R or U. * * Also check for: * RAWDATA = return the hardware's position information directly * DEADZONE = use the g.user.jpDeadZone deadzone percentages * CENTERED = BUGBUG: What the hell does this do, anyway? * */ if (!(dwFlags & JOY_RETURNRAWDATA)) { WORD wBase; WORD wDelta; DWORD dwMinUser = Joy_GetMinAxisValueUser (axis); DWORD dwMaxUser = Joy_GetMaxAxisValueUser (axis); DWORD dwRangeUser = dwMaxUser - dwMinUser; DWORD dwMinHardware = Joy_GetMinAxisValueHardware (dwIDZ, axis); DWORD dwMaxHardware = Joy_GetMaxAxisValueHardware (dwIDZ, axis); DWORD dwRangeHardware = dwMaxHardware - dwMinHardware; dwPos = limit (dwMinHardware, dwPos, dwMaxHardware); if (axis == jaX) { wBase = g.ajd[ dwIDZ ].joycal.wXbase; wDelta = g.ajd[ dwIDZ ].joycal.wXdelta; } else if (axis == jaY) { wBase = g.ajd[ dwIDZ ].joycal.wYbase; wDelta = g.ajd[ dwIDZ ].joycal.wYdelta; } else { if ((dwRangeUser == 0) || (dwRangeHardware == 0)) { wBase = 0; wDelta = 1; } else { wDelta = (WORD)DivRoundUp( dwRangeUser, dwRangeHardware ); wBase = (WORD)( dwMinHardware - dwMinUser ); } } dwPos = (DWORD)( (dwPos-(DWORD)wBase) * (DWORD)wDelta ); if (dwFlags & JOY_USEDEADZONE) { DWORD dwAverageUser = dwMinUser + dwRangeUser/2; DWORD dwDeadZone = Joy_GetDeadZonePercentage (axis); DWORD dwDeadZoneMin = dwAverageUser - ((dwRangeUser*dwDeadZone/100)/2); DWORD dwDeadZoneMax = dwDeadZoneMin + (dwRangeUser*dwDeadZone/100); if ((dwPos >= dwDeadZoneMin) && (dwPos < dwDeadZoneMax)) { dwPos = dwAverageUser; } } dwPos = limit (dwMinUser, dwPos, dwMaxUser); } return dwPos; } MMRESULT Joy_GetPosEx (DWORD dwIDZ, JOYINFOEX *pjiex) { DWORD dwFlags; HANDLE hFile; JOY_DD_INPUT_DATA JoyData; DWORD cbRead; MMRESULT rc = JOYERR_NOERROR; DPF((L"Joy_GetPosEx (%lu)", dwIDZ)); if ((hFile = Joy_GetDeviceHandle (dwIDZ)) == INVALID_HANDLE_VALUE) return JOYERR_PARMS; memset (&JoyData, 0x00, sizeof(JoyData)); cbRead = 0; dwFlags = pjiex->dwFlags; if (dwFlags & JOY_CAL_READXONLY) { dwFlags ^= JOY_CAL_READXONLY; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNX; } if (dwFlags & JOY_CAL_READYONLY) { dwFlags ^= JOY_CAL_READYONLY; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNY; } if (dwFlags & JOY_CAL_READZONLY) { dwFlags ^= JOY_CAL_READZONLY; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNZ; } if (dwFlags & JOY_CAL_READRONLY) { dwFlags ^= JOY_CAL_READRONLY; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNR; } if (dwFlags & JOY_CAL_READUONLY) { dwFlags ^= JOY_CAL_READUONLY; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNU; } if (dwFlags & JOY_CAL_READVONLY) { dwFlags ^= JOY_CAL_READVONLY; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNV; } if (dwFlags & JOY_CAL_READXYONLY) { dwFlags ^= JOY_CAL_READXYONLY; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNX | JOY_RETURNY; } if (dwFlags & JOY_CAL_READ3) { dwFlags ^= JOY_CAL_READ3; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ; } if (dwFlags & JOY_CAL_READ4) { dwFlags ^= JOY_CAL_READ4; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNR; } if (dwFlags & JOY_CAL_READ5) { dwFlags ^= JOY_CAL_READ5; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNR | JOY_RETURNU; } if (dwFlags & JOY_CAL_READ6) { dwFlags ^= JOY_CAL_READ6; dwFlags |= JOY_RETURNRAWDATA | JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNR | JOY_RETURNU | JOY_RETURNV; } pjiex->dwXpos = 0; pjiex->dwYpos = 0; pjiex->dwZpos = 0; pjiex->dwRpos = 0; pjiex->dwUpos = 0; pjiex->dwVpos = 0; pjiex->dwButtons = 0; pjiex->dwPOV = 0; pjiex->dwReserved1 = 0; pjiex->dwReserved2 = 0; DPF((L" reading handle 0x%08lX for position information", (LONG)hFile)); if (!ReadFile (hFile, &JoyData, sizeof(JoyData), &cbRead, NULL)) { DPF((L" ReadFile failed")); rc = MMSYSERR_NODRIVER; } else if (cbRead != sizeof(JoyData)) { DPF((L" ReadFile did not return a JoyData structure")); rc = MMSYSERR_NODRIVER; } else if (JoyData.Unplugged && !(dwFlags & JOY_CAL_READALWAYS)) { DPF((L" ReadFile says joystick is unplugged")); rc = JOYERR_UNPLUGGED; } else { switch (JoyData.Mode) { case SIDEWINDER3P_ANALOG_MODE: { DPF((L" ReadFile says SIDEWINDER3P_ANALOG_MODE")); if (dwFlags & JOY_RETURNBUTTONS) { pjiex->dwButtons = JoyData.u.AnalogData.Buttons; } if (dwFlags & (JOY_RETURNPOV | JOY_RETURNPOVCTS)) { pjiex->dwPOV = JOY_POVCENTERED; } if (dwFlags & JOY_RETURNX) pjiex->dwXpos = Joy_CalculateAxis (dwIDZ, JoyData.u.AnalogData.XTime, dwFlags, jaX); if (dwFlags & JOY_RETURNY) pjiex->dwYpos = Joy_CalculateAxis (dwIDZ, JoyData.u.AnalogData.YTime, dwFlags, jaY); if (dwFlags & JOY_RETURNZ) pjiex->dwZpos = Joy_CalculateAxis (dwIDZ, JoyData.u.AnalogData.TTime, dwFlags, jaZ); if (dwFlags & JOY_RETURNR) pjiex->dwRpos = Joy_CalculateAxis (dwIDZ, JoyData.u.AnalogData.ZTime, dwFlags, jaR); if (dwFlags & JOY_RETURNU) pjiex->dwUpos = 0; if (dwFlags & JOY_RETURNV) pjiex->dwVpos = 0; rc = JOYERR_NOERROR; } break; case SIDEWINDER3P_DIGITAL_MODE: case SIDEWINDER3P_ENHANCED_DIGITAL_MODE: { DPF((L" ReadFile says SIDEWINDER3P_[ENHANCED_]DIGITAL_MODE")); if (dwFlags & JOY_RETURNBUTTONS) { pjiex->dwButtons = JoyData.u.DigitalData.Buttons; } if (dwFlags & (JOY_RETURNPOV | JOY_RETURNPOVCTS)) { #define JOY_POVAROUND (2*JOY_POVBACKWARD) switch (JoyData.u.DigitalData.Hat) { case 1: pjiex->dwPOV = JOY_POVFORWARD; break; case 2: pjiex->dwPOV = (JOY_POVFORWARD + JOY_POVRIGHT) /2; break; case 3: pjiex->dwPOV = JOY_POVRIGHT; break; case 4: pjiex->dwPOV = (JOY_POVRIGHT + JOY_POVBACKWARD) /2; break; case 5: pjiex->dwPOV = JOY_POVBACKWARD; break; case 6: pjiex->dwPOV = (JOY_POVBACKWARD + JOY_POVLEFT) /2; break; case 7: pjiex->dwPOV = JOY_POVLEFT; break; case 8: pjiex->dwPOV = (JOY_POVLEFT + JOY_POVAROUND) /2; break; default: pjiex->dwPOV = JOY_POVCENTERED; break; } } if (dwFlags & JOY_RETURNX) pjiex->dwXpos = Joy_CalculateAxis (dwIDZ, JoyData.u.DigitalData.XOffset, dwFlags, jaX); if (dwFlags & JOY_RETURNY) pjiex->dwYpos = Joy_CalculateAxis (dwIDZ, JoyData.u.DigitalData.YOffset, dwFlags, jaY); if (dwFlags & JOY_RETURNZ) pjiex->dwZpos = Joy_CalculateAxis (dwIDZ, JoyData.u.DigitalData.TOffset, dwFlags, jaZ); if (dwFlags & JOY_RETURNR) pjiex->dwRpos = Joy_CalculateAxis (dwIDZ, JoyData.u.DigitalData.RzOffset, dwFlags, jaR); if (dwFlags & JOY_RETURNU) pjiex->dwUpos = 0; if (dwFlags & JOY_RETURNV) pjiex->dwVpos = 0; rc = JOYERR_NOERROR; } break; default: DPF((L" ReadFile says unknown mode")); rc = JOYERR_NOCANDO; break; } } #ifdef DEBUG if (rc == JOYERR_NOERROR) { dprintf(L" Joy_GetPosEx returning:"); if (dwFlags & JOY_RETURNX) dprintf(L" X = %lu", pjiex->dwXpos); if (dwFlags & JOY_RETURNY) dprintf(L" Y = %lu", pjiex->dwYpos); if (dwFlags & JOY_RETURNZ) dprintf(L" Z = %lu", pjiex->dwZpos); if (dwFlags & JOY_RETURNR) dprintf(L" R = %lu", pjiex->dwRpos); if (dwFlags & JOY_RETURNU) dprintf(L" U = %lu", pjiex->dwUpos); if (dwFlags & JOY_RETURNV) dprintf(L" V = %lu", pjiex->dwVpos); if (dwFlags & JOY_RETURNBUTTONS) dprintf(L" Buttons = 0x%08lX", pjiex->dwButtons); if (dwFlags & (JOY_RETURNPOV | JOY_RETURNPOVCTS)) dprintf(L" Hat = %lu", pjiex->dwPOV); } #endif return rc; } /* * CONFIGURATION ______________________________________________________________ * */ UINT CB_AddString (HWND hItm, LPCTSTR psz, LPARAM lp); void CB_Select (HWND hItm, UINT index); BOOL CB_SelectByData (HWND hItm, LPARAM lpMatch); UINT CB_GetSelected (HWND hItm); LPARAM CB_GetSelectedData (HWND hItm); LPARAM CB_GetData (HWND hItm, UINT index); UINT CB_GetIndex (HWND hItm, LPARAM lpMatch); #define GetCheckBox(h,i) (BOOL)SendDlgItemMessage(h,i,BM_GETCHECK,0,0L) #define SetCheckBox(h,i,b) SendDlgItemMessage(h,i,BM_SETCHECK,(WPARAM)(b),0L) typedef struct { DWORD dwPort; // DeviceAddress (0x201) DWORD nAxes; // NumberOfAxes (2,3,4) DWORD dwType; // DeviceType (1) } KernelConfig; #define cszJOY_DD_NAXES TEXT(JOY_DD_NAXES) #define cszJOY_DD_DEVICE_ADDRESS TEXT(JOY_DD_DEVICE_ADDRESS) #define cszJOY_DD_DEVICE_TYPE TEXT(JOY_DD_DEVICE_TYPE) static DWORD adwValidPorts[] = { 0x0200, 0x0201 }; #define nValidPorts (sizeof(adwValidPorts) / sizeof(adwValidPorts[0])) void Joy_Config_DefaultSettings (KernelConfig *pkc, LPTSTR pszKernel) { pkc->dwPort = JOY_IO_PORT_ADDRESS; pkc->nAxes = 4; pkc->dwType = JOY_TYPE_SIDEWINDER; } void Joy_Config_ReadSettings (KernelConfig *pkc, LPTSTR pszKernel) { HKEY hk; TCHAR szKey[ MAX_PATH ]; wsprintf (szKey, TEXT("%s\\%s\\%s"), REGSTR_PATH_SERVICES, pszKernel, cszPARAMETERS); if (RegOpenKey (HKEY_LOCAL_MACHINE, szKey, &hk) == 0) { DWORD dwValue; DWORD dwType; DWORD cb; // nAxes: // cb = sizeof(dwValue); if (RegQueryValueEx (hk, cszJOY_DD_NAXES, 0, &dwType, (LPBYTE)&dwValue, &cb) == 0) { if ( (cb == sizeof(dwValue)) && (dwType == REG_DWORD) ) { if ((dwValue >= 2 && dwValue <= 4)) pkc->nAxes = dwValue; } } // dwPort: // cb = sizeof(dwValue); if (RegQueryValueEx (hk, cszJOY_DD_DEVICE_ADDRESS, 0, &dwType, (LPBYTE)&dwValue, &cb) == 0) { if ((cb == sizeof(dwValue)) && (dwType == REG_DWORD)) { int ii; for (ii = 0; ii < nValidPorts; ++ii) { if (adwValidPorts[ ii ] == dwValue) { pkc->dwPort = dwValue; break; } } } } // dwType: // cb = sizeof(dwValue); if (RegQueryValueEx (hk, cszJOY_DD_NAXES, 0, &dwType, (LPBYTE)&dwValue, &cb) == 0) { if ( (cb == sizeof(dwValue)) && (dwType == REG_DWORD) ) { pkc->dwType = dwValue; } } RegCloseKey (hk); } } void Joy_Config_WriteSettings (KernelConfig *pkc, LPTSTR pszKernel) { HKEY hk; TCHAR szKey[ MAX_PATH ]; wsprintf (szKey, TEXT("%s\\%s\\%s"), REGSTR_PATH_SERVICES, pszKernel, cszPARAMETERS); if (RegCreateKey (HKEY_LOCAL_MACHINE, szKey, &hk) == 0) { RegSetValueEx (hk, cszJOY_DD_NAXES, 0, REG_DWORD, (LPBYTE)&pkc->nAxes, sizeof(DWORD)); RegSetValueEx (hk, cszJOY_DD_DEVICE_ADDRESS, 0, REG_DWORD, (LPBYTE)&pkc->dwPort, sizeof(DWORD)); RegSetValueEx (hk, cszJOY_DD_DEVICE_TYPE, 0, REG_DWORD, (LPBYTE)&pkc->dwType, sizeof(DWORD)); RegCloseKey (hk); } } static TCHAR szOEMNAME[] = REGSTR_VAL_JOYOEMNAME; static TCHAR szOEMDATA[] = REGSTR_VAL_JOYOEMDATA; static struct { int idsKeyName; // "SideWinder_1", etc TCHAR *pszValueName; // "OEMData", etc BOOL fDataValue; // TRUE if should use u.data; FALSE if u.ids int idsData; // description for this entry BYTE aData[8]; // JOYREGHWSETTINGS for this entry } OEMRegistryList[] = { { IDS_KEY_SIDEWINDER, szOEMNAME, FALSE, IDS_NAME_SIDEWINDER }, { IDS_KEY_SIDEWINDER, szOEMDATA, TRUE, 0, { 0x0B,0,0x08,0,0x04,0,0,0 } } }; #define cOEMRegistryList (sizeof(OEMRegistryList)/sizeof(OEMRegistryList[0])) void EnsureOEMListInRegistry (void) { HKEY hk; if (RegCreateKey (HKEY_LOCAL_MACHINE, REGSTR_PATH_JOYOEM, &hk) == 0) { int ii; for (ii = 0; ii < cOEMRegistryList; ++ii) { TCHAR szText[ MAX_PATH ]; HKEY hkII; GetString (szText, OEMRegistryList[ii].idsKeyName); if (RegCreateKey (hk, szText, &hkII) == 0) { if (OEMRegistryList[ii].fDataValue) { RegSetValueEx (hkII, OEMRegistryList[ii].pszValueName, 0, REG_BINARY, (CONST LPBYTE)OEMRegistryList[ii].aData, sizeof(OEMRegistryList[ii].aData)); } else { GetString (szText, OEMRegistryList[ii].idsData); RegSetValueEx (hkII, OEMRegistryList[ii].pszValueName, 0, REG_SZ, (CONST LPBYTE)szText, sizeof(TCHAR)*( 1+lstrlen(szText) )); } RegCloseKey (hkII); } } RegCloseKey (hk); } } void Joy_RemoveService (LPTSTR pszKernel) { SC_HANDLE scManager; SC_HANDLE scDriver; if (scManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)) { if (scDriver = OpenService (scManager, pszKernel, SERVICE_ALL_ACCESS)) { SERVICE_STATUS ss; if (ControlService (scDriver, SERVICE_CONTROL_STOP, &ss)) { DeleteService (scDriver); } CloseServiceHandle (scDriver); } CloseServiceHandle (scManager); } } BOOL Joy_RestartService (LPTSTR pszKernel, BOOL fMustRestart) { SC_HANDLE scManager; SC_HANDLE scDriver; BOOL rc = TRUE; #define msecMaxWaitForLOCK 3000 // wait this long to lock the services DB #define msecMaxWaitForSTOP 3000 // wait this long for the service to stop // First step: determine if the driver has a service now. // If not, create one and start it up. // if ((scManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { rc = FALSE; } else { scDriver = OpenService (scManager, pszKernel, SERVICE_ALL_ACCESS); if (scDriver == NULL) { SC_LOCK scLock = NULL; DWORD tickStart = GetTickCount(); TCHAR szFullPath[ MAX_PATH ]; DWORD dwTagId; while (!(scLock = LockServiceDatabase (scManager))) { if (GetTickCount() > tickStart +msecMaxWaitForLOCK) { rc = FALSE; break; } Sleep(100); } if (rc) { wsprintf (szFullPath, TEXT("%%SystemRoot%%\\System32\\Drivers\\%s.SYS"), pszKernel); if ((scDriver = CreateService (scManager, pszKernel, NULL, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, szFullPath, TEXT("Extended Base"), &dwTagId, TEXT("\0"), NULL, NULL)) == NULL) { rc = FALSE; } UnlockServiceDatabase (scLock); } if (rc) { if (!StartService (scDriver, 0, NULL)) { rc = FALSE; } else if (!ChangeServiceConfig (scDriver, SERVICE_NO_CHANGE, SERVICE_SYSTEM_START, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { rc = FALSE; } CloseServiceHandle (scDriver); } } else // opened existing service successfully { SERVICE_STATUS ss; if (!ChangeServiceConfig (scDriver, SERVICE_NO_CHANGE, SERVICE_SYSTEM_START, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { rc = FALSE; } if (rc) { if (!QueryServiceStatus (scDriver, &ss)) { rc = FALSE; } if (rc && fMustRestart && ss.dwCurrentState == SERVICE_RUNNING) { if (!ControlService (scDriver, SERVICE_CONTROL_STOP, &ss)) { rc = FALSE; } else { DWORD tickStart = GetTickCount(); do { if (QueryServiceStatus (scDriver, &ss)) { if (ss.dwCurrentState != SERVICE_STOP_PENDING) break; } } while (GetTickCount() < tickStart + msecMaxWaitForSTOP); if (ss.dwCurrentState != SERVICE_STOPPED) { rc = FALSE; } } } if (rc && ss.dwCurrentState == SERVICE_STOPPED) { if (!StartService (scDriver, 0, NULL)) { rc = FALSE; } } } CloseServiceHandle (scDriver); } CloseServiceHandle (scManager); } return rc; } BOOL CALLBACK Joy_ConfigDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static LPTSTR pszKernel; static KernelConfig kcOrig; switch (msg) { case WM_INITDIALOG: { int ii; KernelConfig kcDialog; if ((pszKernel = (LPTSTR)lp) == NULL) { EndDialog (hDlg, DRV_CANCEL); return 0; } // Stick some default values into kcOrig, just // in case we can't find any settings in the registry now. // Joy_Config_DefaultSettings (&kcOrig, pszKernel); // Try to read the driver's current settings // Joy_Config_ReadSettings (&kcOrig, pszKernel); // Fill in the dialog with the current settings // kcDialog = kcOrig; #ifndef ALLOW_NON_SIDEWINDER EnableWindow (GetDlgItem (hDlg, ID_THREEAXES), FALSE); EnableWindow (GetDlgItem (hDlg, ID_FOURAXES), FALSE); if ( (kcDialog.nAxes == 3) || (kcDialog.nAxes == 4 && kcDialog.dwType != JOY_TYPE_SIDEWINDER) ) { kcDialog.nAxes = 4; kcDialog.dwType = JOY_TYPE_SIDEWINDER; } #endif #ifndef ALLOW_TWO_JOYSTICKS EnableWindow (GetDlgItem (hDlg, ID_TWOAXES), FALSE); if (kcDialog.nAxes == 2) { kcDialog.nAxes = 3; kcDialog.dwType = JOY_TYPE_UNKNOWN; } #endif if (kcDialog.nAxes == 2) SetCheckBox (hDlg, ID_TWOAXES, TRUE); else if (kcDialog.nAxes == 3) SetCheckBox (hDlg, ID_THREEAXES, TRUE); else if (kcDialog.dwType == JOY_TYPE_UNKNOWN) SetCheckBox (hDlg, ID_FOURAXES, TRUE); else SetCheckBox (hDlg, ID_SIDEWINDER, TRUE); for (ii = 0; ii < nValidPorts; ++ii) { TCHAR szPort[ 16 ]; wsprintf (szPort, TEXT("0x%04lX"), adwValidPorts[ ii ]); CB_AddString (GetDlgItem (hDlg, ID_PORT), szPort, adwValidPorts[ ii ]); } if (!CB_SelectByData (GetDlgItem (hDlg, ID_PORT), kcDialog.dwPort)) { CB_Select (GetDlgItem (hDlg, ID_PORT), 0); } break; } case WM_COMMAND: { switch (LOWORD(wp)) { case IDCANCEL: { EndDialog (hDlg, DRV_CANCEL); return 0; } case IDOK: { BOOL fRestartService = FALSE; BOOL fRestartComputer = FALSE; KernelConfig kcNew; kcNew.dwPort = CB_GetSelectedData (GetDlgItem (hDlg, ID_PORT)); kcNew.dwType = kcOrig.dwType; // (until we find otherwise) if (GetCheckBox (hDlg, ID_TWOAXES)) kcNew.nAxes = 2; else if (GetCheckBox (hDlg, ID_THREEAXES)) kcNew.nAxes = 3; else { kcNew.nAxes = 4; if (GetCheckBox (hDlg, ID_FOURAXES)) kcNew.dwType = JOY_TYPE_UNKNOWN; else if (kcNew.dwType == JOY_TYPE_UNKNOWN) kcNew.dwType = JOY_TYPE_SIDEWINDER; } if ( (kcNew.dwPort != kcOrig.dwPort) || (kcNew.nAxes != kcOrig.nAxes) || (kcNew.dwType != kcOrig.dwType) ) { fRestartService = TRUE; } Joy_Config_WriteSettings (&kcNew, pszKernel); EnsureOEMListInRegistry (); Joy_CloseAllHandles (); if (!Joy_RestartService (pszKernel, fRestartService)) fRestartComputer = TRUE; Joy_RequeryAll (TRUE); joyConfigChanged (0); if (fRestartComputer) EndDialog (hDlg, DRV_RESTART); else EndDialog (hDlg, DRV_OK); return 0; } } break; } } return 0; } UINT CB_AddString (HWND hItm, LPCTSTR psz, LPARAM lp) { UINT index; if ((index = (UINT)SendMessage (hItm, CB_ADDSTRING, 0, (LPARAM)psz)) != -1) { SendMessage (hItm, CB_SETITEMDATA, index, lp); } return index; } void CB_Select (HWND hItm, UINT index) { SendMessage (hItm, CB_SETCURSEL, index, 0); } BOOL CB_SelectByData (HWND hItm, LPARAM lpMatch) { UINT index; if ((index = CB_GetIndex (hItm, lpMatch)) == (UINT)-1) return FALSE; CB_Select (hItm, index); return TRUE; } UINT CB_GetSelected (HWND hItm) { return (UINT)SendMessage (hItm, CB_GETCURSEL, 0, 0); } LPARAM CB_GetSelectedData (HWND hItm) { UINT index; if ((index = CB_GetSelected (hItm)) == -1) return 0; return CB_GetData (hItm, index); } LPARAM CB_GetData (HWND hItm, UINT index) { return (LPARAM)SendMessage (hItm, CB_GETITEMDATA, index, 0); } UINT CB_GetIndex (HWND hItm, LPARAM lpMatch) { UINT idxMax = (UINT)SendMessage (hItm, CB_GETCOUNT, 0, 0); UINT index; for (index = 0; index < idxMax; index++) { if (SendMessage (hItm, CB_GETITEMDATA, index, 0) == lpMatch) return index; } return (UINT)-1; } /* * DEBUG ______________________________________________________________________ * */ #ifdef DEBUG void cdecl dprintf (LPWSTR szFormatW, ...) { WCHAR szW[ 512 ]; va_list arg; va_start (arg, szFormatW); wsprintfW (szW, L"SIDEWNDR: "); wvsprintfW (&szW[ lstrlen(szW) ], szFormatW, arg); lstrcatW (szW, L"\r\n"); OutputDebugStringW (szW); } #define szz L" " #define UserRange(_ch) \ g.user.jrvRanges.jpMin.dw##_ch, \ g.user.jrvRanges.jpMax.dw##_ch, \ g.user.jrvRanges.jpCenter.dw##_ch #define HardwareRange(_dwidz, _ch) \ g.ajd[ _dwidz ].config.hwv.jrvHardware.jpMin.dw##_ch, \ g.ajd[ _dwidz ].config.hwv.jrvHardware.jpMax.dw##_ch #define UserDead(_ch) \ g.user.jpDeadZone.dw##_ch void Joy_DumpUserValues (void) { dprintf (szz L"g.user values:"); dprintf (szz L" dwTimeOut=%lu", g.user.dwTimeOut); dprintf (szz L" X axis: %lu..%lu, C=%lu DZ=%lu%%", UserRange(X), UserDead(X)); dprintf (szz L" Y axis: %lu..%lu, C=%lu DZ=%lu%%", UserRange(Y), UserDead(Y)); dprintf (szz L" Z axis: %lu..%lu, C=%lu DZ=%lu%%", UserRange(Z), UserDead(Z)); dprintf (szz L" R axis: %lu..%lu, C=%lu DZ=%lu%%", UserRange(R), UserDead(R)); dprintf (szz L" U axis: %lu..%lu, C=%lu DZ=%lu%%", UserRange(U), UserDead(U)); dprintf (szz L" V axis: %lu..%lu, C=%lu DZ=%lu%%", UserRange(V), UserDead(V)); } void Joy_DumpHardwareValues (DWORD dwIDZ) { dprintf (szz L"hardware values for \\\\.\\joy%lu:", 1+dwIDZ); dprintf (szz L" hws.dwFlags=0x%08lX", g.ajd[dwIDZ].config.hws.dwFlags); dprintf (szz L" hws.dwNumButtons=%lu", g.ajd[dwIDZ].config.hws.dwNumButtons); dprintf (szz L" dwUsageSettings=0x%08lX",g.ajd[dwIDZ].config.dwUsageSettings); dprintf (szz L" X axis range: %lu..%lu", HardwareRange(dwIDZ, X)); dprintf (szz L" Y axis range: %lu..%lu", HardwareRange(dwIDZ, Y)); dprintf (szz L" Z axis range: %lu..%lu", HardwareRange(dwIDZ, Z)); dprintf (szz L" R axis range: %lu..%lu", HardwareRange(dwIDZ, R)); dprintf (szz L" U axis range: %lu..%lu", HardwareRange(dwIDZ, U)); dprintf (szz L" V axis range: %lu..%lu", HardwareRange(dwIDZ, V)); dprintf (szz L" POV_FORWARD: %lu", g.ajd[ dwIDZ ].config.hwv.dwPOVValues[ JOY_POVVAL_FORWARD ]); dprintf (szz L" POV_BACKWARD: %lu", g.ajd[ dwIDZ ].config.hwv.dwPOVValues[ JOY_POVVAL_BACKWARD ]); dprintf (szz L" POV_LEFT: %lu", g.ajd[ dwIDZ ].config.hwv.dwPOVValues[ JOY_POVVAL_LEFT ]); dprintf (szz L" POV_RIGHT: %lu", g.ajd[ dwIDZ ].config.hwv.dwPOVValues[ JOY_POVVAL_RIGHT ]); dprintf (szz L" hwv.dwCalFlags=0x%08lX", g.ajd[dwIDZ].config.hwv.dwCalFlags); dprintf (szz L" dwType=%lu", g.ajd[dwIDZ].config.dwType); dprintf (szz L" dwReserved=%lu", g.ajd[dwIDZ].config.dwReserved); } #endif