发信人: catyboy()
整理人: nhyjq(2001-12-07 16:57:53), 站内信件
|
Programming Mouse and Keyboard with DirectInput 3.0 - Peter Donnelly
Peter Donnelly has been a game designer since 1972 and a programmer si nce 1984. He has authored four "paper" games with historical and fanta sy themes, and his computer design credits include three interactive c omic-book adventures and one full-scale adventure game. He has also de veloped shareware games and game editors for MS-DOS and Windows.
Abstract
In September 1996 Microsoft released Version 3.0 of the DirectX?SDK (s oftware development kit), a set of tools for games and multimedia appl ications. This version of the SDK is the first to provide support for mouse and keyboard input. In previous versions, DirectInput?was actual ly just another name for the extended joystick services in the standar d Windows SDK.
In this article I will guide you through the process of setting up and using DirectInput objects for mouse and keyboard. I will only touch o n the existing joystick services, which are well documented elsewhere.
The information here is intended to supplement the documentation found in DINPUT3E.DOC, part of the DirectX SDK. You should have that docume nt at hand as a reference to the data structures and functions I will introduce as I step through the implementation of DirectInput.
The Sample Program
DINPUT.EXE is a simple program that illustrates most of the techniques described in this article. It relies entirely on DirectInput routines for mouse and keyboard input, except for input processed by Windows i tself, such as mouse clicks on the menu.
Two keystrokes are recognized: Esc to quit the program, and F2 to disp lay absolute mouse coordinates. Clicking the left mouse button display s a message, and the right button flushes the mouse input buffer. Othe r options are available on the View menu.
The program is in the form of a project for Microsoft?Visual C++?versi on 4.0. It uses C++ syntax but no classes other than the COM objects. It requires Windows?95 and will not run under Windows NT?
From time to time I'll also be referring to Scrawl, the sample DirectI nput application included with the DirectX SDK. You'll probably want t o refer to the source code for that application as I go along.
The Component Object Model
DirectX is based on the component object model (COM), defined by Willi ams and Kindel (see Further Reading at the end of this article) as "a component software architecture that allows applications and systems t o be built from components supplied by different software vendors." Pu t another way, it is a protocol that allows software modules to work w ith one another even when they are written in different languages and running on different platforms.
You may find it helpful to have some understanding of COM before delvi ng into the DirectX SDK, but it is possible to implement DirectInput f or a single-user Win32 application without getting into the nitty-grit ty of COM interfaces. (In COM, an object's interface is a menu of its available methods.) Consequently I won't go into the theory, but at th e end of the article I will point you to several good overviews.
It is important to note here that although COM is object-oriented, you don't need an object-oriented programming language to use DirectInput or, indeed, any of the DirectX APIs (application programming interfac es). Although the examples I'll present here are written for C++, the samples in the DirectX package all use plain C. The only significant d ifference is the way of calling member functions of the DirectInput ob jects. For instance, in C you would use the following syntax to call t he CreateDevice function for the DirectInput object (treated as a stru cture) that is pointed to by lpdi:
lpdi->lpVtbl->CreateDevice(lpdi, &guid, &lpdiKeyboard, NULL);
Here lpVtbl is a pointer to a table of pointers to functions. In C++, since we can treat lpdi as a pointer to an object, we access the membe r functions more directly:
lpdi->CreateDevice(guid, &lpdiKeyboard, NULL);
Setting up--An Overview
Before an application can start reading the mouse or keyboard with Dir ectInput, it has to go through a few preliminary steps, first to set u p the master DirectInput object and identify any available devices, th en to set up each device to be used.
Setting up DirectInput
The following two procedures have to be performed once only. The Direc tInput object is then ready to manage all mouse and keyboard devices a ttached to the system.
1. Create the DirectInput object. This sets up the basic framework for handling all input.
2. Enumerate the devices. We obtain the GUID (globally unique identifi er) for any input devices attached to the system. At the same time we can do any other processing we like, such as checking for a particular mouse model.This step can be skipped if the application is using only the standard system mouse and keyboard; in this case the predefined g lobal variables GUID_SysMouse and GUID_SysKeyboard can be used to iden tify the devices.
Setting Up a Device
After the DirectInput object is created and the devices have been enum erated, the following steps are needed to set up each input device. No rmally this would mean one mouse and one keyboard, but potentially an application could support multiple pointing devices.
1. Create the device. This step creates a code object for the mouse or keyboard and attaches it to the DirectInput object.
2. Set the cooperative level. You let the system know the desired leve l of access to the device. This step is optional if the default settin g (nonexclusive background) is acceptable.
3. Set the data format. Tell DirectInput how to return the information you want about the device when you poll it.
4. Set properties. An optional step taken only if you want to change a ny of the default properties of the device, e.g. to tell the mouse to return absolute rather than relative coordinates.
5. Acquire the device. Finally, you tell the system that you wish to b egin receiving data from the device. This step gives DirectInput a cha nce to set up the input buffer and otherwise optimize the device for y our application. The step has to be repeated whenever your window has "lost" the device because it has been yielded to another application o r to the system.
Setting Up--The Details
Create DirectInput Object
The first step in setting up DirectInput in your application is to cre ate the master object that provides access to the COM interface. It's done like this:
LPDIRECTINPUT lpdi;DirectInputCreate(hTheInstance, DIRECTINPUT_VERSION , &lpdi, NULL);
The first argument is the handle of the application or dynamic-link li brary (DLL) that is creating the object. The second is always DIRECTIN PUT_VERSION, defined in DINPUT.H; this is simply a safety check to mak e sure the application is running with the correct version of the run- time DLLs. The third argument is the location where we want the pointe r to the IDirectInput interface to be stored. Finally, we pass NULL to the function if we want the object to be created normally and initial ized automatically. (To put it another way, if you don't know what the documentation for this argument means by "the controlling unknown for OLE aggregation," just pass NULL and don't worry.)
If the call succeeds, lpdi points to the IDirectInput interface and, a s we've seen, can be used just like a pointer to an object; for exampl e, in C++, lpdi->EnumDevices() calls the EnumDevices method implemente d for the IDirectInput interface.
Enumerate Devices
The EnumDevices function finds all mice (including similar devices suc h as trackballs) and keyboards on the system and calls an application- defined callback function for each one. If you want to restrict the en umeration to devices of one type or the other, pass DIDEVTYPE_MOUSE or DIDVETYPE_KEYBOARD as the dwDevType argument; to enumerate all device s, make this argument zero. You can't use subtypes in the argument to narrow down the search further, but you can check the subtype in the c allback function.
Here's an example that checks whether the machine has an enhanced keyb oard attached. First the callback function:
LPDIRECTINPUT lpdi;
BOOL hasEnhanced;
GUID KeyboardGUID = GUIDSysKeyboard;
BOOL CALLBACK DIEnumKbdProc(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
{
*(GUID*) pvRef = lpddi->guidProduct;
if (GET_DIDEVICE_SUBTYPE(lpddi->dwDevType) == DIDEVTYPEKEYBOARD_PCE NH)
{
hasEnhanced = TRUE;
return DIENUM_STOP;
}
return DIENUM_CONTINUE;
}
Then, in the setup section, we do the enumeration:
lpdi->EnumDevices(DIDEVTYPE_KEYBOARD, DIEnumKbdProc, &KeyboardGUID, DI EDFL_ATTACHEDONLY);
The third argument to EnumDevices can be a pointer to any data you wan t to process in the callback function. In this case we use it to retur n the GUID for the enhanced keyboard.
We use DIENUM_CONTINUE as the default return value for the callback be cause we want to be sure to look at all keyboards attached to the syst em, in the unlikely event that there are more than one. As soon as an enhanced keyboard is found, DIENUM_STOP is returned so that EnumDevice s stops enumerating--which doesn't accomplish much in the example exce pt possibly shave a few milliseconds off the application's startup tim e.
Create Device
Now that you've obtained the GUID for a device, either through EnumDev ices or by using the predefined GUID_SysMouse and GUID_SysKeyboard var iables, you have to create a code object for communicating with the ID irectInputDevice interface. This is like what we did to create the mas ter DirectInput object pointed to by lpdi. Here we go:
LPDIRECTINPUTDEVICE lpdiKeyboard;lpdi->CreateDevice(GUID_SysKeyboard, &lpdiKeyboard, NULL);
Our DirectInput object has now created a DirectInputDevice object, poi nted to by lpdiKeyboard. This pointer gives us access to all the metho ds we need. As with CreateDirectInput, the NULL argument ensures that the device is created normally, in which case initialization is automa tic.
Note: In all the code examples that follow, lpdiDevice, lpdiKeyboard, and lpdiMouse are presumed to be initialized pointers of type LPDIRECT INPUTDEVICE.
Set Cooperative Level
The cooperative level of a device determines the circumstances in whic h an application has access to it, and whether the Windows system has access. It's set by passing a combination of flags, plus the window ha ndle, to SetCooperativeLevel:
lpdiDevice->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREG ROUND)
For Windows 95, the valid flag combinations are:
DISCL_NONEXCLUSIVE | DISCL_BACKGROUND (the default)
DISCL_NONEXCLUSIVE | DISCL_FOREGROUND
DISCL_EXCLUSIVE | DISCL_FOREGROUND (mouse only)
DISCL_BACKGROUND really means "foreground and background." A device wi th a DISC_BACKGROUND cooperative level can be acquired (i.e. used) by an application at any time, even when the application does not have th e focus. In the sample application, if you set the Access option butto n in the Mouse Properties dialog to Background, and turn on the displa y of mouse coordinates, you will see the coordinates changing even whe n another application is in the foreground. With the Foreground settin g, the mouse is "unacquired" whenever another application is activated , so the coordinates don't change.
The DISC_EXCLUSIVE argument gives the application exclusive access to the mouse while the application is in the foreground. This means that Windows stops monitoring the mouse, and no messages are generated for it. A side effect is that the cursor disappears, since Windows is no l onger tracking the axes. (I'll get back to the topic of cursors later in this article.)
Note: DirectInput is not currently supported on Windows NT, and in fut ure only the exclusive foreground mode will be supported. Also keep in mind the warning in the DirectInput documentation: "The Windows NT ve rsion of DirectInput (and the Windows 95 version of DirectInput on a n on-supported mouse driver) will report DIERR_INPUTLOST more frequently than the Windows 95 version [on a fully supported mouse driver], and your application should be written to handle these cases."
Set Data Format
Before your application can acquire a device, you have to tell DirectI nput the format of the data required from the device. This mechanism i s doubtless intended to allow future compatibility with joysticks, for ce-feedback gloves, and other devices. At present, because only the mo use and keyboard are supported, you don't have to worry about the intr icacies of the DIDATAFORMAT structure used in the argument toSetDataFo rmat. DirectInput provides a couple of global variables, c_dfDIKeyboar d and c_dfDIMouse, that are declared in DINPUT.H and defined in DINPUT .LIB.
This is all you need to do:
lpdiKeyboard->SetDataFormat(&c_dfDIKeyboard);lpdiMouse->SetDataFormat( &c_dfDIMouse);
Looking at the reference for the DIDATAFORMAT structure, you might be tempted to think that you can tinker with the device properties here b y changing the contents of dwFlags. But you can't do this with c_dfDIM ouse, because it is a const. If you want to change the properties, do so in the next step.
Set Properties
A device (e.g. mouse) or device object (e.g. mouse axis) has up to fou r properties:
Axis mode (determines whether it reports its position absolutely or re latively)
Range of values reported for an axis
Granularity (smallest increment reported for an axis value)
Size of the buffer used for buffer input
Only one of these, the buffer size, applies to the keyboard. Two prope rties, the axis mode and the buffer size, can be altered at runtime.
You can't set properties for an acquired device. If it's necessary to change a property after the initial setup, use Unacquire first.
Before calling SetProperty (or GetProperty, for that matter), you need to set up a DIPROPDWORD structure, which consists of a header and a d ata word. Initialize the header with (1) its own size, (2) the size of the DIPROPDWORD structure, (3) an object identifier, and (4) a "how" code indicating the way the object ID should be interpreted. When sett ing properties, dwObj is always zero and dwHow is always DIPH_DEVICE. (Future versions of DirectInput may permit properties to be changed fo r individual objects, but for now they can only be changed for devices .)
After these rather complicated preliminaries, you pass the address of the header into SetProperty, along with an identifier for the property you want to change.
Here's an example that sets the buffer size for a device so that it wi ll hold 10 data items:
#define BufferSize 10
DIPROPDWORD dipdw;
HRESULT hres;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = BufferSize;
hres = lpdiDevice->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph);
if (hres != DI_OK)
OutputDebugString("Failed to set buffer size.\n");
As I've already stated, DirectInput 3.0 doesn't let you set object pro perties. So you can't set the axis mode for an individual axis; it mus t be set for the entire device, using zero in the dwObj field.
Acquire Device
We've finally reached the last step in making the device ready for use . In DirectInput, "acquiring" a device essentially means getting permi ssion to use it. Acquisition is not permanent; your application may ac quire and unacquire the mouse or keyboard many times. It's a bit like an old-fashioned hotel where you're expected to leave your key at the front desk. You've already booked the room and checked in, but you sti ll need to go to the desk every time you enter or leave the building. There might be a single key (exclusive access) or more than one (nonex clusive), but no one gets in without a key.
Simple-minded analogies not enough? Okay, here's a more technical expl anation of just why an application needs to acquire and unacquire devi ces. There are two main points.
First, DirectInput has to be able to tell the application when the flo w of data from the device has been interrupted by the system. For inst ance, if the user has switched to another application with ALT+TAB, an d used the mouse or keyboard in that application, your application nee ds to know that the input no longer belongs to it and that the state o f the buffers may have changed. DirectInput automatically unacquires t he device for you in such circumstances.
Second, because your application can alter the properties of the devic e, without safeguards DirectInput would have to check the properties e ach time you wanted to get data with GetDeviceState or GetDeviceData. Obviously this would be very inefficient. Even worse, messy things cou ld happen like a hardware interrupt accessing a data buffer just as yo u were changing the buffer size. So DirectInput requires your applicat ion to unacquire the device before changing properties. When you reacq uire it, DirectInput looks at the properties and decides on the optima l way of transferring data from the device to the application. This is done only once, thereby making GetDeviceState and GetDeviceData light ning-fast.
If the device's cooperative level is set to foreground only, it is aut omatically unacquired whenever the window loses the focus, even to the application's own menu. The DINPUT sample copes by reacquiring the mo use and keyboard whenever the window is reactivated. This may not be t he best way of doing things, in light of the warning in the DirectInpu t documentation that I quoted above--with some mouse drivers, DIERR_IN PUTLOST may be reported "more frequently". See SCRAWL.CPP for another way of ensuring that your app is not trying to get data from an unacqu ired device.
There's no harm in attempting to reacquire a device that is already ac quired. Repeated calls to Acquire have no effect, and the device can s till be unacquired with a single call to Unacquire.
Remember, Windows doesn't have access to the mouse when your applicati on is using it in exclusive mode. If you want to let Windows have the mouse, you must let it go. There's an example in the Scrawl applicatio n, which responds to a click of the right button by unacquiring the mo use, putting the Windows cursor in the same spot as its own, popping u p a context menu, and letting Windows handle the input until a menu ch oice is made.
Using the Keyboard
You can use DirectInput either to take a snapshot of the keyboard stat e or to maintain a buffer of keyboard incidents, an "incident" being t he press or release of a key. (I'll use this word rather than "event" in order to avoid confusion with Windows messages or event objects.) W e'll look at buffered input later; for now, here's a bit of code that takes a snapshot and checks for a depressed Esc key:
BYTE diKeys[256];
if(lpdiKeyboard->GetDeviceState(256, &diKeys) == DI_OK)
{
if(diKeys[DIK_ESCAPE] & 0x80)
DoSomething();
}
The call to GetDeviceState simply returns an array of 256 bytes, each standing for a key. If the key is depressed, the high bit is set. The array can be indexed most conveniently with the DIK_* defines from DIN PUT.H.
Remember, GetDeviceState returns the present state of the keyboard, no t a record of incidents. If your application does something in respons e to a keystroke, it's your responsibility to make sure the action isn 't repeated inappropriately if the key is still down on the next pass through the polling loop.
Using the Mouse
DirectInput Doesn't Do Cursors
When programming the mouse with the standard Windows API, it's easy to think of the mouse and the cursor as being one and the same. With Dir ectInput you can't think this way, because DirectInput does not concer n itself about the cursor at all. Its mouse services are first and las t an interface to the device rolling around on the tabletop, and the s creen might as well not exist at all.
The Scrawl sample program shows how you can synthesize a mouse-control led cursor with DirectInput. This technique is necessary if you need t o have both a cursor and exclusive access to the mouse in a full-scree n application. (Remember, exclusive mode kills the default cursor and all Windows mouse messages.) In other cases, if you need a cursor but not exclusive access--and if performance is not a big issue--it's way easier to let Windows handle cursor movement while you track it with G etCursorPos or WM_MOUSEMOVE messages. You can still use DirectInput to monitor mouse clicks.
Bear in mind that if your application does synthesize a cursor, none o f the user settings in the Control Panel mouse applet, such as speed a nd acceleration, are relevant. Also, there is no way for DirectInput t o deduce how far the mouse has actually rolled in order to produce a g iven change of axis. That's why Scrawl allows the user to set mouse se nsitivity within the program--at the default setting, the cursor may m ove too slowly or too quickly in response to mouse movements.
Polling the Mouse
DirectInput bypasses the Windows message system, so the only way to fi nd out what the mouse is up to is to ask it. To poll the mouse in real time (we'll get to buffered input later), use GetDeviceState, which r eturns the current state of the mouse in a DIMOUSESTATE structure. Her e's an example that retrieves the axis values, and produces a beep if the left button is clicked:
DIMOUSESTATE diMouseState;
BOOL ClickHandled;
LONG MouseX, MouseY;
if(lpdiMouse->GetDeviceState(sizeof(diMouseState), &diMouseState) == D I_OK)
{
if(diMouseState.rgbButtons[0] & 0x80)
{
if(!ClickHandled)
{
MessageBeep(0);
ClickHandled = TRUE;
}
}
else
ClickHandled = FALSE;
MouseX = diMouseState.lX;
MouseY = diMouseState.lY;
}
We use the ClickHandled variable to keep track of whether the button p ress has been dealt with. Remember, GetDeviceState returns the current state of the mouse buttons, not presses or releases, so we have to en sure that the program doesn't keep responding to the "mouse down" stat e.
In DINPUT.EXE you can see the results of real-time mouse axis polling after setting Axis Display to Relative or Absolute in the Mouse Proper ties dialog.
Relative and Absolute Axes
By default, the mouse axis coordinates are returned as relative values , i.e. the amount by which they have changed since the last call to Ge tDeviceState or, in the case of buffered input, since the last item wa s put in the buffer. You can use SetProperty to have the axes returned as absolute values, which show the position of the mouse relative to an arbitrary point (somewhere in the next county, judging from the siz e of the default offset in my tests). In both cases the units are "mic keys", a mickey being the smallest measurable movement of a given devi ce.
Using Buffered Data
When real-time input is not what's needed, your application can read e vents from a buffer instead.
It's your responsibility to set the size of the buffer; see the exampl e under Set Properties, above. By default the buffer size is zero, so this is a crucial step. The value in the dwData field of the DIPROPHEA DER structure passed into SetProperty is the number of data items you want buffered, not the memory size in bytes or words. For the keyboard , each press and each release of a key is an item of data. For the mou se, an item is a button press or release or any movement that generate s an interrupt.
The sample program, DINPUT.EXE, buffers both mouse and keyboard input. Press a few keys and then examine the contents of the keyboard buffer by choosing Keyboard buffer on the View menu. Move the mouse slightly , click the left button once or twice, then press the right button to display the contents of the mouse buffer. Displaying the buffer also h as the effect of flushing it, though this behavior can be altered.
The buffer display shows the sequence number and age of each incident. For the keyboard, the key scan code and the type of action is also sh own. For the mouse, you see which button or axis generated the inciden t, the type of action, and--for axes--the relative movement, unless th e axes have been changed to absolute in the Mouse Properties dialog. I f more than 10 incidents have occurred since the buffer was last flush ed, a buffer overflow warning is displayed as well. When the buffer ov erflows, it is the most recent events, not the oldest, that are lost.
Items are in chronological order within the buffer, so you don't need the sequence numbers for determining the order in which they were gene rated. But there are a couple of tasks where sequence numbers do come in handy:
1. Recognizing simultaneous events. You'll observe in our sample progr am that a diagonal movement of the mouse produces the same sequence nu mber for the changes in the X and Y axes. In Scrawl, a line is not dra wn till simultaneous changes in the X and Y axes have both been proces sed--otherwise a right angle would be drawn instead of a slanting line .
2. Finding out which event came first when processing input from more than one device. Take, for example, a quiz game where two players vie to be the first to sound a buzzer, one player using the keyboard while the other uses the mouse. Sequence numbers would allow the applicatio n to determine whether the keystroke or the mouse click came first, re gardless of how close together they were. (Note that DirectInput maint ains a single sequence of numbers, not a separate sequence for each de vice.)
Here's the code our sample uses to retrieve and display the contents o f the keyboard buffer:
HRESULT FlushKbdBuffer(void)
{
HRESULT hres;
DWORD dwItems;
char szScan[7];
char szAge[10];
char szOutput[99];
DWORD k;
DIDEVICEOBJECTDATA *lpdidod;
dwItems = BUFFERCOUNT; // defined as 10
hres = lpdiKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
KbdBuffer, &dwItems, 0);
if(hres == DI_BUFFEROVERFLOW)
TextToScreen(hMainWindow, "Buffer overflow!");
for (k = 0; k dwSequence);
strcat(szOutput, ". Scan code ");
sprintf(szScan, "%x", lpdidod->dwOfs);
strcat(szOutput, szScan);
if (lpdidod->dwData & 0x80)
strcat(szOutput, " pressed ");
else
strcat(szOutput, " released ");
sprintf(szAge, "%d ms ago", GetTickCount() - lpdidod->dwTimeStam p);
strcat(szOutput, szAge);
TextToScreen(hMainWindow, szOutput);
}
return hres;
}
Points to note:
Before calling GetDeviceData, we set dwItems to the maximum number of items in the buffer. On return, dwItems holds the actual number of ite ms retrieved. You don't have to retrieve the entire contents of the bu ffer; the Scrawl demo, for instance, retrieves just one item at a time . Whenever you retrieve an item with GetDeviceData, by default it is r emoved from the buffer, making room for another. You can, however, pre serve the data in the buffer by using the DIGDD_PEEK flag.
KbdBuffer is an array of DIDEVICEOBJECTDATA structures. Each structure holds the data for one key press or release. The information returned includes the scan code of the key, a sequence number, and a timestamp in milliseconds equivalent to what would be returned by GetTickCount. The size of the array depends on the definition of BUFFERCOUNT, and w e have to be careful not to attempt to retrieve more elements than exi st. For the keyboard, the value returned in dwOfs is the scan code of the key that generated the incident. For the mouse, this is a DIMOFS_* constant (more accurately a macro that returns a constant) representi ng one of the buttons or axes. The most significant bit of the low-ord er byte of the dwData field for each data item is set for a key press and cleared for a release. This works the same way for mouse buttons. For axes, dwData holds the coordinate.
Event Notification
DirectInput provides support for thread synchronization in the form of the SetEventNotification function. You can see a simple implementatio n in Scrawl, which sets up an event object for the mouse with CreateEv ent and then orders DirectInput's mouse object to report to the event object with SetEventNotification. The WinMain loop then calls MsgWaitF orMultipleObjects to check whether the mouse event is signaled, i.e. w hether there is input that needs to be processed.
Running Control Panel Applets
I can't imagine the circumstances under which you would want to run Co ntrol Panel from your application, since most if not all of the Contro l Panel settings are ignored by DirectInput. However, if you want to r un the applet for a device, it's a simple call:
lpdiKeyboard->RunControlPanel(NULL, 0);
The first argument is the parent window, and the second argument is fo r flags, none of which is so far defined.
Other Input Devices under DirectInput 3.0
Future releases of DirectX will include COM-based services for other i nput devices including the joystick, game pads, and virtual-reality gl oves. At present, however, DirectInput's support for devices other tha n the mouse and keyboard is nominal; in fact, it is available in the s tandard Windows SDK in the form of the extended joystick services cent ered on joyGetPosEx.
The joyGetPosEx function is actually quite powerful, supporting up to 32 buttons, six axes, and a point-of-view hat on digital and analog de vices. It is well documented elsewhere, and I won't discuss it here fu rther than to point you to the useful articles listed below.
Incidentally, the 32-bit joystick driver VJOYD.VXD is improved in Dire ctX 3. Older versions were flawed, and several companies produced fixe s, causing confusion. One problem that has been cleared up is the tend ency for the joystick readings to "jitter" when they happened to coinc ide with DMA (direct memory access) transfers.
-- ※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.96.183.119]
|
|