The MapVirtualKey in GetDeviceState is not "the way to do it". It does not map VK_?? to DIK_??. I don't know why it does work most of the time. "ptr[i] = 0x80; ptr[i + 0x80] = 0x80;" gave double key events in a game I have.
The correct way to convert the keys is by this table (attachment). I made this table by pressing all the keys on my keyboard, and record the keycodes.
The code I wrote for this function is:
static HRESULT WINAPI SysKeyboardAImpl_GetDeviceState( LPDIRECTINPUTDEVICE2A iface, DWORD len, LPVOID ptr ) { DWORD i; memset( ptr, 0, len ); if (len != 256) { WARN("whoops, got len %ld?\n", len); return DI_OK; }
for (i = 0; i < (sizeof(vk_to_dik) / sizeof(vk_to_dik[0])); i++) if (GetAsyncKeyState( vk_to_dik[i][0] ) & 0x8000) ((LPBYTE)ptr)[ vk_to_dik[i][1] ] = 0x80;
return DI_OK; }
Is there an array_size() macro in wine? where should I put this table? Any other comments about my first usefull (at least for me) wine code?
unsigned char vk_to_dik[][2] = { {VK_BACK, DIK_BACK}, {VK_TAB, DIK_TAB}, {VK_CLEAR, DIK_NUMPAD5}, /* Num Lock off */ {VK_RETURN, DIK_NUMPADENTER}, {VK_SHIFT, DIK_LSHIFT}, {VK_CONTROL, DIK_LCONTROL}, {VK_MENU, DIK_LMENU}, {VK_CAPITAL, DIK_CAPITAL}, {VK_ESCAPE, DIK_ESCAPE}, {VK_SPACE, DIK_SPACE}, {VK_PRIOR, DIK_PRIOR}, {VK_NEXT, DIK_NEXT}, {VK_END, DIK_END}, {VK_HOME, DIK_HOME}, {VK_LEFT, DIK_LEFT}, {VK_UP, DIK_UP}, {VK_RIGHT, DIK_RIGHT}, {VK_DOWN, DIK_DOWN}, {VK_INSERT, DIK_INSERT}, {VK_DELETE, DIK_DELETE}, {VK_0, DIK_0}, {VK_1, DIK_1}, {VK_2, DIK_2}, {VK_3, DIK_3}, {VK_4, DIK_4}, {VK_5, DIK_5}, {VK_6, DIK_6}, {VK_7, DIK_7}, {VK_8, DIK_8}, {VK_9, DIK_9}, {VK_A, DIK_A}, {VK_B, DIK_B}, {VK_C, DIK_C}, {VK_D, DIK_D}, {VK_E, DIK_E}, {VK_F, DIK_F}, {VK_G, DIK_G}, {VK_H, DIK_H}, {VK_I, DIK_I}, {VK_J, DIK_J}, {VK_K, DIK_K}, {VK_L, DIK_L}, {VK_M, DIK_M}, {VK_N, DIK_N}, {VK_O, DIK_O}, {VK_P, DIK_P}, {VK_Q, DIK_Q}, {VK_R, DIK_R}, {VK_S, DIK_S}, {VK_T, DIK_T}, {VK_U, DIK_U}, {VK_V, DIK_V}, {VK_W, DIK_W}, {VK_X, DIK_X}, {VK_Y, DIK_Y}, {VK_Z, DIK_Z}, {VK_LWIN, DIK_LWIN}, {VK_RWIN, DIK_RWIN}, {VK_APPS, DIK_APPS}, {VK_NUMPAD0, DIK_NUMPAD0}, {VK_NUMPAD1, DIK_NUMPAD1}, {VK_NUMPAD2, DIK_NUMPAD2}, {VK_NUMPAD3, DIK_NUMPAD3}, {VK_NUMPAD4, DIK_NUMPAD4}, {VK_NUMPAD5, DIK_NUMPAD5}, /* Num Lock on */ {VK_NUMPAD6, DIK_NUMPAD6}, {VK_NUMPAD7, DIK_NUMPAD7}, {VK_NUMPAD8, DIK_NUMPAD8}, {VK_NUMPAD9, DIK_NUMPAD9}, {VK_MULTIPLY, DIK_MULTIPLY}, {VK_ADD, DIK_ADD}, {VK_SUBTRACT, DIK_SUBTRACT}, {VK_DECIMAL, DIK_DECIMAL}, {VK_DIVIDE, DIK_DIVIDE}, {VK_F1, DIK_F1}, {VK_F2, DIK_F2}, {VK_F3, DIK_F3}, {VK_F4, DIK_F4}, {VK_F5, DIK_F5}, {VK_F6, DIK_F6}, {VK_F7, DIK_F7}, {VK_F8, DIK_F8}, {VK_F9, DIK_F9}, {VK_F10, DIK_F10}, {VK_F11, DIK_F11}, {VK_F12, DIK_F12}, {VK_NUMLOCK, DIK_NUMLOCK}, {VK_SCROLL, DIK_SCROLL}, {VK_OEM_1, DIK_SEMICOLON}, {VK_OEM_PLUS, DIK_EQUALS}, {VK_OEM_COMMA, DIK_COMMA}, {VK_OEM_MINUS, DIK_MINUS}, {VK_OEM_PERIOD, DIK_PERIOD}, {VK_OEM_2, DIK_SLASH}, {VK_OEM_3, DIK_GRAVE}, {VK_OEM_4, DIK_LBRACKET}, {VK_OEM_5, DIK_BACKSLASH}, {VK_OEM_6, DIK_RBRACKET}, {VK_OEM_7, DIK_APOSTROPHE} };
On Fri, 22 Feb 2002, Arjen Nienhuis wrote:
The MapVirtualKey in GetDeviceState is not "the way to do it". It does not map VK_?? to DIK_??. I don't know why it does work most of the time.
It works because DIK_* is (at least up to 0x53, as far as I can see) identical to the PC keyboard scancodes, and MapVirtualKey handles scancodes perfectly well. I really don't think this is a coincidence, I think this is so that Microsoft's DirectInput do *not* need the kind of mapping table you propose. If there's a problem with the scancode conversion, you should fix it in MapVirtualKey. I think you may want to keep in mind that the scancode tables in the keyboard code denote extended codes with e.g. 0x138 (0x100 + 0x38), while the DIK for that key is 0xB8 (0x80 + 0x38). Perhaps the keyboard scancode tables could be changed to be compatible with the DIK codes without losing any functionality.
The correct way to convert the keys is by this table (attachment). I made this table by pressing all the keys on my keyboard, and record the keycodes.
Then that table would depend on the keyboard layout (country, language, etc). It should be enough to keep the keyboard layout table mess in *one* place in Wine (in the keyboard code, where MapVirtualKey resides), not two.
Ove Kaaven wrote:
On Fri, 22 Feb 2002, Arjen Nienhuis wrote:
The MapVirtualKey in GetDeviceState is not "the way to do it". It does not map VK_?? to DIK_??. I don't know why it does work most of the time.
It works because DIK_* is (at least up to 0x53, as far as I can see) identical to the PC keyboard scancodes, and MapVirtualKey handles scancodes perfectly well. I really don't think this is a coincidence, I think this is so that Microsoft's DirectInput do *not* need the kind of mapping table you propose. If there's a problem with the scancode conversion, you should fix it in MapVirtualKey. I think you may want to keep in mind that the scancode tables in the keyboard code denote extended codes with e.g. 0x138 (0x100 + 0x38), while the DIK for that key is 0xB8 (0x80 + 0x38). Perhaps the keyboard scancode tables could be changed to be compatible with the DIK codes without losing any functionality.
The problem with
152: ((LPBYTE)ptr)[i] = 0x80; 153: ((LPBYTE)ptr)[i | 0x80] = 0x80;
is that it breaks a program I have: It moves the cursor two positions on a key press. When I checked the MapVirtualKeyA( i, 1 ) results in windows, I noticed that it wasn't the direct mapping between VK and DIK. Also notice, that to check if the DIK_NUMPAD5 key is pressed it is necesary to check both VK_CLEAR and VK_NUMPAD5.
Then that table would depend on the keyboard layout (country, language, etc). It should be enough to keep the keyboard layout table mess in *one* place in Wine (in the keyboard code, where MapVirtualKey resides), not two.
I don't think this table is dependant on the keyboard layout. I think (VK_5 <=> DIK_5) is also true in german or french. The only buttons that might be a problem are:
{VK_OEM_1, DIK_SEMICOLON}, {VK_OEM_2, DIK_SLASH}, {VK_OEM_3, DIK_GRAVE}, {VK_OEM_4, DIK_LBRACKET}, {VK_OEM_5, DIK_BACKSLASH}, {VK_OEM_6, DIK_RBRACKET}, {VK_OEM_7, DIK_APOSTROPHE}
but I don't know if those are dependant on the keyboard layout. Do you?
I also found out that the Microsoft code for DirectInput doesn't use GetAsyncKeyState, as there is a bug in that function, and not in DI.
On Fri, 22 Feb 2002, Arjen Nienhuis wrote:
The problem with
152: ((LPBYTE)ptr)[i] = 0x80; 153: ((LPBYTE)ptr)[i | 0x80] = 0x80;
Yes, that is obviously wrong, but certainly not because of lack of DIK mapping. If the user presses UP, this code would set both the numpad UP (DIK_NUMPAD8, 0x48) and the gray UP (DIK_UP, 0xC8). The fact that this does cause a double keypress is another sign that it's all just scancodes (that MapVirtualKey handles)...
is that it breaks a program I have: It moves the cursor two positions on a key press. When I checked the MapVirtualKeyA( i, 1 ) results in windows, I noticed that it wasn't the direct mapping between VK and DIK.
True, it's a bit complicated, but your table doesn't improve that, you'd pretty much just duplicate MapVirtualKey's table.
I don't think this table is dependant on the keyboard layout. I think (VK_5 <=> DIK_5) is also true in german or french. The only buttons that might be a problem are:
{VK_OEM_1, DIK_SEMICOLON}, {VK_OEM_2, DIK_SLASH}, {VK_OEM_3, DIK_GRAVE}, {VK_OEM_4, DIK_LBRACKET}, {VK_OEM_5, DIK_BACKSLASH}, {VK_OEM_6, DIK_RBRACKET}, {VK_OEM_7, DIK_APOSTROPHE}
but I don't know if those are dependant on the keyboard layout. Do you?
I think they are.
I also found out that the Microsoft code for DirectInput doesn't use GetAsyncKeyState, as there is a bug in that function, and not in DI.
Yes, Microsoft does it differently than Wine, but in the name of clean DLL separation, the Microsoft way isn't currently possible in Wine. It should be possible to fix that, but it would take some effort.
but I don't know if those are dependant on the keyboard layout. Do you?
I think they are.
You are right. Whit a different codepage DIK_BACKSLASH is not a backslash at all.
What do you think of this?
--- wine/dlls/dinput/keyboard/main.c Thu Oct 18 23:30:06 2001 +++ main.c Sat Feb 23 23:07:52 2002 @@ -132,6 +132,51 @@ return 0; }
+/* use MapVirtualKey for dik < 0x80 + for dik >= 0x80 use this table. + MapVirtualKey returns the wrong values for the numpad, + so those are here also. +*/ +struct { + BYTE dik; + BYTE vk; +} dik_to_vk[] = { + {DIK_NUMPAD7, VK_NUMPAD7}, + {DIK_NUMPAD8, VK_NUMPAD8}, + {DIK_NUMPAD9, VK_NUMPAD9}, + {DIK_SUBTRACT, VK_SUBTRACT}, + {DIK_NUMPAD4, VK_NUMPAD4}, + {DIK_NUMPAD5, VK_NUMPAD5}, /* Check both */ + {DIK_NUMPAD5, VK_CLEAR}, /* Num Lock off */ + {DIK_NUMPAD6, VK_NUMPAD6}, + {DIK_ADD, VK_ADD}, + {DIK_NUMPAD1, VK_NUMPAD1}, + {DIK_NUMPAD2, VK_NUMPAD2}, + {DIK_NUMPAD3, VK_NUMPAD3}, + {DIK_NUMPAD0, VK_NUMPAD0}, + {DIK_DECIMAL, VK_DECIMAL}, + {0x56, VK_OEM_102}, + {DIK_F11, VK_F11}, + {DIK_F12, VK_F12}, + {0x5E, 0x5E}, + {0x5F, 0x5F}, + {DIK_NUMPADENTER, VK_RETURN}, + {DIK_DIVIDE, VK_DIVIDE}, + {DIK_HOME, VK_HOME}, + {DIK_UP, VK_UP}, + {DIK_PRIOR, VK_PRIOR}, + {DIK_LEFT, VK_LEFT}, + {DIK_RIGHT, VK_RIGHT}, + {DIK_END, VK_END}, + {DIK_DOWN, VK_DOWN}, + {DIK_NEXT, VK_NEXT}, + {DIK_INSERT, VK_INSERT}, + {DIK_DELETE, VK_DELETE}, + {DIK_LWIN, VK_LWIN}, + {DIK_RWIN, VK_RWIN}, + {DIK_APPS, VK_APPS} +}; + static HRESULT WINAPI SysKeyboardAImpl_GetDeviceState( LPDIRECTINPUTDEVICE2A iface,DWORD len,LPVOID ptr ) @@ -144,13 +189,19 @@ WARN("whoops, got len %ld?\n", len); return DI_OK; } - for (i = 0; i < 0x80; i++) + for (i = 0; i < DIK_NUMPAD7; i++) { WORD vkey = MapVirtualKeyA( i, 1 ); if (vkey && (GetAsyncKeyState( vkey ) & 0x8000)) { ((LPBYTE)ptr)[i] = 0x80; - ((LPBYTE)ptr)[i | 0x80] = 0x80; + } + } + for (i = 0; i < (sizeof(dik_to_vk) / sizeof(dik_to_vk[0])); i++) + { + if (GetAsyncKeyState( dik_to_vk[i].vk ) & 0x8000) + { + ((LPBYTE)ptr)[dik_to_vk[i].dik] = 0x80; } } return DI_OK;
On Sat, 23 Feb 2002, Arjen Nienhuis wrote:
+/* use MapVirtualKey for dik < 0x80
- for dik >= 0x80 use this table.
Well, better. I still think it might be better to make MapVirtualKey handle codes above 0x80 itself, but I'm not sure.
By the way, the problems we have with MapVirtualKey not having an one-to-one mapping might be solved by implementing map mode 3 (see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/keybi...).
- MapVirtualKey returns the wrong values for the numpad,
- so those are here also.
This probably depends on the Num Lock state, or it's a Wine bug. But perhaps this might also be fixed if we used map mode 3?
Well, better. I still think it might be better to make MapVirtualKey handle codes above 0x80 itself, but I'm not sure.
I don't think so. That is like extending the Windows API for our own use. Isn't that what Microsoft is hated for?
By the way, the problems we have with MapVirtualKey not having an one-to-one mapping might be solved by implementing map mode 3 (see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/keybi...).
I don't know what mode 3 returns, as I have only win9x, but the only difference in the documentation is "that distinguishes between left- and right-hand keys". I can't imagine that solves anything.
- MapVirtualKey returns the wrong values for the numpad,
- so those are here also.
This probably depends on the Num Lock state, or it's a Wine bug. But perhaps this might also be fixed if we used map mode 3?
Argh! It does depend on the Num Lock state. So, it can check for the Num Lock state, and use different VK_ values, or it should not use GetAsyncKeyState at all.
An other, minor problem is, that DIK_POWER & co. officialy have no VK at all, so they also can not be seen with GetAsyncKeyState.
Isn't there an easy way to see what physical buttons are pressed?
On Sun, 24 Feb 2002, Arjen Nienhuis wrote:
Well, better. I still think it might be better to make MapVirtualKey handle codes above 0x80 itself, but I'm not sure.
I don't think so. That is like extending the Windows API for our own use. Isn't that what Microsoft is hated for?
I see no reason that MapVirtualKey shouldn't handle extended scancodes, even under Windows. In fact, seeing how basic this functionality is, it would be incredibly stupid of Microsoft if they didn't, so I'm not going to believe that they don't support this without proof.
By the way, the problems we have with MapVirtualKey not having an one-to-one mapping might be solved by implementing map mode 3 (see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/keybi...).
I don't know what mode 3 returns, as I have only win9x, but the only difference in the documentation is "that distinguishes between left- and right-hand keys". I can't imagine that solves anything.
Well, currently we have that both left and right shift, and left and right ctrl, etc, maps to VK_SHIFT and VK_CONTROL, thus making them hard to distinguish... would be better to map to VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, etc, which is what mode 3 does.
This probably depends on the Num Lock state, or it's a Wine bug. But perhaps this might also be fixed if we used map mode 3?
Argh! It does depend on the Num Lock state. So, it can check for the Num Lock state, and use different VK_ values, or it should not use GetAsyncKeyState at all.
Something like that.
An other, minor problem is, that DIK_POWER & co. officialy have no VK at all, so they also can not be seen with GetAsyncKeyState.
If X reports that these keys are on the keyboard, and they do not exist in Wine's keyboard tables, then the Wine keyboard code would assign them to some VK_OEM_* or other, and some scancode between 0x60 and 0x80. But they should probably be entered into these tables to make it predictable...
Isn't there an easy way to see what physical buttons are pressed?
Not both easily and portably.