Microsoft Corporation
June 24, 1996 (updated August 15, 1996)
Microsoft® DirectInput™, the joystick driver subsystem for Windows® 95, is the much-needed new standard for game developers working with input devices such as joysticks and game pads. The new digital joystick devices, such Microsoft SideWinder™ 3D Pro, support multiple devices on a single port and provide discrete button and position information. DirectInput provides support for multiple joysticks, and it supports the latest in gaming-device technology, without the developer having to know the intricacies of the device.
This article describes how to extend the joystick services and how to fully utilize DirectInput to take advantage of the latest technology in gaming devices. Use this document in combination with the joystick documentation, which can be found on the MSDN Library as a subset of the Platform Software Development Kit (SDK).
When you start Windows 95, joystick services are loaded. The services are used to monitor up to sixteen joysticks. Joystick functions are used to determine the joysticks' capabilities. You can process a joystick's position and button information by querying the joystick directly or by capturing the joystick and processing messages from it.
Each gaming device (joystick, game pad, flight yoke, and so on) has several capabilities that are available to your application. You can retrieve the capabilities of a device using the joyGetDevCaps function to fill a JOYCAPS structure with joystick capabilities. Such capabilities include:
The following JOYCAPS structure contains information about the joystick capabilities:
typedef struct
{
WORD wMid; // manufacturer identifier
WORD wPid; // product identifier
CHAR szPname[MAXPNAMELEN]; // see below
UINT wXmin; // min. x coordinate
UINT wXmax; // max. x coordinate
UINT wYmin; // min. y coordinate
UINT wYmax; // max. y coordinate
UINT wZmin; // min. z coordinate
UINT wZmax; // max. z coordinate
UINT wNumButtons; // number of joystick buttons
UINT wPeriodMin; // see below
UINT wPeriodMax; // see below
// The following members are not in previous versions of Windows.
UINT wRmin; // see below
UINT wRmax; // see below
UINT wUmin; // see below
UINT wUmax; // see below
UINT wVmin; // see below
UINT wVmax; // see below
UINT wCaps; // see below
UINT wMaxAxes; // see below
UINT wNumAxes; // see below
UINT wMaxButtons; // see below
CHAR szRegKey[MAXPNAMELEN]; // see below
CHAR szOEMVxD[MAXOEMVXD]; // see below
} JOYCAPS;
JOYCAPS Structure
Element | Descriptions |
zOEMVxD | Null-terminated string identifying the OEM joystick minidriver. |
SzPname | Null-terminated string containing the joystick product name (in the case of Vjoyd.vxd, this field is always "Microsoft PC-joystick driver"). |
SzRegKey | Null-terminated string containing the registry key for the joystick. |
Wcaps | Joystick capabilities (see flags in the following table, which defines individual joystick capabilities). |
Flags Defining Joystick Capabilities
Flag | Joystick Capability |
JOYCAPS_HASZ | Z-coordinate information. |
JOYCAPS_HASR | Rudder (fourth-axis) information. |
JOYCAPS_HASU | U-coordinate (fifth-axis) information. |
JOYCAPS_HASV | V-coordinate (sixth-axis) information. |
JOYCAPS_HASPOV | Point-of-view information. |
JOYCAPS_POV4DIR | Point of view supports discrete values (centered, forward, backward, left, and right). |
JOYCAPS_POVCTS | Point of view supports continuous degree bearings. |
wMaxAxes | Maximum number of axes supported. |
wMaxButtons | Maximum number of buttons supported. |
wNumAxes | Number of axes currently in use. |
wPeriodMax | Largest polling frequency supported when captured by the joySetCapture function (in milliseconds). |
wPeriodMin | Smallest polling frequency supported when captured by the joySetCapture function (in milliseconds). |
wRmax and wRmin | Maximum and minimum rudder values (rudder is the fourth axis of movement). |
wUmax and wUmin | Maximum and minimum u-coordinate (fifth-axis) values. |
wVmax and wVmin | Maximum and minimum v-coordinate (sixth-axis) values. |
The following code fragment demonstrates how to determine if a device supports a rudder.
/************************************************************
** BOOL joyHasRudder(UINT joyid)
**
** Description: Determine if a given joystick has a rudder.
**
** Parameters: UINT joyid - Joystick id.
**
** Returns: TRUE - YES the joystick has a rudder.
** FALSE - NO the joystick has no rudder.
*****************************************************************/
BOOL joyHasRudder(UINT joyid)
{
JOYCAPS jc;
if(joyGetDevCaps(joyid,(LPJOYCAPS)&jc,sizeof(JOYCAPS)==JOYERR_NOERROR)
{
if(jc.wCaps & JOYCAPS_HASR)
return TRUE;
}
return FALSE;
}
DirectInput stores all device-specific information in the registry. Some of this information, including the OEM product's name, is not available through the multimedia-joystick API calls. The OEM product name, as well as other device-specific information, is stored in the registry. To retrieve joystick information, use the registry information of three primary keys: CurrentJoystickSettings, OEM Joysticks, and JoystickSettings.
The CurrentJoystickSettings key contains configuration information about all joysticks installed (or configured). The installed joysticks may not be connected properly, but this key reflects what the driver system is configured for. The following illustration describes this key's registry hierarchy:
HKEY_LOCAL_MACHINE
System
CurrentControlSet
Control
MediaResources
Joystick
(JOYCAPS.szRegKey) (Note: obtained by joyGetDevCaps.)
CurrentJoystickSettings
If an OEM joystick is present, the JoysticknOEMName field—the registry key of the OEM joystick—will be present. The n in JoysticknOEMName represents the joystick ID. For example:
Joystick1OEMName = "SideWinder 3D Pro"
specifies that the registry key for Joystick ID1 is assigned to SideWinder 3D Pro. For details, see the next section.
The OEM Joysticks key is the parent to all available OEM joysticks, whether or not the joysticks are attached. The following illustration describes this key's registry hierarchy.
HKEY_LOCAL_MACHINE
System
CurrentControlSet
Control
MediaProperties
PrivateProperties
Joystick
OEM
The OEM Joysticks key contains one or more child keys that can be enumerated to determine which OEM joysticks may be available. Each child key has at least two fields:
Child Key Fields
Child Key Field | Description |
OEMName | OEM product name |
OEMData | Hardware-specific settings and the number of buttons |
The JoystickSettings key contains configuration information about the last state of the joystick. It is used primarily by the Control Panel when a joystick is removed and then reattached to the system. You can use this key to reestablish information (primarily regarding calibration) about the state of the joystick when it was last properly connected. The following illustration describes this key's registry hierarchy:
HKEY_LOCAL_MACHINE
System
CurrentControlSet
Control
MediaResources
Joystick
(JOYCAPS.szRegKey) (Note: obtained by joyGetDevCaps.)
JoystickSettings
The following fragment demonstrates how to obtain the OEM product name of a specific joystick (by ID):
#include <regstr.h>
. . .
/**********************************************************
**
**LRESULT joyGetOEMProductName(UINT id, PSTR szName)
**
** Description: Retrieve the OEM product name for a
** given joystick ID.
**
** Parameters: UINT id - Joystick ID number.
** PSTR szName - Joystick OEM product name.
**
** Returns: ERROR_SUCCESS = OK , szName will be
** filled with joystick OEM product name.
** else an error code from registry call.
**
**************************************************************/
#define ERROR_NOTOEM -1
MMRESULT joyGetOEMProductName(UINT id, TCHAR * pszName)
{
JOYCAPS JoyCaps;
TCHAR szKey[256];
TCHAR szValue[256];
UCHAR szOEMKey[256];
HKEY hKey;
DWORD dwcb;
LONG lr;
// Note: JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based.
id++;
if (id > joyGetNumDevs() ) return JOYERR_NOCANDO;
// Open .. MediaResources\CurentJoystickSettings.
joyGetDevCaps((id-1), &JoyCaps, sizeof(JoyCaps));
sprintf(szKey,
"%s\\%s\\%s",
REGSTR_PATH_JOYCONFIG,
JoyCaps.szRegKey,
REGSTR_KEY_JOYCURR);
lr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPTSTR) &szKey, 0, KEY_ALL_ACCESS, &hKey);
if (lr != ERROR_SUCCESS) return JOYERR_NOCANDO;
// Get OEM Key name.
dwcb = sizeof(szOEMKey);
sprintf(szValue, "Joystick%d%s", id, REGSTR_VAL_JOYOEMNAME);
lr = RegQueryValueEx(hKey, szValue, 0, 0, (LPBYTE) &szOEMKey, (LPDWORD) &dwcb);
RegCloseKey(hKey);
if (lr != ERROR_SUCCESS)
{
*pszName = 0;
return JOYERR_NOCANDO;
}
// Open OEM Key from ...MediaProperties.
sprintf(szKey, "%s\\%s", REGSTR_PATH_JOYOEM, szOEMKey);
lr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_ALL_ACCESS, &hKey);
if (lr != ERROR_SUCCESS) return JOYERR_NOCANDO;
// Get OEM Name.
dwcb = sizeof(szValue);
lr = RegQueryValueEx( hKey,
REGSTR_VAL_JOYOEMNAME,
0, 0,
(LPBYTE) pszName,
(LPDWORD) &dwcb);
RegCloseKey(hKey);
if (lr != ERROR_SUCCESS)
return JOYERR_NOCANDO;
else
return JOYERR_NOERROR;
}
The Windows 95 joystick services support up to 16 different devices connected to the system at once, although it's unlikely that so many devices would be connected at a time.
To determine the number of gaming devices connected, walk through all 16 JOYSTICK IDS and poll each device with the joyGetPosEx service. The joyGetPosEx service returns the JOYERR_NOERROR message if the joystick is working properly. The following demonstrates how to obtain the number of devices connected.
Note that the joyGetNumDevs service returns the number of possible joysticks the system supports (currently 16); it does not return the number of connected joysticks.
#include <windows.h>
#include <mmsystem.h>
. . .
/****************************************************************
**
** UINT joyGetNumDevicesConnected(void)
**
** Description: Determine the number of joysticks connected.
**
** Parameters: None.
**
** Returns: UINT - Number of devices connected.
**
*****************************************************************/
UINT joyGetNumDevicesConnected(void)
{
UINT JoyID;
UINT nDevicesConnected=0;
JOYINFOEX ji;
ji.dwSize = sizeof(JOYINFOEX);
for (JoyID=JOYSTICKID1; JoyID<joyGetNumDevs(); JoyID++)
{
if (joyGetPosEx(JoyID, &ji)==JOYERR_NOERROR)
{
nDevicesConnected++;
}
}
return nDevicesConnected;
}
When the user or Setup changes the joystick settings, a notification message is posted to all top-level windows, including disabled or invisible windows. This notification can be used to do one of the following:
To be notified of changes in joystick settings, the application must first retrieve the message ID of the notification event posted by the DirectInput driver subsystem. To retrieve the message ID, use the RegisterWindowMessage Windows API call, which passes "MSJSTICK_VJOYD_MSGSTR" as its argument:
uMsgId = RegisterWindowMessage("MSJSTICK_VJOYD_MSGSTR");
With the message ID retrieved, you can use the application's windows message queue to check for the occurrence of this notification, as shown in the following example.
#include <windows.h>
#include <mmsystem.h>
. . .
UINT uMsgId;
int WinMain(…)
{
// Retrieve message ID that joystick driver subsystem posts.
uMsgId = RegisterWindowMessage("MSJSTICK_VJOYD_MSGSTR");
// Init Instance, Register Classes, Create Window etc..
. . .
// Acquire messages from queue and dispatch messages until a WM_QUIT
// message is received.
while (GetMessage(&msg,NULL, NULL, NULL))
{
TranslateMessage(&msg); // Translates virtual key codes.
DispatchMessage(&msg); // Dispatches message to window.
}
return (msg.wParam); // Returns the value from PostQuitMessage.
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Did the joystick configuration change?
if(msg == uMsgId)
{
// Yes.
// Here we can inform the user of the changes in joystick
// configuration and/or update our joystick device context.
. . .
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
// Perform normal message processing. . . .
switch (msg)
{
. . .
}
return (NULL);
}
All of the Windows 95 joystick services except for joyGetNumDevs require a joystick ID. The DirectInput driver subsystem (Msjstick.drv and Vjoyd.vxd) uses the joystick ID to coordinate which device should be called to retrieve the information requested. Up to 16 joysticks are supported.
Note The multimedia reference mentions only JOYSTICKID1 and JOYSTICKID2 as valid joystick IDs, and the Mmsystem.h file defines only these two IDs. The multimedia reference and Mmsystem.h file together imply that only two joysticks can be connected at a time. However, newer gaming devices, digital technology, and the joystick driver model of Windows 95 (which supports a minidriver model) all make it possible for these devices to "daisy chain" themselves to the port and appear as multiple devices to the joystick subsystem. Therefore, the game developer can take advantage of full device support for two or more gaming devices.
The following code fragment demonstrates how to maintain multiple device contexts and to detect what device is attached and what its capabilities are.
#include <windows.h>
#include <mmsystem.h>
. . .
typedef struct _CONTEXTINFO
{
UINT id; // Keep the joystick ID here.
char szOEMProductName[128]; // OEM product name.
JOYCAPS JoyCaps; // Joystick capabilities.
} CONTEXTINFO;
typedef struct _JOYCONTEXT
{
UINT nDevices; // Number of joysticks connected (number of CONTEXTINFO)
CONTEXTINFO ContextInfo[16]; // Up to 16 joystick device contexts.
} JOYCONTEXT, *PJOYCONTEXT;
/****************************************************************
** void joyGetContext(PJOYCONTEXT pjc)
**
** Description : Get the current joystick devices' context.
**
** Parameters : PJOYCONTEXT pjc - pointer to JOYCONTEXT structure
** containing the current state of the
** connected joystick devices (see above
** declaration).
**
** Returns : None.
**
*****************************************************************/
void joyGetContext(PJOYCONTEXT pjc)
{
UINT JoyID;
UINT i=0;
JOYINFOEX ji;
ji.dwSize = sizeof(JOYINFOEX);
for (JoyID=JOYSTICKID1; JoyID<joyGetNumDevs(); JoyID++)
{
if (joyGetPosEx(JoyID, &ji)==JOYERR_NOERROR)
{
pjc->ContextInfo[i].id = JoyID;
joyGetOEMProductName(JoyID, pjc->ContextInfo[i].szOEMProductName);
joyGetDevCaps(JoyID, &pjc->ContextInfo[i].JoyCaps,sizeof(JOYCAPS));
i++;
}
}
pjc->nDevices= i;
}