IOCTL-based Win32 joystick interface

Introduction

This interface allows PPJoy to represent a virtual joystick interface. A Win32 application provides joystick input to PPJoy via IOCTL calls. PPJoy will feed this input into DirectInput as a joystick; Win32 applications will think the input comes from a real joystick.

Below we present the steps required to open the virtual joystick control device, send input to it and finally close the control device.

Opening a PPJoy virtual joystick

PPJoy creates a control device for each virtual joystick defined. This device is named \\.\PPJoyIOCTL1 for the first virtual joystick through to \\.\PPJoyIOCTL16 for the last one. Before you can send IOCTL commands to the joystick device you need a handle to it:

HANDLE h;
h= CreateFile("\\\\.\\PPJoyIOCTL1",GENERIC_WRITE,FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);

Sending data to virtual joystick

Joystick data is placed in a special structure and then sent to PPJoy using the DeviceIoControl() function. The structure consists is defined as follows:

#pragma pack(push,1)		/* All fields in structure must be byte aligned. */
typedef struct
{
 unsigned long  Signature;             /* Signature to identify packet to PPJoy IOCTL */
 char           NumAnalog;             /* Num of analog values we pass */
 long           Analog[NUM_ANALOG];    /* Analog input values */
 char           NumDigital;            /* Number of digital values we pass */
 char           Digital[NUM_DIGITAL];  /* Digital input values */
} JOYSTICK_STATE;
#pragma pack(pop)

The Signature field should be set to the constant JOYSTICK_STATE_V1 (defined in ppjioctl.h). NumAnalog and NumDigital should be set to the size of axis and button arrays respectively. PPJoy currently supports up to 63 analog and 128 digital values. Extra values in the arrays will be ignored. By default the first 8 analog values will be reported as axes and the first 16 digital values as buttons. Undefined analog values will be centred; undefined digital values will be off/not pressed.

NOTE: The fields in the structure must be byte aligned with no padding between the fields.

PPJOY_AXIS_MIN and PPJOY_AXIS_MAX represents the minimum and maximum values for an analog value. For digital values 1 means pressed and 0 is not pressed.

The sequence of axes are as follows:

PPJoy does not current support changing the number of axes or buttons reported; however extra axes or button can be mapped to "nothing".

A memory dump of the structure with two analog and four digital values looks like this:

 43 41 54 53       Header
 02                Two analog values to follow
 xx xx xx xx       Analog value 1 (defaults to X Axis)
 xx xx xx xx       Analog value 2 (defaults to Y Axis)
 04                Four digital values to follow
 xx                Digital value 1 (defaults to Button 1)
 xx                Digital value 2 (defaults to Button 2)
 xx                Digital value 3 (defaults to Button 3)
 xx                Digital value 4 (defaults to Button 4)
After setting the axis and button values in the structure it is passed to PPJoy using DeviceIoControl() with an IOCTL code of IOCTL_PPORTJOY_SET_STATE (defined in ppjioctl.h). This code snippet illustrates:
 JOYSTICK_STATE   JoyState;
 DWORD            RetSize;
 DWORD            rc;

 /* Initialise JoyState structure here... */

 if (!DeviceIoControl(h,IOCTL_PPORTJOY_SET_STATE,&JoyState,sizeof(JoyState),NULL,0,&RetSize,NULL))
 {
  rc= GetLastError();
  /* ... */
 }
It is important to check the return code for the DeviceIOControl() funcion. If the control device is still open when the underlying joystick device is deleted the PPortJoy.sys driver cannot unload. In this case GetLastError() will return 2. When you get this error code close the handle to the control device and retry opening the control device.

Closing the virtual joystick

When the Win32 application is no longer using the control device, the control device handle should be closed with the CloseHandle() function. Also see the note above about closing the control device.

Sample application

This is a very simple sample application to demonstrate a Win32 application providing joystick input. It also contains the ppjioctl.h file needed to build other user mode joystick drivers.

Get IOCTLSample sample application (Microsoft Visual C++ 6.0).