This 'beautiful' patch I wrote for fixing dinput was silently ignored. Can someone comment about it?
here is a link to it: http://www.winehq.com/hypermail/wine-patches/2002/05/0047.html
Thank you
On Sat, 18 May 2002 arjen@blehq.org wrote:
This 'beautiful' patch I wrote for fixing dinput was silently ignored. Can someone comment about it?
The x11drv parts are unnecessary. It's done differently (more Alexandre-style) in WineX. Since there's no particular need to hold this piece back whether or not the code trade Gav wants to do is successful (especially since you're working on it in any case), I may as well use it to help you get this functionality into Wine. Here's the code merged against ReWind, licensed under the X11 license. You may have to merge it again against the current Wine code yourself, since some other DirectInput patches may not have been applied to ReWind. (Of course, change this in other ways as well if you feel like it.)
Log entry from WineX: Ove Kaaven ovek@transgaming.com Implemented buffered keyboard input properly (using a system hook instead of polling).
Index: dlls/dinput/keyboard/main.c =================================================================== RCS file: /cvsroot/rewind/rewind/dlls/dinput/keyboard/main.c,v retrieving revision 1.8 diff -u -r1.8 main.c --- dlls/dinput/keyboard/main.c 18 Oct 2001 21:30:06 -0000 1.8 +++ dlls/dinput/keyboard/main.c 18 May 2002 23:30:29 -0000 @@ -34,10 +34,16 @@ GUID guid;
IDirectInputAImpl *dinput; - - /* SysKeyboardAImpl */ - BYTE keystate[256]; + + /* SysKeyboardAImpl */ + BYTE keystate[256]; + HHOOK hook; + HWND win; + DWORD dwCoopLevel; + LPDIDEVICEOBJECTDATA data_queue; + int queue_head, queue_tail, queue_len; int acquired; + CRITICAL_SECTION crit; };
static GUID DInput_Wine_Keyboard_GUID = { /* 0ab8648a-7735-11d2-8c73-71df54a96441 */ @@ -47,6 +53,9 @@ {0x8c, 0x73, 0x71, 0xdf, 0x54, 0xa9, 0x64, 0x41} };
+static IDirectInputDevice2A* current_lock = NULL; + + static BOOL keyboarddev_enum_device(DWORD dwDevType, DWORD dwFlags, LPCDIDEVICEINSTANCEA lpddi) { if ((dwDevType == 0) || (dwDevType == DIDEVTYPE_KEYBOARD)) { @@ -70,6 +79,7 @@ newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysKeyboardAImpl)); newDevice->ref = 1; ICOM_VTBL(newDevice) = kvt; + InitializeCriticalSection(&(newDevice->crit)); memcpy(&(newDevice->guid),rguid,sizeof(*rguid)); memset(newDevice->keystate,0,256); newDevice->dinput = dinput; @@ -107,6 +117,50 @@
DECL_GLOBAL_CONSTRUCTOR(keyboarddev_register) { dinput_register_device(&keyboarddev); }
+static ULONG WINAPI SysKeyboardAImpl_Release( + LPDIRECTINPUTDEVICE2A iface +) +{ + ICOM_THIS(SysKeyboardAImpl,iface); + + This->ref--; + if (This->ref) + return This->ref; + + /* Free the data queue */ + if (This->data_queue != NULL) + HeapFree(GetProcessHeap(),0,This->data_queue); + + if (This->hook) { + UnhookWindowsHookEx( This->hook ); + } + DeleteCriticalSection(&(This->crit)); + + HeapFree(GetProcessHeap(),0,This); + return 0; +} + +static HRESULT WINAPI SysKeyboardAImpl_SetCooperativeLevel( + LPDIRECTINPUTDEVICE2A iface,HWND hwnd,DWORD dwflags +) +{ + ICOM_THIS(SysKeyboardAImpl,iface); + + TRACE("(this=%p,0x%08lx,0x%08lx)\n",This,(DWORD)hwnd,dwflags); + + if (TRACE_ON(dinput)) + _dump_cooperativelevel_DI(dwflags); + + /* Store the window which asks for the keyboard */ + if (!hwnd) + hwnd = GetDesktopWindow(); + This->win = hwnd; + This->dwCoopLevel = dwflags; + + return 0; +} + + static HRESULT WINAPI SysKeyboardAImpl_SetProperty( LPDIRECTINPUTDEVICE2A iface,REFGUID rguid,LPCDIPROPHEADER ph ) @@ -115,13 +169,19 @@
TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph); TRACE("(size=%ld,headersize=%ld,obj=%ld,how=%ld\n", - ph->dwSize,ph->dwHeaderSize,ph->dwObj,ph->dwHow); + ph->dwSize,ph->dwHeaderSize,ph->dwObj,ph->dwHow); if (!HIWORD(rguid)) { switch ((DWORD)rguid) { case (DWORD) DIPROP_BUFFERSIZE: { - LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph; - - TRACE("(buffersize=%ld)\n",pd->dwData); + LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph; + + TRACE("buffersize = %ld\n",pd->dwData); + + This->data_queue = (LPDIDEVICEOBJECTDATA)HeapAlloc(GetProcessHeap(),0, + pd->dwData * sizeof(DIDEVICEOBJECTDATA)); + This->queue_head = 0; + This->queue_tail = 0; + This->queue_len = pd->dwData; break; } default: @@ -132,28 +192,64 @@ return 0; }
+static LRESULT CALLBACK dinput_keyboard_hook( int code, WPARAM wparam, LPARAM lparam ) +{ + LRESULT ret; + KBDLLHOOKSTRUCT *hook = (KBDLLHOOKSTRUCT *)lparam; + SysKeyboardAImpl *This = (SysKeyboardAImpl*) current_lock; + DWORD dwCoop; + WORD dik; + + if (code != HC_ACTION) return CallNextHookEx( This->hook, code, wparam, lparam ); + + EnterCriticalSection(&(This->crit)); + dwCoop = This->dwCoopLevel; + + TRACE(" msg %x scan %lx flags %lx\n", + wparam, hook->scanCode, hook->flags ); + + dik = hook->scanCode; + if (hook->flags & LLKHF_EXTENDED) dik |= 0x80; + if (hook->flags & LLKHF_UP) { + if (This->keystate[dik] != 0) { + GEN_EVENT(dik, 0x00, hook->time, This->dinput->evsequence++); + This->keystate[dik] = 0; + } + } + else { + if (This->keystate[dik] == 0) { + GEN_EVENT(dik, 0x80, hook->time, This->dinput->evsequence++); + This->keystate[dik] = 0x80; + } + } + + LeaveCriticalSection(&(This->crit)); + + if (dwCoop & DISCL_NONEXCLUSIVE) + { /* pass the events down to previous handlers (e.g. win32 input) */ + ret = CallNextHookEx( This->hook, code, wparam, lparam ); + } + else ret = 1; /* ignore message */ + return ret; +} + static HRESULT WINAPI SysKeyboardAImpl_GetDeviceState( LPDIRECTINPUTDEVICE2A iface,DWORD len,LPVOID ptr ) { - DWORD i; + ICOM_THIS(SysKeyboardAImpl,iface); + + if (!This->acquired) + return DIERR_NOTACQUIRED;
- memset( ptr, 0, len ); - if (len != 256) - { - WARN("whoops, got len %ld?\n", len); - return DI_OK; - } - for (i = 0; i < 0x80; i++) - { - WORD vkey = MapVirtualKeyA( i, 1 ); - if (vkey && (GetAsyncKeyState( vkey ) & 0x8000)) - { - ((LPBYTE)ptr)[i] = 0x80; - ((LPBYTE)ptr)[i | 0x80] = 0x80; - } - } - return DI_OK; + memset( ptr, 0, len ); + if (len != 256) + { + WARN("whoops, got len %ld?\n", len); + return DI_OK; + } + memcpy(ptr, This->keystate, len); + return DI_OK; }
static HRESULT WINAPI SysKeyboardAImpl_GetDeviceData( @@ -162,34 +258,53 @@ ) { ICOM_THIS(SysKeyboardAImpl,iface); - int i, n; + DWORD len, nqtail; + + EnterCriticalSection(&(This->crit)); + TRACE("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx)\n",This,dodsize,*entries,flags);
- TRACE("(this=%p,%ld,%p,%p(%ld)),0x%08lx)\n", - This,dodsize,dod,entries,entries?*entries:0,flags); + len = ((This->queue_head < This->queue_tail) ? This->queue_len : 0) + + (This->queue_head - This->queue_tail); + if (len > *entries) len = *entries; + + if (dod == NULL) { + if (len) + TRACE("Application discarding %ld event(s).\n", len); + + *entries = len; + nqtail = This->queue_tail + len; + while (nqtail >= This->queue_len) nqtail -= This->queue_len; + } else { + if (dodsize < sizeof(DIDEVICEOBJECTDATA)) { + ERR("Wrong structure size !\n"); + LeaveCriticalSection(&(This->crit)); + return DIERR_INVALIDPARAM; + } + + if (len) + TRACE("Application retrieving %ld event(s).\n", len); + + *entries = 0; + nqtail = This->queue_tail; + while (len) { + DWORD span = ((This->queue_head < nqtail) ? This->queue_len : This->queue_head) + - nqtail; + if (span > len) span = len; + /* Copy the buffered data into the application queue */ + memcpy(dod + *entries, This->data_queue + nqtail, span * dodsize); + /* Advance position */ + nqtail += span; + if (nqtail >= This->queue_len) nqtail -= This->queue_len; + *entries += span; + len -= span; + } + } + if (!(flags & DIGDD_PEEK)) + This->queue_tail = nqtail;
+ LeaveCriticalSection(&(This->crit));
- for (i = n = 0; (i < 0x80) && (n < *entries); i++) - { - WORD state, vkey = MapVirtualKeyA( i, 1 ); - if (!vkey) continue; - state = (GetAsyncKeyState( vkey ) >> 8) & 0x80; - if (state != This->keystate[vkey]) - { - if (dod) - { - /* add an entry */ - dod[n].dwOfs = i; /* scancode */ - dod[n].dwData = state; - dod[n].dwTimeStamp = GetCurrentTime(); /* umm */ - dod[n].dwSequence = This->dinput->evsequence++; - n++; - } - if (!(flags & DIGDD_PEEK)) This->keystate[vkey] = state; - } - } - if (n) TRACE_(dinput)("%d entries\n",n); - *entries = n; - return DI_OK; + return DI_OK; }
static HRESULT WINAPI SysKeyboardAImpl_Acquire(LPDIRECTINPUTDEVICE2A iface) @@ -199,10 +314,27 @@ TRACE("(this=%p)\n",This); if (This->acquired == 0) { - This->acquired = 1; + DWORD i; + + /* Store (in a global variable) the current lock */ + current_lock = (IDirectInputDevice2A*)This; + + /* Install our keyboard hook */ + This->hook = SetWindowsHookExW( WH_KEYBOARD_LL, dinput_keyboard_hook, 0, 0 ); + + /* Read current keyboard state */ + memset(&This->keystate, 0, 256); + for (i = 0; i < 0x100; i++) + { + WORD vkey = MapVirtualKeyA( i, 1 ); /* FIXME: use map mode 3 when implemented */ + if (vkey && (GetAsyncKeyState( vkey ) & 0x8000)) + This->keystate[i] = 0x80; + } + + This->acquired = 1; + return DI_OK; } - - return DI_OK; + return S_FALSE; }
static HRESULT WINAPI SysKeyboardAImpl_Unacquire(LPDIRECTINPUTDEVICE2A iface) @@ -211,9 +343,16 @@ TRACE("(this=%p)\n",This);
if (This->acquired == 1) { - This->acquired = 0; + if (This->hook) { + UnhookWindowsHookEx( This->hook ); + This->hook = 0; + } + + current_lock = NULL; + + This->acquired = 0; } else { - ERR("Unacquiring a not-acquired device !!!\n"); + ERR("Unacquiring a not-acquired device !!!\n"); }
return DI_OK; @@ -254,7 +393,7 @@ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE IDirectInputDevice2AImpl_QueryInterface, IDirectInputDevice2AImpl_AddRef, - IDirectInputDevice2AImpl_Release, + SysKeyboardAImpl_Release, SysKeyboardAImpl_GetCapabilities, IDirectInputDevice2AImpl_EnumObjects, IDirectInputDevice2AImpl_GetProperty, @@ -265,7 +404,7 @@ SysKeyboardAImpl_GetDeviceData, IDirectInputDevice2AImpl_SetDataFormat, IDirectInputDevice2AImpl_SetEventNotification, - IDirectInputDevice2AImpl_SetCooperativeLevel, + SysKeyboardAImpl_SetCooperativeLevel, IDirectInputDevice2AImpl_GetObjectInfo, IDirectInputDevice2AImpl_GetDeviceInfo, IDirectInputDevice2AImpl_RunControlPanel,
On Sun, 19 May 2002, Ove Kaaven wrote:
On Sat, 18 May 2002 arjen@blehq.org wrote:
This 'beautiful' patch I wrote for fixing dinput was silently ignored. Can someone comment about it?
The x11drv parts are unnecessary. It's done differently (more Alexandre-style) in WineX. Since there's no particular need to hold this piece back whether or not the code trade Gav wants to do is successful (especially since you're working on it in any case), I may as well use it to help you get this functionality into Wine. Here's the code merged against ReWind, licensed under the X11 license. You may have to merge it again against the current Wine code yourself, since some other DirectInput patches may not have been applied to ReWind. (Of course, change this in other ways as well if you feel like it.)
Thank you, I guess. I don't want to get involved in any politics. I don't really know anything about other trees.
I think the hook is agood idea.
I think the best thing to do now is to get best out of the two different implementations. I have some comments about your code, and I can improve mine with yours. I'll make a new patch from these two, and send it later. If anybody has any other comments, I would like to hear them.
- /* SysKeyboardAImpl */
- BYTE keystate[256];
There is not a real reason to get a keystate per keyboard, as there is only one keyboard. I made this global. Is that OK?
static HRESULT WINAPI SysKeyboardAImpl_SetProperty( LPDIRECTINPUTDEVICE2A iface,REFGUID rguid,LPCDIPROPHEADER ph ) @@ -115,13 +169,19 @@
TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph); TRACE("(size=%ld,headersize=%ld,obj=%ld,how=%ld\n",
ph->dwSize,ph->dwHeaderSize,ph->dwObj,ph->dwHow);
if (!HIWORD(rguid)) { switch ((DWORD)rguid) { case (DWORD) DIPROP_BUFFERSIZE: {ph->dwSize,ph->dwHeaderSize,ph->dwObj,ph->dwHow);
LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
TRACE("(buffersize=%ld)\n",pd->dwData);
LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
TRACE("buffersize = %ld\n",pd->dwData);
This->data_queue = (LPDIDEVICEOBJECTDATA)HeapAlloc(GetProcessHeap(),0,
pd->dwData * sizeof(DIDEVICEOBJECTDATA));
How do you know the data_queue is free (NULL)? My patch made sure that: !Acquired => (data_queue == NULL) And setproperty can only be invoked when !Acquired.
static HRESULT WINAPI SysKeyboardAImpl_GetDeviceState( LPDIRECTINPUTDEVICE2A iface,DWORD len,LPVOID ptr ) {
- DWORD i;
- ICOM_THIS(SysKeyboardAImpl,iface);
- if (!This->acquired)
return DIERR_NOTACQUIRED;
I have this game that does not acquire the device. Guess what? It works with windows.
- memset( ptr, 0, len );
- if (len != 256)
- {
WARN("whoops, got len %ld?\n", len);
return DI_OK;
- }
- for (i = 0; i < 0x80; i++)
- {
WORD vkey = MapVirtualKeyA( i, 1 );
if (vkey && (GetAsyncKeyState( vkey ) & 0x8000))
{
((LPBYTE)ptr)[i] = 0x80;
((LPBYTE)ptr)[i | 0x80] = 0x80;
}
- }
- return DI_OK;
- memset( ptr, 0, len );
- if (len != 256)
- {
WARN("whoops, got len %ld?\n", len);
return DI_OK;
DI_OK?
- }
- memcpy(ptr, This->keystate, len);
- return DI_OK;
}
static HRESULT WINAPI SysKeyboardAImpl_GetDeviceData( @@ -162,34 +258,53 @@ ) { ICOM_THIS(SysKeyboardAImpl,iface);
- int i, n;
- DWORD len, nqtail;
- EnterCriticalSection(&(This->crit));
- TRACE("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx)\n",This,dodsize,*entries,flags);
- TRACE("(this=%p,%ld,%p,%p(%ld)),0x%08lx)\n",
This,dodsize,dod,entries,entries?*entries:0,flags);
- len = ((This->queue_head < This->queue_tail) ? This->queue_len : 0)
+ (This->queue_head - This->queue_tail);
- if (len > *entries) len = *entries;
- if (dod == NULL) {
if (len)
TRACE("Application discarding %ld event(s).\n", len);
*entries = len;
nqtail = This->queue_tail + len;
while (nqtail >= This->queue_len) nqtail -= This->queue_len;
- } else {
if (dodsize < sizeof(DIDEVICEOBJECTDATA)) {
ERR("Wrong structure size !\n");
LeaveCriticalSection(&(This->crit));
return DIERR_INVALIDPARAM;
}
if (len)
TRACE("Application retrieving %ld event(s).\n", len);
*entries = 0;
nqtail = This->queue_tail;
while (len) {
DWORD span = ((This->queue_head < nqtail) ? This->queue_len : This->queue_head)
If dodsize > 16 then span = 1, but I don't really get the algorithm. Do you know, that most of the time there is not more than one entry in the buffer. I once read a peace about UNIX and algorithms and N being small... Well, never mind.
- nqtail;
if (span > len) span = len;
/* Copy the buffered data into the application queue */
memcpy(dod + *entries, This->data_queue + nqtail, span * dodsize);
/* Advance position */
nqtail += span;
if (nqtail >= This->queue_len) nqtail -= This->queue_len;
*entries += span;
len -= span;
}
}
if (!(flags & DIGDD_PEEK))
This->queue_tail = nqtail;
LeaveCriticalSection(&(This->crit));
return DI_OK;
}
I think my implementation is cleaner.
static HRESULT WINAPI SysKeyboardAImpl_Acquire(LPDIRECTINPUTDEVICE2A iface) @@ -199,10 +314,27 @@ TRACE("(this=%p)\n",This);
if (This->acquired == 0) {
This->acquired = 1;
DWORD i;
/* Store (in a global variable) the current lock */
current_lock = (IDirectInputDevice2A*)This;
/* Install our keyboard hook */
This->hook = SetWindowsHookExW( WH_KEYBOARD_LL, dinput_keyboard_hook, 0, 0 );
/* Read current keyboard state */
memset(&This->keystate, 0, 256);
for (i = 0; i < 0x100; i++)
{
WORD vkey = MapVirtualKeyA( i, 1 ); /* FIXME: use map mode 3 when implemented */
if (vkey && (GetAsyncKeyState( vkey ) & 0x8000))
This does not work (see earlier mails). but it isn't needed when keystate is global.
This->keystate[i] = 0x80;
}
This->acquired = 1;
}return DI_OK;
- return DI_OK;
- return S_FALSE;
}
Could you send me the file instead of the patch, as I don't really know what to do with this patch.
To get involved in politics anyway: you can have the changes I make to this patch.
On Sun, 19 May 2002 arjen@blehq.org wrote:
- /* SysKeyboardAImpl */
- BYTE keystate[256];
There is not a real reason to get a keystate per keyboard, as there is only one keyboard. I made this global. Is that OK?
Probably, current_lock is global anyway.
How do you know the data_queue is free (NULL)?
The SetProperty code was just copied from mouse/main.c, so if there's a bug in it, it's there too.
I have this game that does not acquire the device. Guess what? It works with windows.
Hmm. Guess the docs don't tell the whole truth then, as usual.
DI_OK?
Right. dinput.h defines it as:
#define DI_OK S_OK
so it's the canonical return value from DirectInput methods.
*entries = 0;
nqtail = This->queue_tail;
while (len) {
DWORD span = ((This->queue_head < nqtail) ? This->queue_len : This->queue_head)
If dodsize > 16 then span = 1, but I don't really get the algorithm.
This is also copied from mouse/main.c and works there. The algorithm is simply about determining how much can be copied with a single memcpy, taking in account buffer wraparound and stuff. Here, it determines whether it can copy all the way to the queue head immediately, or stop at the end of the buffer first, before wrapping around to the beginning of it.
- nqtail;
if (span > len) span = len;
This->hook = SetWindowsHookExW( WH_KEYBOARD_LL, dinput_keyboard_hook, 0, 0 );
/* Read current keyboard state */
memset(&This->keystate, 0, 256);
for (i = 0; i < 0x100; i++)
{
WORD vkey = MapVirtualKeyA( i, 1 ); /* FIXME: use map mode 3 when implemented */
if (vkey && (GetAsyncKeyState( vkey ) & 0x8000))
This does not work (see earlier mails). but it isn't needed when keystate is global.
It works in WineX since map mode 3 is actually implemented there. I may be able to submit the patch that implements it if you want, it's not a very big patch.
Could you send me the file instead of the patch, as I don't really know what to do with this patch.
OK.
To get involved in politics anyway: you can have the changes I make to this patch.
Thanks.
Ove Kaaven wrote:
The SetProperty code was just copied from mouse/main.c, so if there's a bug in it, it's there too.
I can fix that later.
DI_OK?
Right. dinput.h defines it as:
#define DI_OK S_OK
so it's the canonical return value from DirectInput methods.
DI_NOTOK whould be better.
*entries = 0;
nqtail = This->queue_tail;
while (len) {
DWORD span = ((This->queue_head < nqtail) ? This->queue_len : This->queue_head)
If dodsize > 16 then span = 1, but I don't really get the algorithm.
This is also copied from mouse/main.c and works there. The algorithm is simply about determining how much can be copied with a single memcpy, taking in account buffer wraparound and stuff. Here, it determines whether it can copy all the way to the queue head immediately, or stop at the end of the buffer first, before wrapping around to the beginning of it.
It is not possible to use memcopy when dodsize != sizeof(DeviceObjectData).
This->hook = SetWindowsHookExW( WH_KEYBOARD_LL, dinput_keyboard_hook, 0, 0 );
/* Read current keyboard state */
memset(&This->keystate, 0, 256);
for (i = 0; i < 0x100; i++)
{
WORD vkey = MapVirtualKeyA( i, 1 ); /* FIXME: use map mode 3 when implemented */
if (vkey && (GetAsyncKeyState( vkey ) & 0x8000))
This does not work (see earlier mails). but it isn't needed when keystate is global.
It works in WineX since map mode 3 is actually implemented there. I may be able to submit the patch that implements it if you want, it's not a very big patch.
Is map mode mode 3 not dependant on the NumLock state then? DInput isn't. But on the other hand, it only mathers when someone acquires the device while a key is pressed.
Well here is my new patch. I only used the Hook idea and the critical sections. It is better now, and shorter.
? paard Index: dlls/dinput/dinput.spec =================================================================== RCS file: /home/wine/wine/dlls/dinput/dinput.spec,v retrieving revision 1.8 diff -u -r1.8 dinput.spec --- dlls/dinput/dinput.spec 14 May 2002 20:54:59 -0000 1.8 +++ dlls/dinput/dinput.spec 19 May 2002 13:01:43 -0000 @@ -1,4 +1,5 @@ name dinput +init Init
@ stdcall DirectInputCreateA(long long ptr ptr) DirectInputCreateA @ stub DirectInputCreateW Index: dlls/dinput/dinput_main.c =================================================================== RCS file: /home/wine/wine/dlls/dinput/dinput_main.c,v retrieving revision 1.25 diff -u -r1.25 dinput_main.c --- dlls/dinput/dinput_main.c 9 Mar 2002 23:29:36 -0000 1.25 +++ dlls/dinput/dinput_main.c 19 May 2002 13:01:43 -0000 @@ -38,6 +38,7 @@
#include "wine/debug.h" #include "winbase.h" +#include "winuser.h" #include "winerror.h" #include "windef.h" #include "dinput_private.h" @@ -51,6 +52,21 @@ #define MAX_WINE_DINPUT_DEVICES 4 static dinput_device * dinput_devices[MAX_WINE_DINPUT_DEVICES]; static int nrof_dinput_devices = 0; + +BOOL WINAPI Init( HINSTANCE inst, DWORD reason, LPVOID reserv) +{ + switch(reason) + { + case DLL_PROCESS_ATTACH: + keyboard_hook = SetWindowsHookExW( WH_KEYBOARD_LL, KeyboardCallback, 0, 0 ); + break; + case DLL_PROCESS_DETACH: + UnhookWindowsHookEx(keyboard_hook); + break; + } + return TRUE; +} +
/* register a direct draw driver. We better not use malloc for we are in * the ELF startup initialisation at this point. Index: dlls/dinput/dinput_private.h =================================================================== RCS file: /home/wine/wine/dlls/dinput/dinput_private.h,v retrieving revision 1.3 diff -u -r1.3 dinput_private.h --- dlls/dinput/dinput_private.h 9 Mar 2002 23:29:36 -0000 1.3 +++ dlls/dinput/dinput_private.h 19 May 2002 13:01:44 -0000 @@ -42,4 +42,8 @@
extern void dinput_register_device(dinput_device *device) ;
+HHOOK keyboard_hook; + +LRESULT CALLBACK KeyboardCallback( int code, WPARAM wparam, LPARAM lparam ); + #endif /* __WINE_DLLS_DINPUT_DINPUT_PRIVATE_H */ Index: dlls/dinput/keyboard/main.c =================================================================== RCS file: /home/wine/wine/dlls/dinput/keyboard/main.c,v retrieving revision 1.10 diff -u -r1.10 main.c --- dlls/dinput/keyboard/main.c 7 May 2002 01:49:19 -0000 1.10 +++ dlls/dinput/keyboard/main.c 19 May 2002 13:01:44 -0000 @@ -49,13 +49,83 @@ IDirectInputAImpl *dinput; HANDLE hEvent; - HHOOK hook; /* SysKeyboardAImpl */ - BYTE keystate[256]; int acquired; + int buffersize; /* set in 'SetProperty' */ + LPDIDEVICEOBJECTDATA buffer; /* buffer for 'GetDeviceData'. + Alloc at 'Acquire', Free at + 'Unacquire' */ + int count; /* number of objects in use in + 'buffer' */ + int start; /* 'buffer' rotates. This is the + first in use (if count > 0) */ + BOOL overflow; /* return DI_BUFFEROVERFLOW in + 'GetDeviceData' */ + CRITICAL_SECTION crit; };
-static SysKeyboardAImpl* current_lock = NULL; +SysKeyboardAImpl *current; /* Today's acquired device +FIXME: currently this can be only one. +Maybe this should be a linked list or st. +I don't know what the rules are for multiple acquired keyboards, +but 'DI_LOSTFOCUS' and 'DI_UNACQUIRED' exist for a reason. +*/ + +static BYTE DInputKeyState[256]; /* array for 'GetDeviceState' */ + +HHOOK keyboard_hook; + +LRESULT CALLBACK KeyboardCallback( int code, WPARAM wparam, LPARAM lparam ) +{ + if (code == HC_ACTION) + { + BYTE dik_code; + BOOL down; + DWORD timestamp; + + { + KBDLLHOOKSTRUCT *hook = (KBDLLHOOKSTRUCT *)lparam; + dik_code = hook->scanCode; + if (hook->flags & LLKHF_EXTENDED) dik_code |= 0x80; + down = !(hook->flags & LLKHF_UP); + timestamp = hook->time; + } + + DInputKeyState[dik_code] = (down ? 0x80 : 0); + + if (current != NULL) + { + if (current->hEvent) + SetEvent(current->hEvent); + + if (current->buffer != NULL) + { + int n; + + EnterCriticalSection(&(current->crit)); + + n = (current->start + current->count) % current->buffersize; + + current->buffer[n].dwOfs = dik_code; + current->buffer[n].dwData = down ? 0x80 : 0; + current->buffer[n].dwTimeStamp = timestamp; + current->buffer[n].dwSequence = current->dinput->evsequence++; + + if (current->count == current->buffersize) + { + current->start++; + current->overflow = TRUE; + } + else + current->count++; + + LeaveCriticalSection(&(current->crit)); + } + } + } + + return CallNextHookEx(keyboard_hook, code, wparam, lparam); +}
static GUID DInput_Wine_Keyboard_GUID = { /* 0ab8648a-7735-11d2-8c73-71df54a96441 */ 0x0ab8648a, @@ -88,7 +158,6 @@ newDevice->ref = 1; ICOM_VTBL(newDevice) = kvt; memcpy(&(newDevice->guid),rguid,sizeof(*rguid)); - memset(newDevice->keystate,0,256); newDevice->dinput = dinput;
return newDevice; @@ -139,6 +208,12 @@ LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
TRACE("(buffersize=%ld)\n",pd->dwData); + + if (This->acquired) + return DIERR_INVALIDPARAM; + + This->buffersize = pd->dwData; + break; } default: @@ -153,23 +228,11 @@ LPDIRECTINPUTDEVICE2A iface,DWORD len,LPVOID ptr ) { - DWORD i; - - memset( ptr, 0, len ); + /* Note: device does not need to be acquired */ if (len != 256) - { - WARN("whoops, got len %ld?\n", len); - return DI_OK; - } - for (i = 0; i < 0x80; i++) - { - WORD vkey = MapVirtualKeyA( i, 1 ); - if (vkey && (GetAsyncKeyState( vkey ) & 0x8000)) - { - ((LPBYTE)ptr)[i] = 0x80; - ((LPBYTE)ptr)[i | 0x80] = 0x80; - } - } + return DIERR_INVALIDPARAM; + + memcpy(ptr, DInputKeyState, 256); return DI_OK; }
@@ -179,55 +242,86 @@ ) { ICOM_THIS(SysKeyboardAImpl,iface); - int i, n; + int ret = DI_OK, i = 0;
TRACE("(this=%p,%ld,%p,%p(%ld)),0x%08lx)\n", This,dodsize,dod,entries,entries?*entries:0,flags);
+ if (This->buffer == NULL) + return DIERR_NOTBUFFERED;
- for (i = n = 0; (i < 0x80) && (n < *entries); i++) - { - WORD state, vkey = MapVirtualKeyA( i, 1 ); - if (!vkey) continue; - state = (GetAsyncKeyState( vkey ) >> 8) & 0x80; - if (state != This->keystate[vkey]) - { - if (dod) - { - /* add an entry */ - dod[n].dwOfs = i; /* scancode */ - dod[n].dwData = state; - dod[n].dwTimeStamp = GetCurrentTime(); /* umm */ - dod[n].dwSequence = This->dinput->evsequence++; - n++; - } - if (!(flags & DIGDD_PEEK)) This->keystate[vkey] = state; - } - } - if (n) TRACE_(dinput)("%d entries\n",n); - *entries = n; - return DI_OK; -} + if (dodsize < sizeof(*dod)) + return DIERR_INVALIDPARAM;
-static LRESULT CALLBACK dinput_keyboard_hook(int code, WPARAM wparam, LPARAM lparam) -{ - SysKeyboardAImpl *This = current_lock; - if (This && This->hEvent) - SetEvent(This->hEvent); - return 1; + EnterCriticalSection(&(This->crit)); + + /* Copy item at a time for the case dodsize > sizeof(buffer[n]) */ + while ((i < *entries || *entries == INFINITE) && i < This->count) + { + if (dod != NULL) + { + int n = (This->start + i) % This->buffersize; + LPDIDEVICEOBJECTDATA pd + = (LPDIDEVICEOBJECTDATA)((BYTE *)dod + dodsize * i); + pd->dwOfs = This->buffer[n].dwOfs; + pd->dwData = This->buffer[n].dwData; + pd->dwTimeStamp = This->buffer[n].dwTimeStamp; + pd->dwSequence = This->buffer[n].dwSequence; + } + i++; + } + + *entries = i; + + if (This->overflow) + ret = DI_BUFFEROVERFLOW; + + if (!(flags & DIGDD_PEEK)) + { + /* Empty buffer */ + This->count -= i; + This->start = (This->start + i) % This->buffersize; + This->overflow = FALSE; + } + + LeaveCriticalSection(&(This->crit)); + + return ret; }
+static HRESULT WINAPI SysKeyboardAImpl_Unacquire(LPDIRECTINPUTDEVICE2A iface); + static HRESULT WINAPI SysKeyboardAImpl_Acquire(LPDIRECTINPUTDEVICE2A iface) { ICOM_THIS(SysKeyboardAImpl,iface); TRACE("(this=%p)\n",This); - if (This->acquired == 0) { - This->acquired = 1; - } - - This->hook = SetWindowsHookExW(WH_KEYBOARD, dinput_keyboard_hook, 0, 0); + if (This->acquired) + return S_FALSE; + + This->acquired = 1; + + if (current != NULL) + { + FIXME("Not more than one keyboard can be acquired at the same time."); + SysKeyboardAImpl_Unacquire(iface); + } + + current = This; + + if (This->buffersize > 0) + { + This->buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + This->buffersize * sizeof(*(This->buffer))); + This->start = 0; + This->count = 0; + This->overflow = FALSE; + InitializeCriticalSection(&(This->crit)); + } + else + This->buffer = NULL; + return DI_OK; }
@@ -236,19 +330,26 @@ ICOM_THIS(SysKeyboardAImpl,iface); TRACE("(this=%p)\n",This);
- if (This->acquired == 1) { - This->acquired = 0; - UnhookWindowsHookEx( This->hook ); - } else { - ERR("Unacquiring a not-acquired device !!!\n"); - } + if (This->acquired == 0) + return DI_NOEFFECT; + + if (current == This) + current = NULL; + else + ERR("this != current"); + + This->acquired = 0; + + if (This->buffersize >= 0) + { + HeapFree(GetProcessHeap(), 0, This->buffer); + This->buffer = NULL; + DeleteCriticalSection(&(This->crit)); + }
return DI_OK; }
-/****************************************************************************** - * GetCapabilities : get the device capablitites - */ static HRESULT WINAPI SysKeyboardAImpl_SetEventNotification(LPDIRECTINPUTDEVICE2A iface, HANDLE hnd) { ICOM_THIS(SysKeyboardAImpl,iface); @@ -256,10 +357,12 @@ TRACE("(this=%p,0x%08lx)\n",This,(DWORD)hnd);
This->hEvent = hnd; - current_lock = This; return DI_OK; }
+/****************************************************************************** + * GetCapabilities : get the device capablitites + */ static HRESULT WINAPI SysKeyboardAImpl_GetCapabilities( LPDIRECTINPUTDEVICE2A iface, LPDIDEVCAPS lpDIDevCaps)
On Sun, 19 May 2002, Arjen Nienhuis wrote:
Right. dinput.h defines it as:
#define DI_OK S_OK
so it's the canonical return value from DirectInput methods.
DI_NOTOK whould be better.
Oh, is that what you meant. Yeah, I suppose it should have returned something like that, but the original code didn't, and I wasn't trying to overhaul the code beyond what was necessary to get the job done.
It is not possible to use memcopy when dodsize != sizeof(DeviceObjectData).
Hmm. Right. The < sanity check for dodsize should probably have been a != check. Not sure where it came from.
It works in WineX since map mode 3 is actually implemented there. I may be able to submit the patch that implements it if you want, it's not a very big patch.
Is map mode mode 3 not dependant on the NumLock state then? DInput isn't. But on the other hand, it only mathers when someone acquires the device while a key is pressed.
The problem I wanted to solve with map mode 3 was distinguishing left and right shift keys. I'm not sure about numlock.
Well here is my new patch. I only used the Hook idea and the critical sections. It is better now, and shorter.
OK, maybe the WineX code will be replaced with your code eventually, then.