Joystick Conference Summary

Prepared by Zachary Booth Simpson
Origin Systems
8 Jun 1995

(c)2002 ZBS. http://www.mine-control.com/zack
Please sign my guestbo0k if you find this work useful.

Overview

This is the summary of the Joystick Conference held at Origin Systems on June 8th and 9th. I try to summarize the topics discussed and propose a plan of action. It became clear at the meeting that the basic plan would be to modify the proposed GameSDK specification to solve a few basic problems. I have modified a few points that were resolved in the meeting after consultation with the GameSDK engineers.

For the purposes of clarity for those not at the meeting, I first cover the basic concepts that were established during the first day of the meeting.

Following the overview, I discuss the proposed changes to the GameSDK and demonstrate sample code. The specific data structure and function names of the implementation are not proposed as final, but are expected to be used a guidelines for the changes. After discussion with Microsoft, it has become clear that it is too late to change the existing GameSDK joystick specification as it has already been included as part of the Win Ď95 final shipment. Thus, these extensions are proposed as additional enhancements for version 2.0. The specifics of how version 2.0 will interact with version 1.0 will have to be left to Microsoft.

Finally, I summarize various agreed upon data such as the family and parametric data. Also, I present a prototype Control Panel Applet.

Conference Attendees

Name

Company

Title

e-mail

Zachary Simpson (host)

Origin Systems / Electronic Arts

Director of Technology

zsimpson@origin.ea.com

John Landry

Compaq

Senior Member/

Technical Staff

landry@bangate.compaq.com

Steve McGowan

Forte

Director of Engineering

SBM@fortech.com

Joe Rayhawk

Thrust Master

Project Engineer

joe@thrustmaster.com

Rick Salvador

CH Products

Technical Representative

chproducts@aol.com

Barbara Gutierrez

Logitech

OEM Account Development Manager

barbara_gutierrez@ logitech.com

Ken Hall

Colorado Spectrum

President

73233.467@compuserve.com

Beth Marcus

Exos

Chief Technical Officer

exos@exos.com

Kevin English

Tanisys

Software Design Engineer

kevine@tanisys.com

Jennifer Nyland

Tanisys

Senior Design Engineer

jennifer@tanisys.com

Alex St John

Microsoft

Games Evangelist

alexjo@microsoft.com

Reno Suffi

Suncom Technologies

Electrical Engineer

reno@xnet.com

Bernard Geaghan

MicroTouch

Vice President, Research and Development

bgeaghan@mts.mhs. compuserve.com

Louie McCrady

Dynamix / Sierra

Software Engineer

louiem@dynmx.com

Rick Overman

Dynamix / Sierra

Software Engineer

ricko@dynmx.com

Mark Klonower

Crystal Semiconductor Corporation

Marketing

mklonow@crystal.cirrus. com

Paul DiLascia

Microsoft Systems Journal

Contributing Editor

72400,2702@compuserve.com

John Smith

Advanced Gravis

UltraSound Project Manager

john.smith@gravis.com

Ron Haidenger

Advanced Gravis

Director Product Managemant

75300,773@compuserve.com

Tim OíNeal

Sempro

Product Development

Director

 

Alan Kleymeyer

Mind Path Technologies

 

alankley@aol.com

Charles Ader

Micro Speed

Senior Member Technical Staff

cader@netcom.com

David Petchey

Mindscape

Staff Scientist

dpetchey@mindscape. com

Michael Rakieten

Dell

Product Marketing Manager Multimedia

& I/O Devices

michael_rakieten@ccmail.us.dell.com

Vernon Weiss

Dell

Dimension Product Marketing Manager

71333,3400@compuserve.com

Gray Chenault

Dell

Multimedia Engineer

gary_chenault@ccmail.us.dell.com

Joseph Pinzarrone

First Person Gaming

Vice President of Engineering

joseph@fpgaming.com

 

Basic Concepts

Channels

All input devices can be thought of as consisting of a certain number of discreet channels of information. A channel is one atomic degree of freedom on the device. For example, a button is a channel, a horizontal axis is a channel, etc.

It is sometimes desirable from the point of view of an application to view a logical channel as a conglomerate or disassembly of the physical channels on a device. For example, the top hats on many flight sticks are implemented as a set of 4 discreet switches while on other flight sticks the top hat is implemented as two full axes (a sort of joystick on a joystick). From the point of view of the application, these implementations are the same; it is just that the discreet version has a much more limited resolution. Thus, a top hat would have two logical channels Ė a horizontal and vertical. The fact that the physical device is implemented as four separate switches or as a single voltage divider is irrelevant.

Mapping

As just discussed, joysticks can be thought of as having many independent channels. Unfortunately, as new joysticks are created there is no intuitive standard which specifies which physical channels should be mapped to which logical channels. There is, however, one exception Ė "generic" joysticks. The "generic" sticks conform to the old 1980 IBM standard of 2 axes and two buttons. However, even for generic joysticks there still exists a problem in that it is unclear which button is primary and which is secondary. The unspoken standard for "generic" joysticks is that button 0 is primary and button 1 is secondary. The point is, as more buttons and axis are created, the problem of mapping will become increasingly difficult.

The above diagram demonstrates the basic layers of mapping. The physical map is the data that is pulled off of the device. With the exception of generic joysticks, this mapping will be completely arbitrary and specific to the device.

The logical map is the channel structure as revealed by the driver to the application. The order of this map is not arbitrary since the application must be able to determine the usage of these channels.

The final layer is the actual application map which is directly associated with the behaviors of the application.

One of the keys to a successful joystick driver model will be to designate how the logical map will be revealed to the application. There are three ways that the logical map is exposed: vendor mapping, family mapping, and parametric mapping. These are not mutually exclusive.

Vendor Mapping

One way for the application to determine the logical-to-application mapping is by storing a unique map for each kind of joystick. The obvious disadvantage of this approach is that it is not guaranteed that the user will have one of the supported devices. On the other hand, this type of mapping does have the benefit of ensuring that for at least some very popular joystick models, all of the proprietary features can be utilized.

Family Mapping

Another approach to logical mapping would be to create standards for common varieties of devices. For example, these families might fall into categories like: "flight controller", "steering wheel", etc. Families should define:

A device can support more than one family. For example, a "flight stick" could certainly be used as a "generic" family also. Each of the supported families of the joystick is a mode, and a joystick can not be in more than one mode at a time per application.

Parametric Mapping

Yet another way to determine the logical mapping is by defining a set of parameters which describe the channels. These descriptions would take on two basic forms: physical parameters and usage parameters.

Physical parameters would describe things such as: "two state button", "horizontal axis", "spring loaded", "on the floor", "hand held", etc. Usage parameters would describe suggested usage. For example, "vertical POV", "rudders", "brakes", "chaff", etc.

Using these parameters, an application can make an attempt to intelligently determine the logical-to-application mapping. Using all three of the approaches just mentioned (family, vendor, and parametric) will enable applications to be very user-friendly about the usage of known and unknown devices.

Device Input

Because game developers have been "talking" to the joystick ports directly for the last 15 years, there has been no ability to undo the constraints of the original IBM joystick port. (Some very clever hacks have been devised however). Clearly, the advantage of creating a new driver model is to empower digital joysticks by which I mean any joystick which is not bound to the clunky analog-polled input model of old. There already exists a demand for such devices, for example, the serial donggles used for laptops. In addition, USB and other new bus standards are emerging which would seem to be well suited to joysticks.

Several optimizations can be made independent of how joysticks evolve over the next few years to a defined API can enable. Two in particular are worthy of inspection: simultaneous device polling and channel deactivation.

Channel Deactivation

Because it takes time to poll and transfer data from a device (especially with analog polled devices), it would be nice if the application could deactivate unused channels. This would enable "smart devices" to speed up polling as well as transfer times and also decrease power consumption.

Simultaneous Polling

It is often the case that an application wants to poll more than one device at the same time. Thus, it would be nice if the driver could minimize the number of transitions to ring zero. This could be implemented in several ways, but the basic idea is that some kind of data structure internal to the driver is maintained that instructs the VxD to poll more than one device during a call to poll the device.

Proposed Changes to the GameSDK v 1.0

The first draft of the GameSDK Joystick interface defines an extension to the original Windows joystick model. Because very few developers actually ever used this old joystick model due to its inefficiency, there is little incentive to remain backward (or near-backward) compatible. Instead, we should insure that we design a system which will have an extended lifetime.

The old Windows driver model was based on the limitations of the original IBM joystick port. It defines two usable axes and two buttons per stick. The new GameSDK is quite in improvement in that it now defines six axis and many more buttons. However, the mapping problem as described above is still not addressed.

The currently proposed GameSDK specification will be shipping with Win Ď95; this preclude dramatic modification due to the shipping timeline of Win Ď95. However, we can build a more general purpose model and slot it in as a pseudo-replacement for the GameSDK model. The exact way in which this will implemented is left up to Microsoft.

I propose to make several key changes to the GameSDK:

During the conference, much debate centered around whether there should exist a transport independent layer in the driver model. I for one, was swayed both directions during the discussion. We concluded at the time that a transport independent layer should be built into the DDI model. After consideration with Microsoft, I recommend that we do not include this layer. I believe that the complications that will be created by this layer outweigh the benefits.

Homogenious Channels

I propose that we move away from specialized data structures that try to encapsulate the functionality of axis and buttons, and replace it with the concept of a homogeneous channel buffer. In other words, we create all channels equal; buttons and axis will not be differentiated. This fundamental change will increase the flexibility of the system and also make application coding easier by allowing developers to use highly efficient and convenient lookup tables to perform the logical-to-application mapping.

Thus, today we have the GameSDK proposed JoyGetPosEx(JOYINFOEX *) function which takes the following structure:

struct JOYINFOEX {
DWORD dwSize;
DWORD dwFlags;
DWORD dwXPos;
DWORD dwYPos;
DWORD dwZPos;
DWORD dwRPos;
DWORD dwUPos;
DWORD dwVPos;
DWORD dwButtons;
DWORD dwButtonNumber;
DWORD dwPOV;
DWORD dwReserved1;
DWORD dwReserved2;
};

We would replace this structure with simply and array of:

DWORD channelBuffer [numberOfChannels + 1]

The +1 is reserved for a DWORD dwSize at the beginning of the structure that is required to determine the length.

Logical Mapping Modes

As soon as we eliminate the structure listed above and replace it with a generic channel buffer, we create the need for a well defined logical mapping. As described above, there are three basic ways to resolve the logical map: vendor, family, and parametric. I suggest that we incorporate all of this data into this system to allow the application designer maximum flexibility. I believe that this can be done in a surprisingly simple data structure as demonstrated below.

Like the channel buffer described above, the existing GameSDK capability data is compacted into one structure called JOYCAPS. I reorganize this data into more than one structure to allow for the enumeration of the differing modes and channels.

It is important to remember that a device will support more than mode. In fact, it may support many many modes. Modes are cheap from the driver point of view because all they require is data that tells the driver how to convert its physical bits into logical channels. Because similar information is utilized for every mode in a given device, this physical to logical mapping can be directed though a lookup table which is initialized from data in the registry. This lookup table approach avoids large amounts of unused code in the driver space an will keep overhead to a minimum.

Every device will probably want to support some of the following kinds of modes:

Family Modes

A joystick manufacturer will choose to support a family if they believe that their device naturally supports such a family. There will be considerable over-lap among families. For example, a joystick that is built as a "flight stick" will clearly work as a "generic" also. In fact, almost every device will support "generic".

Native Modes

Any manufacturer with new features such as more axes or buttons will certainly want to create a native mode where every button and every axis is included in the logical map. These modes will be utilized by the application engineers in one of two ways; they will either recognize the manufacturer and product ID and use an pre-determined mapping (if the joystick companyís marketing is doing a good job!), or they will evaluate the channel parameters one by one and create an application mapping parametricly.

Compatibilty Modes

The globally unique identifiers used to recognize a particular device are not copyrightable. Thus, it is certainly fair game to write a driver which emulates the native mapping of another device. In this case, the driver should not "lie" about its real manufacturer in the manufacturer ID fields revealed in the capability structures. Instead, the device capability should report the "truth" about the manufacturer and the mode IDs should tell the "lie". This way, customer service representatives from all sides will be able to determine the true configuration of the machine.

Optimizations

The two optimizations described above in the Basic Concepts, will be implemented through two special calls. The simultaneously polling is accomplished by associating more than one device and channel buffer with a primary device which is then used to poll the chain of devices. Channel deactivation is done by allowing the application to set the channel state for each channel after the logical-to-application map has been derived and the usage is known. Note, it is assumed that for all family modes the driver will disable unused physical channels automatically.

Prototype Data Structures

CHANNELBUFFER is a replacement for JOYINFOEX.

typedef struct tagCHANNELBUFFER {

DWORD size;

DWORD channelData [1];

// ... actually 1 .. n channel DWORDs here ...

} CHANNELBUFFER;

JOYCAPS is a subset of the JOYCAPS structure defined in the GameSDK ver 1.0. The primary difference is that the channel information has been moved out. See JOYCHANNELCAPS below.

typedef struct tagJOYCAPS {

DWORD dwSize;

WORD wIsPrimary;

GUID guidManufactuerID;

GUID guidProductID;

CHAR szManufactersName [MAXNAMELEN];

CHAR szProductName [MAXNAMELEN];

CHAR szRegistrationKey [MAXNAMELEN];

CHAR szOEMVxD [MAXOEMVXDLEN];

} JOYCAPS;

JOYMODECAPS is an extension of the GameSDKs JOYCAPS function. The reason for the distinction is to facilitate the logical mapping as described above. This structure is filled out by the driver for each mode that is supported during the call to JoyEnumModes.

typedef struct tagJOYMODECAPS {

DWORD dwSize;

WORD wFlags;

GUID guidManufactuerID;

GUID guidProductID;

CHAR szManufactersName [MAXNAMELEN];

CHAR szProductName [MAXNAMELEN];

GUID guidFamily;

WORD wNumChannels;

DWORD reservedForDriverModeIDStorge; // This gets set as a mode id by JoyEnumModes

DWORD reserved;

} JOYMODECAPS;

Note that this structure has a few duplicated fields compared to JOYCAPS. The important distinction is that these fields are used to report supported logical modes, not the actual device. Thus, a joystick manufactured by one company can emulate the logical mapping provided by another company. For example, suppose WackyCorp. decides to emulate BigCorpís joystick. They would fill in the JOYCAPS structure as usual, but they would enumerate an extra mode for this compatibility. In this enumeration, they would fill the structure as follows:

joymodecaps.szManufactuerName = "Wacky Corp";

joymodecaps.szProductName = "BigCorp(tm) UltraPro(tm) compatibility mode.";

JOYCHANNELCAPS is also a subset of JOYCAPS in the GameSDK. This structure is now enumerated on a per channel basis instead of being consolidated into one structure. This allows for homogeneous channels as described earlier.

typedef struct tagJOYCHANNELCAPS {

DWORD dwSize;

DWORD flags;

// bit 0: 1 = active / 0 = disabled

// bit 1-2: 0 = read only, 1 = write only, 2 = read / write

DWORD dwMin;

DWORD dwMax;

DWORD dwCenter;

DWORD dwResolution;

// Resolution is the number of discrete values that can be represented

CHAR szRegisterKey [MAXNAMELEN];

// Contains the key name of the parameteric info.

};

Prototype API

I do not know exactly how these functions will fit into the current GameSDK specification. I suspect that they will be added above or in parallel to the existing specification. Thus, all of these function names are purely descriptive; they are not intended to conflict with or replace any existing functions. Also, no error detection or other house cleaning elements like close are described here. Treat this as purely a descriptive prototype.

int JoyEnum (JOYCAPS *joyCaps, int done)

During each call this function fills out the capability structure for the next device. The done flag is initialized to zero, and when it has completed enumerating all of the devices, it returns zero. This allows for very simple coding as demonstrated in initJoysticks in the sample code below.

In some cases, joystick manufacturers may choose to integrate more than logical device into the same package or connector. For example, a flight stick might also include a throttle. The family designations a designed to be simple, so there exists a flight stick family and a throttle family. Thus, the device driver for this integrated device would report itself as two different devices in the JoyEnum function despite the fact that it is really one device.

HANDLE JoyOpenDevice (int joystickNumber, JOYCAPS *joyCaps)

This opens the device specified by the ordinal of the device from the JoyEnum function or the application can pass in PREFERRED_JOYSTICK to allow the driver to choose (this is usually based on the control panel settings). The joyCaps argument must be passed in from the previous JoyEnum because it contains the ID used by the driver to correlate the appropriate mini-driver. (If this is in fact unnecessary from a DDI implementation, the last argument would be eliminated.)

It returns the handle to the device used in many subsequent calls.

int JoyModesEnum (HANDLE deviceHandle, JOYMODECAPS *joyModeCaps, int done)

Like JoyEnum, this enumerates the capabilities into the caps structure one at a time. In this case, it enumerates the logical mapping modes supported by the device. The joystick manufacturer will typically enumerate the following kinds of modes:

void JoySetMode (HANDLE deviceHandle, JOYMODECAPS *joyModeCaps)

This function puts the driver into the mode selected by the joyModeCaps which was derived from the JoyModesEnum call. Once this has been done, all calls to JoyReadChannels will return the channels as mapped by the definition of the selected mode.

int JoyChannelEnum (
HANDLE deviceHandle,
JOYCHANNELCAPS *joyChannelCaps,
int done
)

Like JoyEnum and JoyModesEnum, this function enumerates the capabilities into the caps argument one at a time. With this function, the channels of the currently selected mode are enumerated. This gives basic channel descriptions such as min, max, and center, but more importantly, it gives a registry key which can be used to derive parametric information.

void JoyReadChannels (HANDLE deviceHandle, CHANNELBUFFER *channelBuffer)

This is the function that actually retrieves the position and state information for the channels. All the channels are a DWORD in size and their order is dependent on the selected mode.

void JoyWriteChannels (HANDLE deviceHandle, CHANNELBUFFER *channelBuffer)

This function writes the channel data back to the device if the device supports this. This is not demonstrated in the sample code.

void JoySetChannelState (
HANDLE deviceHandle,
int channelNum,
JOYCHANNELCAPS *caps
)

This function will set the state of the enable / disable bit. I chose to pass in the entire caps structure here in case any other data might be useful to set, but this function is primarily intended to disable unused channels. The advantage of using this is that intelligent devices may use this information to turn-off certain parts of the polling on the joystick to save polling time, transfer time, and power. Failing to use this function will not cause harm, it will just potentially result in a less efficient poll. It is assumed that all modes set the unused physical channels automatically. That is, if JoySetMode is called with FLIGHT_STICK_FAMILY, it is assumed that the device driver is smart enough to turn off all logic on the device unused by the "flight stick" family.

void JoyAddSimultanousDevicePoll (
HANDLE primaryDevice,
HANDLE associatedDevice,
CHANNELBUFFER *associatedChannelBuffer
)

This function is used to avoid ring transitions. It instructs the driver that the associated handle and buffer should be polled at the same time that the primary device is. The basic idea is that if the application is going to poll more than one device then this will usually be done at the same time. By using this function to chain the devices together, the JoyReadChannels call can get all the work done during one ring transition.

These associations are maintained Internal to the device driver presumably in a linked list. I do not define how this will take place.

This function is not demonstrated in the sample code.

Application Sample Code

This piece of sample code demonstrates both how to initialize the logical mapping and how to poll the joystick. For this example, we imagine that the game has four basic commands: CHANGE_DIRECTION, ACCELERATE, SHOOT_GUNS, and SHOOT_MISSLES.

This code demonstrates the simplicity of homogeneous channels by the use of a lookup table called channelMap. This map is used to pull the appropriate commands out of the channel buffer and into the command variables used by the application.

Note that the initialization code here is deliberately over complicated to demonstrate all possible ways to derive the logical mapping mode. In a real application, the programmer would probably stick to either a simple family and vendor map. The parametric information would, most likely, be used only by a very complex application such as one that required or supported sophisticated VR hardware.

//------------------------------------------------------------------

// Application Channels

//------------------------------------------------------------------

#define CHANGE_DIRECTION 0

#define ACCELERATE 1

#define SHOOT_GUNS 2

#define SHOOT_MISSLES 3

#define NUMBER_OF_APPLICATION_CHANNELS 4

//------------------------------------------------------------------

// Global Variables

//------------------------------------------------------------------

HANDLE joystickDeviceHandle = NULL;

int numChannels;

char channelLookupTable [NUMBER_OF_APPLICATION_CHANNELS];

//------------------------------------------------------------------

// initJoysticks

//------------------------------------------------------------------

void initJoysitcks() {

// Count all of the joystick devices that are connected by

// enumerating them into JOYCAPS holders

// In a real application, you might want to use all of them,

// but in this demonstration we will only use the designated primary.

JOYCAPS joyCaps;

int done = 0;

for (

int numDevices=0;

done = JoyEnum (&joyCaps, done);

numDevices++

);

if (numDevices > 1) {

// This is a placeholder for a dialog box...

print ("You have more than one physical joystick connected to your comupter "

"Your current selection is the %s\n"

"Press \"Select\" to open the control panel.",

joyDevices[JoyGetPreferredJoystick()].

);

// Open the joysitck control panel applet if they ask to...

}

// Set the global device handle. This also resets the joyCaps variable

joystickDeviceHandle = JoyOpenDevice (PREFERED_JOYSTICK, &joyCaps);

//------------------------------------------------------------------

// The initialization code uses the following algorithm
// to deterimne the logical mapping.

// 1. Enumerate the modes.

// 2. Choose the appropraite mode based on the following preferences:

// 1. First preference: Vendor/Product is Wacky Ultra Pro™ or compatible

// 2. Second preference: Flight Stick Family

// 3. Thrid prefernce: A good parametric fit

// 4. Fourth preference: Generic Family

// 3. If none of these are avilable, resort to a parametric approach

// 1. Enumerate the channels

// 2. Find a appropriate horizontal, vertical axises as well as two buttons.

// 4. Open the device in the appropraite mode.

//

// Now, enumerate the modes to determine the appropraite logical map

// Use a simple "best match" search algorithm. Keep the generic

// mode as a backup in case we canít find any other preferable mode.

JOYMODECAPS tmpModeCaps, bestModeCaps, genericModeCaps;

done = 0;

int best = 0;

while (done = JoyModesEnum (device, &tmpModeCaps, done)) {

if (

tmpModeCaps.guidFamily == UNKNOWN_FAMILY &&

tmpModeCaps.guidManufactuerID == WACKY_CORP &&

tmpModeCaps.guidProductID == WACKY_JOYSTICK_ULTRA_PRO_STICK_EXTREME

) {

memcpy (&bestModeCaps, &tmpModeCaps, sizeof(JOYMODECAPS));

best = 2;

}

else if (tmpModeCaps.guidFamily == FLIGHT_STICK_FAMILY && best < 2) {

memcpy (&bestModeCaps, &tmpModeCaps, sizeof(JOYMODECAPS));

best = 1;

}

else if (

tmpModeCaps.guidFamily == UNKNOWN_FAMILY &&

tmpModeCaps.guidManufactuerID == joyCaps.guidManufactuerID &&

tmpModeCaps.guidProductID == joyCaps.guidProductID &&

best < 1

) {

// This is the native mode.

memcpy (&bestModeCaps, &tmpModeCaps, sizeof(JOYMODECAPS));

}

else if (tmpModeCaps.guidFamily == GENERIC_FAMILY) {

// Always keep the generic mode around in case all else fails.

memcpy (&genericModeCaps, &tmpModeCaps, sizeof(JOYMODECAPS));

}

}

// Now set the mode. This causes the joystick driver to return values

// as appropraite for the established mode during calls to JoyReadChannels

JoySetMode (joystickDeviceHandle, &bestModeCaps);

// Set the global numChannels variable

numChannels = bestModeCaps.numChannels;

//------------------------------------------------------------------

// Now set up our application conversion lookup table depending on

// the mode. If we havenít found a family that we have a pre built

// lookup table for, then we will have to resort to a parametric approach.

// These mappings are all arbitrary examples

static char wackyUltraProChannelMapping[CHANNEL_MAP_SIZE] = { 3, 2, 1, 0 };

static char flightStickChannelMapping[CHANNEL_MAP_SIZE] = { 1, 2, 3, 0 };

static char genericFamilyChannelMapping[CHANNEL_MAP_SIZE] = { 0, 2, 1, 3 };

if (best == 2) {

// Load the WACKY_JOYSTICK_ULTRA_PRO_STICK_EXTREME_TM

// mapping into the lookup table

memcpy (

channelLookupTable,

wackyUltraProChannelMapping,

NUMBER_OF_APPLICATION_CHANNELS

);

solutionFound = 1;

}

else if (best == 1) {

// Load the flight-stick family mapping into the lookup table

memcpy (

channelLookupTable,

flightStickChannelMapping,

NUMBER_OF_APPLICATION_CHANNELS

);

solutionFound = 1;

}

//------------------------------------------------------------------

// Now life starts getting tricky, we havenít found any of our

// prefered mapping modes so we must resort to the

// channel parameters of the native mode. If we canít get a good

// fit here, then we will resort to the generic family.

// Note that some application designers may choose to skip this step

if (best == 0) {

// Enumerate all of the channels

JOYCHANNELCAPS *channels = new JOYCHANNELCAPS [numChannels];

done = 0;

int i = 0;

for (

int i=0;

done = JoyChannelEnum (joystickDeviceHandle, &channels[i], done);

i++

);

for (i=0; i<bestModeCaps.numChannels; i++) {

// THE FOLLOWIUNG IS ALL PSUEDO-CODE

// lookup in the registry using the channels[i].registryKey

// and find the parametric info. Set up each channel

// if you find an appropraite channel description

for (i = enumerate the channel parameters) {

if its a primary button then

channelLookupTable[FIRE_BUTTON] = i;

if its a secondary button then

channelLookupTable[MISSLE_BUTTON] = i;

if its a primary horizontal axis then

channelLookupTable[X_AXIS] = i;

if its a primary vertical axis then

channelLookupTable[Y_AXIS] = i;

}

channels[i].flags = channel actually used ? enabled : disabled;

solutionFound = 1;

}

// Now tell the driver which channels are actually active.

// This is an optimization that can make it so that any intelligent

// device can shut off the polling of extraneous channels

if (solutionFound) {

for (i = 0; i < numChannels; i++) {

JoySetChannelState (joystcikDeviceHandle, i, &channels[i]);

}

}

else {

// If we canít find any other solution than we will resort to generic

memcpy (

channelLookupTable,

genericFamilyChannelMapping,

NUMBER_OF_APPLICATION_CHANNELS

);

}

}

}

//--------------------------------------------------------------------

// Main Funtion

// This demonstrates the intialization and polling, nothing else.

//--------------------------------------------------------------------

void main() {

initJoystick();

// The previous function set the joystickDeviceHandle

// and numChannels global variables.

channelBuffer = new DWORD [numChannels + 1];

// The +1 is for the required dwSize field

channelBuffer[0] = sizeof(DWORD) * (numChannels + 1);

while (the game is still running) {

// Poll the iput

JoyReadChannels (joystickDevicehandle, channelBuffer);

// Execute the commands...

direction = channelBuffer[channelLookupTable[CHANGE_DIRECTION]];

speed += channelBuffer[channelLookupTable[ACCELERATE]];

if (channelBuffer[channelLookupTable[SHOOT_GUNS]]) {

launch a bullet

}

if (channelBuffer[channelLookupTable[SHOOT_MISSILES]]) {

launch a missle

}

update the world

render the world

}

}

 

Family Synopsis

Families are designed to specify the logical map of common input devices. Note that for each one, there is a designated ordering to both the channels and the scales. In general:

Family

Channel

Designation

Notes

Generic /

0

X

Minimum values on left

Game Pad

1

Y

Minimum values on top

 

2

Suggested Primary Button

For all buttons, one means pressed zero mean released.

 

3

Suggested Secondary Button

 
 

4 .. n

Auxiliary Buttons

Generally ordered in easiest to hit to least easy to hit. No defined meaning.

Flight Stick

0

X

Minimum values on left

 

1

Y

Minimum values on top

 

2

Trigger

Generally on index finger.

 

3

Primary Thumb Button

 
 

4

Hat X

Entered as an axis even if it is actually implemented as a switch. Minimum values on left

 

5

Hat Y

Entered as an axis even if it is actually implemented as a switch. Minimum values on top

 

6 .. n

Auxiliary Buttons

Generally ordered in easiest to hit to least easy to hit. No defined meaning.

Yoke

0

Roll

Minimum values on left

 

1

Pitch

Minimum values when forward (away from the user).

 

2

Left Primary Button

 
 

3

Right Primary Button

 
 

4 .. n

Auxiliary Buttons

Generally ordered in easiest to hit to least easy to hit. No defined meaning.

 

Pedals

0

Yaw

Minimum values on left

 

1

Left Brake

Minimum values when released

 

2

Right Brake

Minimum values when released

 

3

Accelerator

Minimum values when released

 

4 .. n

Auxiliary Buttons

Generally ordered in easiest to hit to least easy to hit. No defined meaning.

Throttle

0

Thrust

Minimum values when back (towards user)

 

1 .. n

Auxiliary Buttons

Generally ordered in easiest to hit to least easy to hit. No defined meaning.

Steering

0

Steering Axis

Minimum values on left

Wheel

1 .. n

Auxiliary Buttons

Generally ordered in easiest to hit to least easy to hit. No defined meaning.

Directional 3d Tracker

0

Roll

This is relative to the userís orientation. Minimum values on userís left.

 

1

Pitch

This is relative to the userís orientation. Minimum values when pointing down.

 

2

Yaw

This is relative to the userís orientation. Minimum values on userís left.

 

3 .. n

Auxiliary Buttons

Generally ordered in easiest to hit to least easy to hit. No defined meaning.

 

6d Tracker

0

Roll

This is relative to the userís orientation. Minimum values on userís left.

 

1

Pitch

This is relative to the userís orientation. Minimum values when pointing down.

 

2

Yaw

This is relative to the userís orientation. Minimum values on userís left.

 

3

Translational - X

This is left / right relative to the monitor. Minimum values to left.

 

4

Translational - Y

This is towards / away from the monitor. Minimum values towards the monitor.

 

5

Translational - Z

This is up / down relative to the monitor. Minimum values above the monitor

 

6 .. n

Auxiliary Buttons

Generally ordered in easiest to hit to least easy to hit. No defined meaning.

 

Control Panel Applets

Joystick manufacturers should create control panel applets which allow the user to configure their device. I have illustrated what one of these might look like below. This is only a prototype.

 

Parametric info

Parametric information is used by an application to determine a logical-to-application mapping in the case that the device does not conform well to any of the pre-determined families. It may also be used when the application is very sophisticated and requires complete control over a large number of differing devices such as might be the case with VR equipment.

This information is stored entirely in the registry. As described in the Prototype Data Structures above, the CAPS structures contain a key into this registry. This key is used by developers to access the parameter information on a per channel basis.

A large number of these parameters have been enumerated by Steve McGowan at Forte in his Access Bus specifications. I suggest these are used a the basis for creating the data. However, in his specification, he uses a string to describe the data due to the nature of the Access Bus. While strings have the advantage of being flexible, I believe that it is more appropriate to avoid flexibility in this case; this avoidance will preclude inconsistencies arising between application which would otherwise render the data useless. Also, strings have the disadvantage that they are difficult to parse as compared to a structure. Thus, I suggest that we take Steveís info and make it into a structure to be saved into the registry.

The parameteric information falls into two basic categories: application-descriptive and physical- descriptive.

Application-Descriptive

Parameters which relate to a specific usage or are suggestions for a potential use are said to be "application-descriptive". For example, "chaff", "guns", "thrust", "steering wheel", etc. Note that many of the applications described in Steveís document fall into recognizable categories such as "flight controllers", etc. When these categories are recognized, a "family" has been created to simplify matters as documented above.

Physical-Descriptive

Parameters which are descriptions of the physical or mechanical attributes of a device are said to be "physical-descriptive". These are such things as: "does it return to center?", "is it primary?", "is it rotational", etc. Steve has also defined these in his Access Bus specification. I make only a few suggestions in addition:

Summary and Plan of Action

Create a GameSDK Joystick Interface Version 2.0 which will include:

Homogeneous Channels

Enumeration of Logical Mapping Modes including:

Family

Manufacturer

Parametric Information

Create a "standardized" Control Panel Applet.

Define the parametric fields in sub-committee (i.e. Steve McGowan and Microsoft)

Create a WWW page to summarize these changes either at Microsoft or Origin.