Extending DirectInput's Joystick Subsystem Services

Microsoft Corporation
June 24, 1996 (updated August 15, 1996)

Introduction

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).

Determining What Joystick Device Capabilities Exist

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;
}

Determining the OEM Joystick's Product Name

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.

CurrentJoystickSettings Key

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.

OEM Joysticks Key

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

JoystickSettings Key

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;
}

Determining the Number of Gaming Devices Connected

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;
}

Detecting When Joystick Settings Are Changed

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);
}

Supporting Multiple Joystick Devices

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;
}