精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>游戏元素>>● 游戏开发>>制作上路(新手请看)>>入门教程>>图像编程入门>>Mouse&Keyboard with DirectInput

主题:Mouse&Keyboard with DirectInput
发信人: 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]

[关闭][返回]